7.1 Kendi tip dönüştürücünüzü oluşturma

Bazen oldukça karmaşık bir veri türünü bir tablonun bir sütununda depolamak istediğinizde durumlar ortaya çıkar. Hazırda Bekletme, onu bir dizgeye (ve geri) nasıl dönüştüreceğini biliyorsa, o zaman her şey yolundadır. Değilse, kendi veri dönüştürücünüzü yazmanız gerekecektir.

Diyelim ki birisi bir kullanıcının doğum yılını veritabanında şu şekilde saklamaya karar verdi YY.MM.DD, örneğin: 98.12.15. Ayrıca onu normal bir tarihe dönüştürmeniz gerekir: 15/12/1998. O zaman kendi dönüştürücünüzü yazmalısınız.

Bunu yapmak için bir arayüz uygulamanız gerekir 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]);
    }
}

Ve elbette, bu dönüştürücü herhangi bir alana eklenebilir (türlerin eşleşmesi koşuluyla):


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

Veritabanını siz tasarlamadıysanız, dönüştürücülerin sıklıkla kullanılması gerekir. Buradaki veriler "garip biçimlerde" olabilir. Tarihler dizeler olarak, Boolean'lar Y ve N değerlerine sahip CHAR'lar ve benzerleri olarak saklanabilir.

7.2 Kendi veri türümüzü oluşturmak

Hazırda Bekletme ile bilinen türlerin listesini içeren tabloyu hatırlıyor musunuz? Ek açıklama ile birlikte belirtilen türlerden bahsediyorum @Type. Hazırda Bekletme'deki diğer yerleşik türlerle aynı şekilde kullanılabilen kendi veri türünüzü yazabilirsiniz.

Örneğin veritabanında TIME olarak değil VARCHAR olarak depolanacak LocalTime tipine sahip olmak istiyoruz. Ve örneğin, böyle bir veritabanına erişimimiz var ve sütunlarındaki veri türlerini değiştirmemize izin verilmiyor. Sonra kendi Hibernate tipimizi yazabiliriz. Buna LocalTimeString diyelim.

Öncelikle yeni türümüzü tanımlayacak küçük bir sınıfa ihtiyacımız var:

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

Tek bir değerden oluşan Enum türünde bir şeydir. Bu tür tek enam'lerin kümesi, Hibernate tarafından bilinen tüm türlerdir.

Ayrıca, iki yöntem içerecek dönüştürücünün bir analogu olan wrap()ve unwrap()LocalTime türündeki değerleri String'e dönüştürmek için bir sınıfa ihtiyacımız var.

Yöntemleri uygulamadan böyle görünecek:

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

    }

}

Şimdi metotların uygulanmasını yazalım:

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

Ve ikinci yöntem:

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

Hazır. Zamanı bir dizge olarak depolamak için bu sınıfı kullanabilirsiniz:


@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 Türünüzü kaydetme

Veri türünüzü Hazırda Bekletme yapılandırması sırasında da kaydedebilirsiniz. Bu biraz önemsiz.


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

İlk olarak MetadataSources'ı edinmeniz, ondan MetadataBuilder'ı almanız ve sınıfınızı eklemek için kullanmanız gerekecek. aracılığıyla mümkündür hibernate.cfg.xml, ancak aynı zamanda biraz hantaldır.

Ancak kayıt olduktan sonra şöyle yazabilirsiniz:


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