CodeGym /Cursos /All lectures for PT purposes /Usando diferentes conversores de tipo de dados

Usando diferentes conversores de tipo de dados

All lectures for PT purposes
Nível 1 , Lição 227
Disponível

7.1 Criando seu próprio conversor de tipo

Às vezes, surgem situações em que você deseja armazenar um tipo de dados bastante complexo em uma coluna de uma tabela. Se o Hibernate souber como convertê-lo em uma string (e vice-versa), está tudo bem. Caso contrário, você terá que escrever seu próprio conversor de dados.

Digamos que alguém decida armazenar o ano de nascimento de um usuário no banco de dados como YY.MM.DD, por exemplo: 98.12.15. Você também precisa convertê-lo em uma data regular: 15/12/1998. Então você tem que escrever seu próprio conversor.

Para fazer isso, você precisa implementar uma interface 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]);
    }
}

E, claro, este conversor pode ser adicionado a qualquer campo (desde que os tipos correspondam):


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

Frequentemente, os conversores precisam ser usados ​​se você não projetou o banco de dados. Os dados podem estar em "formatos estranhos". As datas podem ser armazenadas como strings, booleanos como CHARs com valores Y e N e assim por diante.

7.2 Criando nosso próprio tipo de dados

Lembra da tabela com a lista de tipos conhecidos pelo Hibernate? Estou falando de tipos que são especificados junto com a anotação @Type. Você pode escrever seu próprio tipo de dados, que pode ser usado da mesma forma que outros tipos embutidos no Hibernate.

Por exemplo, queremos ter o tipo LocalTime, que será armazenado no banco de dados não como TIME, mas como VARCHAR. E, por exemplo, temos acesso a esse banco de dados e não temos permissão para alterar os tipos de dados em suas colunas. Então podemos escrever nosso próprio tipo Hibernate. Vamos chamá-lo de LocalTimeString.

Primeiro, precisamos de uma pequena classe que descreva nosso novo tipo:

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

É algo do tipo Enum que consiste em um valor. O conjunto de tais enams únicos são todos os tipos conhecidos pelo Hibernate.

Também precisamos de uma classe - um análogo do conversor, que conterá dois métodos - wrap()e unwrap()para converter valores do tipo LocalTime em String.

É assim que ficará sem implementar os métodos:

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

    }

}

Agora vamos escrever a implementação dos métodos:

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

E o segundo método:

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

Preparar. Você pode usar esta classe para armazenar o tempo como uma string:


@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 Registrando seu tipo

Você também pode registrar seu tipo de dados durante a configuração do Hibernate. Isso é um pouco não trivial.


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

Você primeiro precisará obter MetadataSources, obter MetadataBuilder dele e usá-lo para adicionar sua classe. É possível através de hibernate.cfg.xml, mas também um pouco complicado.

Mas após o registro, você pode escrever assim:


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