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_falseLes mappings de type des primitives Java ou leurs classes wrappers (ex: Integer pour int) vers les types SQL (propriétaires)
appropriés. boolean, yes_noet true_false sont tous des alternatives pour les types Java boolean ou java.lang.Boolean.
stringMapping de type de java.lang.String vers VARCHAR (ou le VARCHAR2 Oracle).
date, time, timestampMappings de type pour java.util.Date et ses sous-classes vers les types SQL DATE, TIME et TIMESTAMP (ou équivalent).
calendar, calendar_dateMappings de type pour java.util.Calendar vers les types SQL TIMESTAMP et DATE (ou équivalent).
big_decimal, big_integerMappings de type pour java.math.BigDecimal et java.math.BigInteger vers NUMERIC (ou le NUMBER Oracle).
locale, timezone, currencyMappings 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.
classUn type de mapping pour java.lang.Class vers VARCHAR (ou le VARCHAR2 Oracle). Un objet Class est mappé sur son nom Java complet.
binaryMappe les tableaux de bytes vers le type binaire SQL approprié.
textMappe les longues chaînes de caractères Java vers les types SQL CLOB ou TEXT.
serializableMappe 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, blobMappings 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.