J'ai l'extrait de description de classe suivant:
...
@Column(name = "invalidate_token_date")
@Temporal(TemporalType.TIMESTAMP)
private LocalDateTime invalidateTokenDate;
....
Ce code ne fonctionne pas car @Temporal
ne supporte pas LocalDateTime.
J'ai vu la suggestion d'utiliser LocalDateTime à partir de Joda-Time mais j'utilise Java 8.
S'il vous plaît me conseiller quelque chose.
P.S.
Voici ma dépendance JPA actuelle:
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>persistence-api</artifactId>
<version>1.0</version>
</dependency>
Comme Hibernate ne le supporte pas, vous devez implémenter un type d'utilisateur comme indiqué dans this example.
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);
}
}
Le nouveau type d'utilisateur peut ensuite être utilisé dans le mappage avec l'annotation @Type. Par exemple.
@Type(type="com.hibernate.samples.type.LocalDateTimeUserType")
@Column(name = "invalidate_token_date")
private LocalDateTime invalidateTokenDate;
L'annotation @Type nécessite un chemin d'accès complet à la classe qui implémente l'interface userType; c'est l'usine qui produit le type de cible de la colonne mappée.
Voici comment faire la même chose dans JPA2.1
Pour tous les utilisateurs d'Hibernate 5.x, il existe
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-Java8</artifactId>
<version>5.0.0.Final</version>
</dependency>
Vous n'avez rien d'autre à faire. Ajoutez simplement la dépendance, et les types de temps Java 8 doivent fonctionner comme tout autre type de base, sans annotation.
private LocalDateTime invalidateTokenDate;
Remarque: ceci ne sauvera cependant pas le type timestamp
. En testant avec MySQL, il enregistre au type datetime
.
Si vous pouvez utiliser Java EE 7, il y a plus élégante solution :
>> Implémenter ceci:
@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);
}
}
>> Utilisez comme ceci:
...
@Column(name = "invalidate_token_date")
private LocalDateTime invalidateTokenDate;
....
La valeur (autoApply = true)
signifie que @Converter
est automatiquement utilisé pour la conversion de chaque propriété LocalDateTime
de votre entité JPA.
Btw, AttributeConverter
est assez bon pour cartographier Enums aussi.
J'ai créé un plugin simple pour nous permettre d'utiliser les classes Java.time. *. Actuellement, les classes les plus couramment utilisées sont implémentées. Jetez un oeil ici: https://github.com/garcia-jj/jpa-javatime .
Si vous utilisez Maven, voici la configuration de l'artefact:
<dependency>
<groupId>br.com.otavio</groupId>
<artifactId>jpa-javatime</artifactId>
<version>0.2</version>
</dependency>
Il y a plus d'informations sur comment utiliser à la page du projet.
Je vous remercie.