Intégration de JodaTime avec Hibernate et Jaxb

Joda Time est une API regroupant tous les outils de manipulation de dates en Java (calculs, parsing, formatage, gestions des timezones, …), et qui concurrence très largement les API java.util.Date, Calendar, GregorianCalendar…

Malgré qu’elle existe depuis 2005, son intégration n’est pas encore complète dans tous les outils, et il n’est pas toujours simple de trouver des informations lorsqu’on souhaite l’intégrer à une API bien précise.

Je vais ici traiter de la manière d’intégrer Joda Time avec le framework Hibernate pour gérer la persistance de ces dates en base de données, ainsi que dans l’API de manipulation XML, JAXB.

Côté Hibernate

Tout d’abord, côté Hibernate, la problématique consiste à stocker en base des dates au format Joda Time. Cela ne se fait pas automatiquement, il faut utiliser une annotation spéciale. En outre, l’intégration de Joda Time dépend de la version du framework Hibernate !

Si vous utilisez la version 3.6 d’Hibernate, il faut importer joda-time-hibernate :

<dependency>
    <groupId>joda-time</groupId>
    <artifactId>joda-time-hibernate</artifactId>
    <version>1.3</version>
</dependency>

Dans ce cas, il suffit d’utiliser l’annotation :

1
@Type(type="org.joda.time.contrib.hibernate.PersistentDateTime")

sur le DateTime concerné dans l’entité :

1
2
3
4
5
6
@Entity
public class Entite{
    @Column
    @Type(type = "org.joda.time.contrib.hibernate.PersistentDateTime")
    private DateTime date;
}

D’autres annotations existent pour mapper les autres classes de Joda Time. Vous trouverez la liste à cette adresse : http://joda-time.sourceforge.net/contrib/hibernate/userguide.html;

Il est également possible de mapper une date Joda Time contenant une information sur la timezone en utilisant deux colonnes en base de données :

1
2
3
@Columns(columns={@Column(name="startTime"), @Column(name="startTimezone")})
@Type(type="org.joda.time.contrib.hibernate.PersistentDateTimeTZ")
private DateTime startDateTime;

Avec Hibernate 4.0, on dispose d’un projet plus développé et plus complet pour mapper les dates de Joda Time : Usertype

<dependency>
    <groupId>org.jadira.usertype</groupId>
    <artifactId>usertype.core</artifactId>
    <version>3.0.0.CR2</version>
</dependency>

Le fonctionnement est le même avec l’annotation :

1
2
3
4
5
6
7
@Type(type = "org.jadira.usertype.dateandtime.joda.PersistentDateTime")
@Entity
public class Entite{
    @Column
    @Type(type = "org.jadira.usertype.dateandtime.joda.PersistentDateTime")
    private DateTime date;
}

Et voilà comment mapper en base les dates de l’API Joda Time.

Côté JAXB

Voyons maintenant, de l’autre côté d’une application, comment utiliser JodaTime avec JAXB pour convertir du XML en Java ou inversement.

JAXB ne permet pas d’utiliser le type JodaTime de base. En effet, l’API ne sait pas comment elle doit convertir cet objet en chaine de caractère. Il faut donc lui préciser quelle méthode de conversion on va utiliser.

Pour cela, on crée une classe DateTimeAdapter qui définit deux méthodes :

– La première, unmarshal, permet de convertir une String en DateTime.
– La seconde, marshal, permet à l’inverse de convertir une DateTime en String.

Voici un exemple :

1
2
3
public static DateTime unmarshal(String stringDate) {
    return new Date(stringDate);
}
1
2
3
public static String marshal(DateTime date) {
    return date.toString();
}

Ensuite, il faut créer un nouveau type dans le .xsd de définition des classes JAXB afin de déclarer l’utilisation de ces méthodes au moment de la conversion en XML (ou JSON).

<xsd:simpleType name="jodaDateTime">
    <xsd:annotation>
        <xsd:appinfo>
            <jaxb:javaType name="org.joda.time.DateTime"
               parseMethod="com.edf.stats.utils.DateTimeAdapter.unmarshal"
               printMethod="com.edf.stats.utils.DateTimeAdapter.marshal"/>
        </xsd:appinfo>
    </xsd:annotation>
    <xsd:restriction base="xsd:string">
        <xsd:minLength value="1"/>
    </xsd:restriction>
</xsd:simpleType>

Enfin, il ne reste plus qu’à utiliser ce convertisseur sur l’élément où on le souhaite :

<xsd:element name="objetAvecDate">
    <xsd:complexType>
        <xsd:sequence>
            <xsd:element name="date" type="jodaDateTime"/>
        </xsd:sequence>
    </xsd:complexType>
</xsd:element>

Après compilation avec Maven, on obtiendra une classe de la forme :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@XmlRootElement(name = "objetAvecDate")
public class ObjetAvecDate {
    @XmlElement(required = true, type = String.class)
    @XmlJavaTypeAdapter(Adapter1 .class)
    protected DateTime date;

    public DateTime getDate() {
        return date;
    }

    public void setDate(DateTime value) {
        this.date = value;
    }
}

Et le XML généré ressemblera à l’encadré ci-dessous :

<objetAvecDate>
    <date>2013-03-16T00:00:00.000Z</date>
</objetAvecDate>

Tout l’intérêt du convertisseur est alors de permettre le formatage du DateTime au moment de la transformation en XML. On peut aisément définir un DateTimeFormatter au moment de l’appel à la méthode marshal :

1
2
3
4
public static String marshal(DateTime date) {
    DateTimeFormatter formatter = DateTimeFormat.forPattern(dd-MM-yyyy);
    return date.toString(formatter);
}

Cela permet de choisir un peu mieux le format d’affichage de la date en XML :

<objetAvecDate>
    <date>16-03-2013</date>
</objetAvecDate>

 

Conclusion

L’intégration de Joda Time n’est donc pas encore complète dans tous les frameworks. Mais étant donné les grands avantages de cet API, il est utile de savoir contourner ces manques pour en profiter pleinement. Cet article appelle donc à être complété par toute autre forme d’intégration de Joda Time dans d’autres outils !

Sources
Joda-time-hibernate : http://joda-time.sourceforge.net/contrib/hibernate/index.html

VN:R_U [1.9.22_1171]
Rating: 0 (from 0 votes)
Share
Ce contenu a été publié dans Non classé. Vous pouvez le mettre en favoris avec ce permalien.

Laisser un commentaire