7.1 Saját típusú konverter készítése

Néha olyan helyzetek adódhatnak, amikor egy meglehetősen összetett adattípust akarunk tárolni egy táblázat egy oszlopában. Ha a Hibernate tudja, hogyan kell karakterláncsá alakítani (és vissza), akkor minden rendben van. Ha nem, akkor meg kell írnia saját adatkonvertálóját.

Tegyük fel, hogy valaki úgy dönt, hogy egy felhasználó születési évét tárolja az adatbázisban YY.MM.DD, például: 98.12.15. Át kell alakítania normál dátummá is: 15/12/1998. Ezután meg kell írni a saját konvertert.

Ehhez meg kell valósítania egy interfészt 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]);
    }
}

És természetesen ez a konverter bármelyik mezőhöz hozzáadható (feltéve, hogy a típusok egyeznek):


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

Nagyon gyakran kell konvertereket használni, ha nem Ön tervezte az adatbázist. Az ott található adatok „furcsa formátumban” lehetnek. A dátumok karakterláncként, a logikai értékek Y és N értékű CHAR-ként tárolhatók, és hasonlók.

7.2 Saját adattípus létrehozása

Emlékszel a táblázatra a Hibernate által ismert típusok listájával? Olyan típusokról beszélek, amelyek a megjegyzéssel együtt vannak megadva @Type. Írhat saját adattípust, amely ugyanúgy használható, mint a Hibernate többi beépített típusa.

Például a LocalTime típust szeretnénk, ami nem TIME, hanem VARCHAR néven lesz tárolva az adatbázisban. És például hozzáférünk egy ilyen adatbázishoz, és nem módosíthatjuk az adattípusokat az oszlopaiban. Ezután megírhatjuk a saját Hibernate típusunkat. Nevezzük LocalTimeString-nek.

Először is szükségünk van egy kis osztályra, amely leírja az új típusunkat:

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

Ez valami Enum típusú, amely egy értékből áll. Az ilyen egyedi lakkkészlet a Hibernate által ismert összes típus.

Szükségünk van egy osztályra is - a konverter analógjára, amely két módszert tartalmaz - wrap()és unwrap()a LocalTime típusú értékek String-re konvertálásához.

Így fog kinézni a módszerek alkalmazása nélkül:

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

    }

}

Most írjuk le a metódusok megvalósítását:

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

És a második módszer:

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

Kész. Ezt az osztályt használhatja az idő tárolására karakterláncként:


@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 A típus regisztrálása

Az adattípust a hibernált konfiguráció során is regisztrálhatja. Ez egy kicsit nem triviális.


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

Először be kell szereznie a MetadataSources-t, be kell szereznie belőle a MetadataBuilder-t, és ezzel kell hozzáadnia az osztályát. -on keresztül lehetséges hibernate.cfg.xml, de egy kicsit nehézkes is.

De regisztráció után ezt írhatod:


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