Pour comprendre le comportement des différents objets Java par rapport au service de persistance, nous avons besoin de les classer en deux groupes :
Une entité existe indépendamment de tout autre objet possédant une référence vers l'entité. Comparez cela avec le modèle Java habituel où un objet est supprimé par le garbage collector dès qu'il n'est plus référencé. Les entités doivent être explicitement enregistrées et supprimées (sauf dans les cas où sauvegardes et suppressions sont cascadées d'une entité mère vers ses enfants). C'est différent du modèle ODMG de persistance par atteignabilité - et correspond mieux à la façon dont les objets sont habituellement utilisés dans des grands systèmes. Les entités permettent les références circulaires et partagées. Elles peuvent aussi être versionnées.
L'état persistant d'une entité consiste en des références vers d'autres entités et instances de types valeurs. Ces valeurs sont des types primitifs, des collections (et non le contenu d'une collection), des composants de certains objets immuables. Contrairement aux entités, les valeurs (et en particulier les collections et composants) sont persistés par atteignabiliité. Comme les valeurs (et types primitifs) sont persistés et supprimés avec l'entité qui les contient, ils ne peuvent pas posséder leurs propres versions. Les valeurs n'ont pas d'identité indépendantes, ainsi elles ne peuvent pas être partagées par deux entités ou collections.
Jusqu'à présent nous avons utilisé le terme "classe persistante" pour parler d'entités. Nous allons continuer à faire ainsi.
Cependant, au sens strict, toutes les classes définies par un utilisateur possédant un état persistant ne sont pas des entités.
Un composant est une classe définie par un utilisateur avec les caractéristiques d'une valeur. Une propriété Java de type java.lang.String
a aussi les caractéristiques d'une valeur. Given this definition, we can say that all types (classes) provided by the JDK
have value type semantics in Java, while user-defined types may be mapped with entity or value type semantics. This decision
is up to the application developer. A good hint for an entity class in a domain model are shared references to a single instance
of that class, while composition or aggregation usually translates to a value type.
Nous nous pencherons sur ces deux concepts tout au long de la documentation.
Le défi est de mapper les type Javas (et la définition des développeurs des entités et valeurs types) sur les types du SQL
ou des bases de données. Le pont entre les deux systèmes est proposé par Hibernate : pour les entités nous utilisons <class>
, <subclass>
et ainsi de suite. Pour les types valeurs nous utilisons <property>
, <component>
, etc., habituellement avec un attribut type
. La valeur de cet attribut est le nom d'un type de mapping Hibernate. Hibernate propose de base de nombreux mappings (pour les types de valeurs standards du JDK). Vous pouvez écrire
vos propres types de mappings et implémenter aussi vos propres stratégies de conversion, nous le verrons plus tard.
Tous les types proposés de base par Hibernate à part les collections autorisent la valeur null.
The built-in basic mapping types may be roughly categorized into
integer, long, short, float, double, character, byte, boolean, yes_no, true_false
Les mappings de type des primitives Java ou leurs classes wrappers (ex: Integer pour int) vers les types SQL (propriétaires)
appropriés. boolean, yes_no
et true_false
sont tous des alternatives pour les types Java boolean
ou java.lang.Boolean
.
string
Mapping de type de java.lang.String
vers VARCHAR
(ou le VARCHAR2
Oracle).
date, time, timestamp
Mappings de type pour java.util.Date
et ses sous-classes vers les types SQL DATE
, TIME
et TIMESTAMP
(ou équivalent).
calendar, calendar_date
Mappings de type pour java.util.Calendar
vers les types SQL TIMESTAMP
et DATE
(ou équivalent).
big_decimal, big_integer
Mappings de type pour java.math.BigDecimal
et java.math.BigInteger
vers NUMERIC
(ou le NUMBER
Oracle).
locale, timezone, currency
Mappings de type pour java.util.Locale
, java.util.TimeZone
et java.util.Currency
vers VARCHAR
(ou le VARCHAR2
Oracle). Les instances de Locale
et Currency
sont mappées sur leurs codes ISO. Les instances de TimeZone
sont mappées sur leur ID
.
class
Un type de mapping pour java.lang.Class
vers VARCHAR
(ou le VARCHAR2
Oracle). Un objet Class
est mappé sur son nom Java complet.
binary
Mappe les tableaux de bytes vers le type binaire SQL approprié.
text
Mappe les longues chaînes de caractères Java vers les types SQL CLOB
ou TEXT
.
serializable
Mappe les types Java sérialisables vers le type SQL binaire approprié. Vous pouvez aussi indiquer le type Hibernate serializable
avec le nom d'une classe Java sérialisable ou une interface qui ne soit pas par défaut un type de base.
clob, blob
Mappings de type pour les classes JDBC java.sql.Clob
and java.sql.Blob
. Ces types peuvent ne pas convenir pour certaines applications car un objet blob ou clob peut ne pas être réutilisable en
dehors d'une transaction (de plus l'implémentation par les pilotes est moyennement bonne).
imm_date, imm_time, imm_timestamp, imm_calendar, imm_calendar_date, imm_serializable, imm_binary
Mappings de type pour ceux qui sont habituellement modifiable, pour lesquels Hibernate effectue certains optimisations convenant
seulement aux types Java immuables, et l'application les traite comme immuable. Par exemple, vous ne devriez pas appeler Date.setTime()
sur une instance mappée sur un imm_timestamp
. Pour changer la valeur de la propriété, et faire que cette modification soit persistée, l'application doit assigner un nouvel
(non identique) objet à la propriété.
Les identifiants uniques des entités et collections peuvent être de n'importe quel type de base excepté binary
, blob
et clob
(les identifiants composites sont aussi permis, voir plus bas).
Les types de base des valeurs ont des Type
constants correspondants définis dans org.hibernate.Hibernate
. Par exemple, Hibernate.STRING
représenté le type string
.
Il est assez facile pour les développeurs de créer leurs propres types de valeurs. Par exemple, vous pourriez vouloir persister
des propriétés du type java.lang.BigInteger
dans des colonnnes VARCHAR
. Hibernate ne procure pas par défaut un type pour cela. Mais les types que vous pouvez créer ne se limitent pas à mapper
des propriétés (ou élément collection) à une simple colonne d'une table. Donc, par exemple, vous pourriez avoir une propriété
Java getName()
/setName()
de type java.lang.String
persistée dans les colonnes FIRST_NAME
, INITIAL
, SURNAME
.
Pour implémenter votre propre type, vous pouvez soit implémenter org.hibernate.UserType
soit org.hibernate.CompositeUserType
et déclarer des propriétés utilisant des noms de classes complets du type. Regardez org.hibernate.test.DoubleStringType
pour voir ce qu'il est possible de faire.
<property name="twoStrings" type="org.hibernate.test.DoubleStringType"> <column name="first_string"/> <column name="second_string"/> </property>
Remarquez l'utilisation des tags <column>
pour mapper une propriété sur des colonnes multiples.
Les interfaces CompositeUserType
, EnhancedUserType
, UserCollectionType
, et UserVersionType
permettent des utilisations plus spécialisées.
Vous pouvez même donner des paramètres en indiquant UserType
dans le fichier de mapping ; Pour cela, votre UserType
doit implémenter l'interface org.hibernate.usertype.ParameterizedType
. Pour spécifier des paramètres dans votre type propre, vous pouvez utiliser l'élément <type>
dans vos fichiers de mapping.
<property name="priority"> <type name="com.mycompany.usertypes.DefaultValueIntegerType"> <param name="default">0</param> </type> </property>
Le UserType
permet maintenant de récupérer la valeur pour le paramètre nommé default
à partir de l'objet Properties
qui lui est passé.
Si vous utilisez fréquemment un UserType
, cela peut être utile de lui définir un nom plus court. Vous pouvez faire cela en utilisant l'élément <typedef>
. Les typedefs permettent d'assigner un nom à votre type propre et peuvent aussi contenir une liste de valeurs de paramètres
par défaut si ce type est paramétré.
<typedef class="com.mycompany.usertypes.DefaultValueIntegerType" name="default_zero"> <param name="default">0</param> </typedef>
<property name="priority" type="default_zero"/>
Il est aussi possible de redéfinir les paramètres par défaut du typedef au cas par cas en utilisant des paramètres type sur le mapping de la propriété.
Bien que le fait que Hibernate propose de base une riche variété de types, et qu'il supporte les composants signifie que vous
aurez très rarement besoin d'utiliser un nouveau type propre, il est néanmoins de bonne pratique d'utiliser des types propres pour les classes (non
entités) qui apparaissent fréquemment dans votre application. Par exemple une classe MonetaryAmount
est un bon candidat pour un CompositeUserType
même s'il pourrait facilement être mappé comme un composant. Une motivation pour cela est l'abstraction. Avec un type propre
vos documents de mapping sont à l'abri des changements futurs dans votre façon de représenter des valeurs monétaires.