hibernate - java8 - localdatetime jpa spring boot
Java 8 LocalDateTime e Hibernate 4 (4)
Tengo el siguiente fragmento de descripción de clase:
...
@Column(name = "invalidate_token_date")
@Temporal(TemporalType.TIMESTAMP)
private LocalDateTime invalidateTokenDate;
....
Este código no funciona porque
@Temporal
no admite
LocalDateTime.
Vi la sugerencia de cómo usar
LocalDateTime
de
Joda-Time
pero uso Java 8.
Por favor, dame un consejo.
PD
Aquí está mi dependencia actual de JPA:
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>persistence-api</artifactId>
<version>1.0</version>
</dependency>
Creé un complemento simple para permitirnos usar clases java.time. *. En este momento se implementan las clases más utilizadas. Echa un vistazo aquí: https://github.com/garcia-jj/jpa-javatime .
Si está utilizando Maven, esta es la configuración del artefacto:
<dependency>
<groupId>br.com.otavio</groupId>
<artifactId>jpa-javatime</artifactId>
<version>0.2</version>
</dependency>
Hay más información sobre cómo usar en la página del proyecto.
Gracias.
Dado que Hibernate no lo admite, debe implementar un tipo de usuario como se muestra en this ejemplo.
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.usertype.EnhancedUserType;
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
public class LocalDateTimeUserType implements EnhancedUserType, Serializable {
private static final int[] SQL_TYPES = new int[]{Types.TIMESTAMP};
@Override
public int[] sqlTypes() {
return SQL_TYPES;
}
@Override
public Class returnedClass() {
return LocalDateTime.class;
}
@Override
public boolean equals(Object x, Object y) throws HibernateException {
if (x == y) {
return true;
}
if (x == null || y == null) {
return false;
}
LocalDateTime dtx = (LocalDateTime) x;
LocalDateTime dty = (LocalDateTime) y;
return dtx.equals(dty);
}
@Override
public int hashCode(Object object) throws HibernateException {
return object.hashCode();
}
@Override
public Object nullSafeGet(ResultSet resultSet, String[] names, SessionImplementor session, Object owner)
throws HibernateException, SQLException {
Object timestamp = StandardBasicTypes.TIMESTAMP.nullSafeGet(resultSet, names, session, owner);
if (timestamp == null) {
return null;
}
Date ts = (Date) timestamp;
Instant instant = Instant.ofEpochMilli(ts.getTime());
return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
}
@Override
public void nullSafeSet(PreparedStatement preparedStatement, Object value, int index, SessionImplementor session)
throws HibernateException, SQLException {
if (value == null) {
StandardBasicTypes.TIMESTAMP.nullSafeSet(preparedStatement, null, index, session);
} else {
LocalDateTime ldt = ((LocalDateTime) value);
Instant instant = ldt.atZone(ZoneId.systemDefault()).toInstant();
Date timestamp = Date.from(instant);
StandardBasicTypes.TIMESTAMP.nullSafeSet(preparedStatement, timestamp, index, session);
}
}
@Override
public Object deepCopy(Object value) throws HibernateException {
return value;
}
@Override
public boolean isMutable() {
return false;
}
@Override
public Serializable disassemble(Object value) throws HibernateException {
return (Serializable) value;
}
@Override
public Object assemble(Serializable cached, Object value) throws HibernateException {
return cached;
}
@Override
public Object replace(Object original, Object target, Object owner) throws HibernateException {
return original;
}
@Override
public String objectToSQLString(Object object) {
throw new UnsupportedOperationException();
}
@Override
public String toXMLString(Object object) {
return object.toString();
}
@Override
public Object fromXMLString(String string) {
return LocalDateTime.parse(string);
}
}
El nuevo usertype se puede usar en la asignación con la anotación @Type. Por ej.
@Type(type="com.hibernate.samples.type.LocalDateTimeUserType")
@Column(name = "invalidate_token_date")
private LocalDateTime invalidateTokenDate;
La anotación @Type necesita una ruta completa a la clase que implementa la interfaz userType; Esta es la fábrica para producir el tipo de destino de la columna asignada.
Here''s explica cómo hacer lo mismo en JPA2.1
Para cualquier usuario de Hibernate 5.x, hay
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-java8</artifactId>
<version>5.0.0.Final</version>
</dependency>
No necesitas hacer nada más. Simplemente agregue la dependencia, y los tipos de tiempo de Java 8 deberían funcionar como cualquier otro tipo básico, no se requieren anotaciones.
private LocalDateTime invalidateTokenDate;
Nota: sin embargo, esto no se guardará en el tipo de
timestamp
de
timestamp
.
Al probar con MySQL, se guarda en el tipo de
datetime
y
datetime
.
Si puede usar Java EE 7, hay una solution más elegante:
>> Implemente esto:
@Converter(autoApply = true)
public class LocalDateTimeConverter implements AttributeConverter<LocalDateTime, Date> {
@Override
public Date convertToDatabaseColumn(LocalDateTime date) {
if (date == null){
return null;
}
return date.toDate();
}
@Override
public LocalDateTime convertToEntityAttribute(Date value) {
if (value == null) {
return null;
}
return LocalDateTime.fromDateFields(value);
}
}
>> Use así:
...
@Column(name = "invalidate_token_date")
private LocalDateTime invalidateTokenDate;
....
El valor
(autoApply = true)
significa que
@Converter
se usa automáticamente para la conversión de cada propiedad
LocalDateTime
en su entidad JPA.
Por cierto,
AttributeConverter
es bastante bueno para mapear Enums.