web-dev-qa-db-fra.com

Java 8 LocalDateTime et Hibernate 4

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>
17
gstackoverflow

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

21
Andy Dufresne

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.

43
Paul Samsotha

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.

9
Petr Hunka

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.

0
Otávio Garcia