7.1 Mencipta penukar jenis anda sendiri

Kadangkala situasi timbul apabila anda ingin menyimpan jenis data yang agak kompleks dalam satu lajur jadual. Jika Hibernate tahu cara menukarnya kepada rentetan (dan belakang), maka semuanya baik-baik saja. Jika tidak, maka anda perlu menulis penukar data anda sendiri.

Katakan seseorang memutuskan untuk menyimpan tahun kelahiran pengguna dalam pangkalan data sebagai YY.MM.DD, sebagai contoh: 98.12.15. Anda juga perlu menukarnya kepada tarikh biasa: 15/12/1998. Kemudian anda perlu menulis penukar anda sendiri.

Untuk melakukan ini, anda perlu melaksanakan antara muka 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]);
    }
}

Dan, sudah tentu, penukar ini boleh ditambah pada mana-mana medan (dengan syarat jenis sepadan):


@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;
}

Penukar sangat kerap perlu digunakan jika anda tidak mereka bentuk pangkalan data. Data di sana boleh dalam "format pelik". Tarikh boleh disimpan sebagai rentetan, Boolean sebagai CHAR dengan nilai Y dan N, dan seumpamanya.

7.2 Mencipta jenis data kami sendiri

Ingat jadual dengan senarai jenis yang diketahui Hibernate? Saya bercakap tentang jenis yang ditentukan bersama dengan anotasi @Type. Anda boleh menulis jenis data anda sendiri, yang boleh digunakan dengan cara yang sama seperti jenis terbina dalam lain dalam Hibernate.

Sebagai contoh, kami ingin mempunyai jenis LocalTime, yang akan disimpan dalam pangkalan data bukan sebagai MASA, tetapi sebagai VARCHAR. Dan, sebagai contoh, kami mempunyai akses kepada pangkalan data sedemikian, dan kami tidak dibenarkan menukar jenis data dalam lajurnya. Kemudian kita boleh menulis jenis Hibernate kita sendiri. Mari kita panggil ia LocalTimeString.

Mula-mula kami memerlukan kelas kecil yang akan menerangkan jenis baharu kami:

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";
    }
}

Ia adalah sesuatu jenis Enum yang terdiri daripada satu nilai. Set enam tunggal sedemikian adalah semua jenis yang diketahui Hibernate.

Kami juga memerlukan kelas - analog penukar, yang akan mengandungi dua kaedah - wrap()dan unwrap()untuk menukar nilai jenis LocalTime kepada String.

Ini adalah bagaimana ia akan kelihatan tanpa melaksanakan kaedah:

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) {

    }

}

Sekarang mari kita tulis pelaksanaan kaedah:

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);
}

Dan kaedah kedua:

@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());
}

sedia. Anda boleh menggunakan kelas ini untuk menyimpan masa sebagai rentetan:


@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 Mendaftarkan jenis anda

Anda juga boleh mendaftarkan jenis data anda semasa konfigurasi Hibernate. Ini agak tidak remeh.


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();

Mula-mula anda perlu mendapatkan MetadataSources, dapatkan MetadataBuilder daripadanya dan menggunakannya untuk menambah kelas anda. Ia boleh dilakukan melalui hibernate.cfg.xml, tetapi juga sedikit menyusahkan.

Tetapi selepas pendaftaran, anda boleh menulis seperti ini:


@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;
}