CodeGym /Kursy /All lectures for PL purposes /Używanie konwerterów różnych typów danych

Używanie konwerterów różnych typów danych

All lectures for PL purposes
Poziom 1, Lekcja 308
Dostępny

7.1 Tworzenie własnego konwertera typów

Czasami pojawiają się sytuacje, gdy chcesz przechowywać dość złożony typ danych w jednej kolumnie tabeli. Jeśli Hibernate wie, jak przekonwertować go na ciąg znaków (i z powrotem), wszystko jest w porządku. Jeśli nie, będziesz musiał napisać własny konwerter danych.

Załóżmy, że ktoś zdecydował się zapisać rok urodzenia użytkownika w bazie danych jako YY.MM.DD, na przykład: 98.12.15. Musisz także przekonwertować go na zwykłą datę: 15/12/1998. Następnie musisz napisać własny konwerter.

Aby to zrobić, musisz zaimplementować interfejs 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]);
    }
}

I oczywiście ten konwerter można dodać do dowolnego pola (pod warunkiem, że typy są zgodne):


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

Konwertery bardzo często muszą być używane, jeśli nie zaprojektowałeś bazy danych. Dane mogą być w „dziwnych formatach”. Daty mogą być przechowywane jako łańcuchy znaków, wartości logiczne jako CHAR z wartościami Y i N i tym podobne.

7.2 Tworzenie własnego typu danych

Pamiętasz tabelę z listą typów znanych Hibernacji? Mówię o typach, które są określone wraz z adnotacją @Type. Możesz napisać własny typ danych, który może być używany w taki sam sposób, jak inne wbudowane typy w Hibernate.

Na przykład chcemy mieć typ LocalTime, który będzie przechowywany w bazie danych nie jako TIME, ale jako VARCHAR. I np. mamy dostęp do takiej bazy danych i nie wolno nam zmieniać typów danych w jej kolumnach. Następnie możemy napisać własny typ Hibernate. Nazwijmy to LocalTimeString.

Najpierw potrzebujemy małej klasy, która opisze nasz nowy typ:

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

Jest to coś typu Enum, które składa się z jednej wartości. Zbiór takich pojedynczych enamów to wszystkie typy znane Hibernacji.

Potrzebujemy również klasy - analogu konwertera, który będzie zawierał dwie metody - wrap()oraz unwrap()do konwersji wartości typu LocalTime na String.

Tak to będzie wyglądać bez implementacji metod:

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

    }

}

Teraz napiszmy implementację metod:

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

I druga metoda:

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

Gotowy. Możesz użyć tej klasy do przechowywania czasu jako ciągu znaków:


@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 Rejestracja typu

Możesz także zarejestrować swój typ danych podczas konfiguracji Hibernate. To jest trochę niebanalne.


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

Najpierw musisz zdobyć MetadataSources, pobrać z niego MetadataBuilder i użyć go do dodania swojej klasy. Jest to możliwe przez hibernate.cfg.xml, ale też trochę uciążliwe.

Ale po rejestracji możesz napisać tak:


@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;
}
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION