CodeGym/Курсове/SQL & Hibernate/Използване на различни конвертори на типове данни

Използване на различни конвертори на типове данни

На разположение

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;
}
Коментари
  • Популярен
  • Нов
  • Стар
Трябва да сте влезли, за да оставите коментар
Тази страница все още няма коментари