7.1 Opprette din egen typekonverter

Noen ganger oppstår situasjoner når du ønsker å lagre en ganske kompleks datatype i én kolonne i en tabell. Hvis Hibernate vet hvordan den skal konverteres til en streng (og tilbake), så er alt i orden. Hvis ikke, må du skrive din egen datakonverter.

La oss si at noen bestemmer seg for å lagre en brukers fødselsår i databasen som YY.MM.DD, for eksempel: 98.12.15. Du må også konvertere den til en vanlig dato: 15/12/1998. Da må du skrive din egen omformer.

For å gjøre dette må du implementere et grensesnitt 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]);
    }
}

Og selvfølgelig kan denne omformeren legges til et hvilket som helst felt (forutsatt at typene samsvarer):


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

Konvertere må veldig ofte brukes hvis du ikke har designet databasen. Dataene der kan være i "merkelige formater". Datoer kan lagres som strenger, boolske som CHAR-er med Y- og N-verdier og lignende.

7.2 Opprette vår egen datatype

Husker du tabellen med listen over typer som er kjent for Hibernate? Jeg snakker om typer som er spesifisert sammen med merknaden @Type. Du kan skrive din egen datatype, som kan brukes på samme måte som andre innebygde typer i Hibernate.

For eksempel ønsker vi å ha LocalTime-typen, som vil bli lagret i databasen ikke som TIME, men som VARCHAR. Og vi har for eksempel tilgang til en slik database, og vi har ikke lov til å endre datatypene i dens kolonner. Da kan vi skrive vår egen Hibernate-type. La oss kalle det LocalTimeString.

Først trenger vi en liten klasse som skal beskrive vår nye type:

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

Det er noe av typen Enum som består av én verdi. Settet med slike enkle enamer er alle typene kjent for Hibernate.

Vi trenger også en klasse - en analog av omformeren, som vil inneholde to metoder - wrap()og unwrap()for å konvertere verdier av typen LocalTime til String.

Slik vil det se ut uten å implementere metodene:

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

    }

}

La oss nå skrive implementeringen av metodene:

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

Og den andre metoden:

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

Klar. Du kan bruke denne klassen til å lagre tid som en streng:


@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 Registrere din type

Du kan også registrere datatypen din under dvalekonfigurasjon. Dette er litt ikke-trivielt.


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

Du må først hente MetadataSources, hente MetadataBuilder fra den og bruke den til å legge til klassen din. Det er mulig gjennom hibernate.cfg.xml, men også litt tungvint.

Men etter registrering kan du skrive slik:


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