7.1 Paglikha ng sarili mong type converter

Minsan lumitaw ang mga sitwasyon kapag gusto mong mag-imbak ng medyo kumplikadong uri ng data sa isang column ng isang talahanayan. Kung alam ng Hibernate kung paano i-convert ito sa isang string (at pabalik), kung gayon ang lahat ay maayos. Kung hindi, pagkatapos ay kailangan mong isulat ang iyong sariling data converter.

Sabihin nating may nagpasya na mag-imbak ng taon ng kapanganakan ng user sa database bilang YY.MM.DD, halimbawa: 98.12.15. Kailangan mo ring i-convert ito sa isang regular na petsa: 15/12/1998. Pagkatapos ay kailangan mong isulat ang iyong sariling converter.

Upang gawin ito, kailangan mong magpatupad ng isang interface AttributeConverter<EntityType, DbType>.


@Converter(autoApply = true)
public class DateConverter implements AttributeConverter<java.time.LocalDate, String> {
 
    public String convertToDatabaseColumn(java.time.LocalDate date) {
    	return date.format("YY.MM.DD");
    }
 
    public java.time.LocalDate convertToEntityAttribute(String dbData) {
    	String[] data = dbData.split(".");
    	return LocalDate.of(data[2], data[1], "19"+data[0]);
    }
}

At, siyempre, ang converter na ito ay maaaring idagdag sa anumang field (kung magkatugma ang mga uri):


@Entity
@Table(name="user")
class User {
   @Id
   @Column(name="id")
   public Integer id;
 
   @Column(name="join_date")
   @Convert(converter = DateConverter.class)
   public java.time.LocalDate date;
}

Ang mga converter ay madalas na kailangang gamitin kung hindi mo idinisenyo ang database. Ang data doon ay maaaring nasa "mga kakaibang format". Maaaring iimbak ang mga petsa bilang mga string, ang mga Boolean bilang mga CHAR na may mga halagang Y at N, at mga katulad nito.

7.2 Paglikha ng aming sariling uri ng data

Tandaan ang talahanayan na may listahan ng mga uri na kilala sa Hibernate? Pinag-uusapan ko ang mga uri na tinukoy kasama ng anotasyon @Type. Maaari mong isulat ang iyong sariling uri ng data, na maaaring magamit sa parehong paraan tulad ng iba pang mga built-in na uri sa Hibernate.

Halimbawa, gusto naming magkaroon ng uri ng LocalTime, na maiimbak sa database hindi bilang TIME, ngunit bilang VARCHAR. At, halimbawa, mayroon kaming access sa naturang database, at hindi kami pinapayagang baguhin ang mga uri ng data sa mga column nito. Pagkatapos ay maaari nating isulat ang sarili nating uri ng Hibernate. Tawagin natin itong LocalTimeString.

Una kailangan namin ng isang maliit na klase na maglalarawan sa aming bagong uri:

public class LocalTimeStringType extends AbstractSingleColumnStandardBasicType<<LocalTime> {

    public static final LocalTimeStringType  INSTANCE = new LocalTimeStringType ();

    public LocalTimeStringType () {
    	super(VarcharTypeDescriptor.INSTANCE, LocalTimeStringJavaDescriptor.INSTANCE);
    }

    @Override
    public String getName() {
    	return "LocalTimeString";
    }
}

Ito ay isang uri ng Enum na binubuo ng isang halaga. Ang hanay ng naturang mga solong enam ay ang lahat ng mga uri na kilala sa Hibernate.

Kailangan din namin ng isang klase - isang analogue ng converter, na maglalaman ng dalawang pamamaraan - wrap()at unwrap()para sa pag-convert ng mga halaga ng uri ng LocalTime sa String.

Ganito ang magiging hitsura nito nang hindi ipinapatupad ang mga pamamaraan:

public class LocalTimeStringJavaDescriptor extends AbstractTypeDescriptor<LocalTime> {

    public static final LocalTimeStringJavaDescriptor INSTANCE =  new  LocalTimeStringJavaDescriptor();

    public LocalTimeStringJavaDescriptor() {
    	super(LocalTime.class, ImmutableMutabilityPlan.INSTANCE);
    }

    public <X> X unwrap(LocalTime value, Class<X> type, WrapperOptions options) {

    }

    public <X> LocalTime wrap(X value, WrapperOptions options) {

    }

}

Ngayon isulat natin ang pagpapatupad ng mga pamamaraan:

public <X> X unwrap(LocalTime value, Class<X> type, WrapperOptions options) {

    if (value == null)
    	return null;

    if (String.class.isAssignableFrom(type))
    	return (X) LocalTimeType.FORMATTER.format(value);

    throw unknownUnwrap(type);
}

At ang pangalawang paraan:

@Override
public <X> LocalTime wrap(X value, WrapperOptions options) {
    if (value == null)
    	return null;

    if(String.class.isInstance(value))
    	return LocalTime.from(LocalTimeType.FORMATTER.parse((CharSequence) value));

    throw unknownWrap(value.getClass());
}

handa na. Maaari mong gamitin ang klase na ito upang mag-imbak ng oras bilang isang string:


@Entity
@Table(name="user")
class User
{
   @Id
   @Column(name="id")
   public Integer id;
 
   @Column(name="join_time")
   @Type(type = "com.codegym.hibernate.customtypes.LocalTimeStringType")  
   public java.time.LocalTime time;
}

7.3 Pagrerehistro ng iyong uri

Maaari mo ring irehistro ang iyong uri ng data sa panahon ng pagsasaayos ng Hibernate. Ito ay medyo non-trivial.


ServiceRegistry serviceRegistry = StandardServiceRegistryBuilder()
    .applySettings(getProperties()).build();
                                                                                                                                                              	                                        	 
    MetadataSources metadataSources = new MetadataSources(serviceRegistry);
    Metadata metadata = metadataSources
  	.addAnnotatedClass(User.class)
  	.getMetadataBuilder()
  	.applyBasicType(LocalTimeStringType.INSTANCE)
  	.build();
                                                                                                                                                              	                                        	 
    SessionFactory factory =  metadata.buildSessionFactory();

Kakailanganin mo munang kumuha ng MetadataSources, kumuha ng MetadataBuilder mula dito, at gamitin ito upang idagdag ang iyong klase. Posible ito sa pamamagitan ng hibernate.cfg.xml, ngunit medyo mahirap din.

Ngunit pagkatapos ng pagpaparehistro, maaari kang sumulat ng ganito:


@Entity
@Table(name="user")
class User
{
   @Id
   @Column(name="id")
   public Integer id;
 
   @Column(name="join_time")
   @Type(type = "LocalTimeString")  
   public java.time.LocalTime time;
}