7.1 Създаване на ваш собствен конвертор на типове
Понякога възникват ситуации, когато искате да съхраните доста сложен тип данни в една колона на table. Ако Hibernate знае How да го преобразува в низ (и обратно), тогава всичко е наред. Ако не, тогава ще трябва да напишете свой собствен конвертор на данни.
Да приемем, че някой реши да съхрани годината на раждане на потребителя в базата данни като YY.MM.DD
например: 98.12.15
. Трябва също да го преобразувате в обикновена дата: 15/12/1998
. След това трябва да напишете свой собствен конвертор.
За да направите това, трябва да внедрите интерфейс 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]);
}
}
И, разбира се, този конвертор може да се добави към всяко поле (при condition че типовете съвпадат):
@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;
}
Много често трябва да се използват конвертори, ако не сте проектирали базата данни. Данните там може да са в "странни формати". Датите могат да се съхраняват като низове, булевите като CHAR със стойности Y и N и други подобни.
7.2 Създаване на наш собствен тип данни
Помните ли tableта със списъка с типове, които са известни на Hibernate? Говоря за типове, които са посочени заедно с анотацията @Type
. Можете да напишете свой собствен тип данни, който може да се използва по същия начин като другите вградени типове в Hibernate.
Например искаме да имаме типа LocalTime, който ще се съхранява в базата данни не като TIME, а като VARCHAR. И например имаме достъп до такава база данни и не ни е позволено да променяме типовете данни в нейните колони. След това можем да напишем наш собствен тип Hibernate. Нека го наречем LocalTimeString.
Първо имаме нужда от малък клас, който ще опише нашия нов тип:
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";
}
}
Това е нещо от тип Enum, което се състои от една стойност. Наборът от такива единични енами е всички видове, известни на Hibernate.
Също така се нуждаем от клас - аналог на конвертора, който ще съдържа два метода - wrap()
и unwrap()
за преобразуване на стойности от типа LocalTime в String.
Ето How ще изглежда без прилагане на методите:
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) {
}
}
Сега нека напишем изпълнението на методите:
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);
}
И вторият метод:
@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());
}
Готов. Можете да използвате този клас, за да съхранявате времето като низ:
@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 Регистриране на вашия тип
Можете също да регистрирате своя тип данни по време на конфигурацията на Hibernate. Това е малко нетривиално.
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();
Първо ще трябва да получите MetadataSources, да получите MetadataBuilder от него и да го използвате, за да добавите своя клас. Възможно е чрез hibernate.cfg.xml
, но и малко тромаво.
Но след регистрация можете да пишете така:
@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;
}
GO TO FULL VERSION