7.1 Membuat konverter tipe Anda sendiri

Terkadang muncul situasi saat Anda ingin menyimpan tipe data yang cukup rumit dalam satu kolom tabel. Jika Hibernate tahu cara mengubahnya menjadi string (dan kembali), maka semuanya baik-baik saja. Jika tidak, maka Anda harus menulis pengonversi data Anda sendiri.

Katakanlah seseorang memutuskan untuk menyimpan tahun kelahiran pengguna di database sebagai YY.MM.DD, misalnya: 98.12.15. Anda juga perlu mengubahnya menjadi tanggal biasa: 15/12/1998. Maka Anda harus menulis konverter Anda sendiri.

Untuk melakukan ini, Anda perlu mengimplementasikan 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]);
    }
}

Dan, tentu saja, konverter ini dapat ditambahkan ke bidang apa pun (asalkan jenisnya cocok):


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

Konverter sangat sering harus digunakan jika Anda tidak mendesain database. Data di sana bisa dalam "format aneh". Tanggal dapat disimpan sebagai string, Boolean sebagai CHAR dengan nilai Y dan N, dan sejenisnya.

7.2 Membuat tipe data kita sendiri

Ingat tabel dengan daftar tipe yang diketahui Hibernate? Saya berbicara tentang jenis yang ditentukan bersama dengan anotasi @Type. Anda dapat menulis tipe data Anda sendiri, yang dapat digunakan dengan cara yang sama seperti tipe bawaan lainnya di Hibernate.

Misalnya, kami ingin memiliki tipe LocalTime, yang akan disimpan di database bukan sebagai TIME, tetapi sebagai VARCHAR. Dan, misalnya, kami memiliki akses ke database semacam itu, dan kami tidak diizinkan mengubah tipe data di kolomnya. Kemudian kita dapat menulis tipe Hibernasi kita sendiri. Sebut saja LocalTimeString.

Pertama kita membutuhkan kelas kecil yang akan mendeskripsikan tipe baru kita:

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

Itu adalah sesuatu dari tipe Enum yang terdiri dari satu nilai. Himpunan enams tunggal tersebut adalah semua jenis yang diketahui Hibernate.

Kami juga membutuhkan kelas - analog dari konverter, yang akan berisi dua metode - wrap()dan unwrap()untuk mengonversi nilai dari tipe LocalTime ke String.

Begini tampilannya tanpa menerapkan metode:

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 penerapan metode:

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

Siap. Anda dapat menggunakan kelas ini untuk menyimpan waktu sebagai 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 Mendaftarkan tipe Anda

Anda juga dapat mendaftarkan tipe data Anda selama konfigurasi Hibernasi. Ini agak non-sepele.


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

Pertama-tama Anda harus mendapatkan MetadataSources, dapatkan MetadataBuilder darinya, dan gunakan untuk menambahkan kelas Anda. Itu mungkin melalui hibernate.cfg.xml, tetapi juga sedikit rumit.

Tetapi setelah pendaftaran, Anda dapat 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;
}