Type de données JPA et Hibernate
DiggIt!
Enregistrer sur Del.icio.us
L'efficacité du développement d'une application de gestion et l'efficacité de cette même application (les temps de réponse) repose grandement sur la construction d'un modèle de données "efficace".
Dans ce billet nous allons nous concentrer sur un choix "efficace" des types simples pour une application Java reposant sur JPA et Hibernate.
Cette article complète une série d'autres billets sur JPA de ce même Blog :
- Faut-il utiliser les EJB 3.0 et la Java Persistence API : Il s'agit d'une réflexion sur l'opportunité d'utiliser les EJB 3.0 et JPA
- Tutorial sur le mapping par annotation JPA : un tutorial d'introduction sur les annotations JPA
- Réalisation d'une application Web Ajax et JPA : un tutorial qui met en oeuvre JPA dans une application Web Ajax
Si le mot "efficace" a été mis à plusieurs reprises entre guillemets, c'est que ce mot peut prendre des aspects contradictoires suivant le point de vu :
- l'efficacité d'un modèle de données pour le DBA (Database Administrateur) va reposer sur l'espace de stockage et la rapidité d'exécution des requêtes,
- pour un développeur cela reposera en grande partie sur la facilité qu'il a à écrire les requêtes qui correspondent au besoin fonctionnel
- pour l'utilisateur ce sera les temps de réponses de l'application
Mon soucis dans ce billet, est de proposer un certain équilibre, une sorte de compromis entre ces différentes aspirations sans perdre de vu, le besoin pragmatique auquel nous devons tous faire face, développer une application au meilleur prix pour un client exigeant.
Dans un premier temps, nous énoncerons un certain nombre de règles simples permettant de choisir rapidement les types de données de ses propriétés. Dans un second temps nous expliquerons avec différents exemples les raisons de ces choix. Cette seconde partie devrait permettre une meilleure compréhension du comportement d'Hibernate et des limites de la portabilité des différentes solutions.
Contraintes pour l'efficacité d'un modèle de données
Nous nous concentrons dans ce billet aux seuls aspects du typage des propriétés et colonnes simples.
Pour pouvoir choisir efficacement un type de données il faut :
- comprendre les nuances entre les différents types
- être capable pour une donnée du monde réel (la valeur que l'on veut stocker) de distinguer les différentes nuances associées aux différents types
Par exemple, un double et un BigDecimal représentent tout les deux un nombre décimal. Alors que le double ne connaît pas de limite à sa précision : la même variable peut contenir un nombre précis à 2 ou 10 chiffres après la virgule, ce n'est pas le cas d'un BigDecimal, cet objet possède un attribut précision qui indique le nombre de chiffres après la virgule. Pour changer sa précision il faut faire appel à une méthode.
Pour un float, 1,23 et 1,230 sont identiques, ce n'est pas le cas pour un BigDecimal car l'attribut précision n'est pas le même.
De même, si l'on sait aujourd'hui, qu'une valeur en euro sera inférieure disons à 1 000 000 €, on ne peut que difficilement savoir la limite qu'il faudra prendre en compte dans une dizaine d'années surtout s'il faut prendre en compte un changement de monnaie.
En conséquence, vouloir pour chaque montant définir des limites différentes en fonction de l'information n'est pas nécessairement pertinent : cela augmente la difficulté du choix et on n'a pas toujours la possibilité de prévoir les évolutions (on augmente le risque d'erreur en voulant être précis).
Pour que ces choix restent pertinents et efficaces sur la durée de vie de l'application (souvent plusieurs années), il faut que les différents développeurs manipulant ces données sachent les manipuler efficacement. On a donc bien souvent intérêt à :
- se limiter à un ensemble de types restreint : une trop grande précision peu provoquer dans certain cas des effets de bords néfastes par exemple dans la taille des Strings : à la fin, on ne sait jamais quelle est la taille d'un champ et du coup la probabilité d'un développeur pressé de se tromper dans le contrôle est plus grande.
- utiliser des types bien connus et maîtrisés par l'ensemble de l'équipe : si un type est mal maîtrisé, son utilisation peut être catastrophique car un des premiers réflexes est bien souvent de revenir à un type connu, ce qui a le double effet de rajouter une surcharge de travail ainsi que des risques d'apparition d'effets de bord lors des conversions. Les effets de bords sont bien évidemment ceux là même qu'on souhaitait éviter avec le choix de ce type.
Mon expérience acquise à travers l'audit de projets montre que bien souvent le mieux est l'ennemi du bien. Une optimisation trop poussée qui force à sortir des sentiers balisés a souvent des effets bien plus pervers pour un projet qu'une optimisation plus raisonnable.
Pour finir, le typage d'une "information" n'est pas intrinsèque à son contenu. Son utilisation a également son importance. Prenant pas exemple un numéro composé exclusivement de chiffres, cela ne veut pas dire qu'il devra être stocké sous forme d'un entier en base. Dans de nombreux cas, il est préférable de le stocker sous forme d'une chaîne de caractères. Cela va dépendre des contrôles que l'on souhaite faire, des recherches que l'on souhaite réaliser...
D'autre part, si une information doit être stockée en base, elle doit également être manipulée en Java et affichée dans un écran. Pour un développement efficace, il faut que le passage d'une représentation à l'autre et les contrôles soient rapides et efficaces. Hibernate et JPA facilitent ce travail, encore ne faut-il pas lui mettre trop de bâtons dans les roues en le forçant à utiliser des types incompatibles.
Les règles retenues sont :
- limitation du nombre de types
- des types connus
- une certaine portabilité à travers les bases de données
- un typage cohérent de la base à l'affichage en passant par la représentation mémoire en Java
Typage des données
Nous avons cinq grands types de données que nous subdivisons en des types plus proches des utilisations :- Les types numériques
- Les montants monétaires : il s'agit des valeurs dans une monnaies comme les prix, soldes, montant, commissions...
- Les mesures : il s'agit d'une valeur décimale qui correspond à la mesure d'un objet qu'il s'agissent d'une distance, d'un poids...
- Les taux et pourcentages : il s'agit de valeurs décimales faibles (inférieur à 100) et nécessitant parfois une précision un peu plus grande (certains taux possèdent 4 chiffres après la virgule)
- Les quantités : il s'agit de valeurs entières permettant de donner un nombre d'unités d'un produit.
- Les valeurs alphanumériques
- Les chaînes de caractères : les chaînes de caractères classiques qui stockent une information en claire (non codée)
- Les codes : il s'agit d'une information courte (moins d'une dizaine de caractères) qui est codé.
- Les commentaires : il s'agit d'un texte composé de plusieurs mots.
- Les valeurs énumérées
- Les valeurs Vrai-Fausse : cela correspond aux cases à cocher
- Les listes déroulantes et énumérations : il s'agit d'information dont la sélection se fait dans une liste déroulante. on stocke en base un code et on affiche un libellé grâce à une table de paramètre qui assure la correspondance.
- Les dates et timestamp
- Les dates traditionnelles précises au jour près
- les heures : une heure dans la journée indépendante de la date
- les tags horaires (TimeStamp) qui permettent de conserver la trace de l'heure précise d'un événement. Ceux sont des dates précises à la milliseconde
- Les identifiants
- les numéro de dossier
- les séquences
Type Montant Monétaire
Les montants monétaires ont une particularité : le nombre de chiffres après la virgule est une constante (souvent 2). Les problèmes d'arrondis doivent toujours être résolu avec cette contrainte : il n'est pas possible d'augmenter temporairement le nombre de chiffres après la virgule.
D'autre part, le BigDecimal est d'une précision absolue dans les comparaisons de nombre décimaux ce qui est impératif pour les montants monétaires.
Pour plus de détail sur l'intérêt de l'utilisation des BigDecimal vous pouvez lire mon billet : Tutorial sur les BigDecimal
Le stockage par hibernate d'un BigDecimal est un Number de 19 chiffres avec 2 chiffres après la virgule. Nous utiliserons ce format pour tous les montants.
Le nombre de chiffres (19) peut paraître excessif si on compte en Euro, mais en Yen cela se réduit et encore plus pour d'autres monnaies.
Il me parait donc plus sage de rester sur la taille par défaut.
Exemple de définition d'un montant :
@Column(name="JLTS_AMOUNT", precision=19, scale=2)
private BigDecimal amountDefault;
Le BigDecimal étant un objet, il peut être Null comme les colonnes correspondantes en base. Il n'aura donc aucun problème à s'adapter au cas où la valeur peut être Null.
| Base de données | Type Base | Remarque |
| Oracle | NUMBER(19,2) | Le nombre est tronqué si la précision est plus grande |
| MySql | decimal(19,2) | Une exception est envoyée si la précision est plus grande |
| HSqlDb | numeric | Le stockage ne connaît ni précision ni contrôle on stocke exactement ce que l'on a reçu |
| Sql Server | numeric(19,2) | Le nombre est tronqué si la précision est plus grande, le nombre est complété pour respecter la précision |
Type Mesure
Une mesure ressemble à un Montant monétaire, à une différence près : la précision des chiffres après la virgule n'est pas d'une grande importance (il ne faut pas oublier que la précision d'une mesure physique est rarement supérieure à 1%).
Il peut donc être judicieux de stocker une mesure dans un double ou Double, ce qui facilite la programmation.
Exemple de définition d'un montant :
@Column(name="JLTS_MESURE", nullable=false)
private double mesure;
| Base de données | Type Base | Remarque |
| Oracle | double precision | |
| MySql | double precision | |
| HsqlDb | double | |
| Sql Server | double precision |
Type Taux et Pourcentage
Les taux et pourcentage sont généralement associés aux données de type monétaire. Il s'agit souvent de réaliser des multiplications d'un montant monétaire afin d'obtenir une nouvelle somme. Généralement, un taux pourcentage peut être stockées avec 2 ou 4 chiffres après la virgule et ne dépasse pas les 3 chiffres avant la virgule. Afin de simplifier la modélisation, on utilisera donc la définition suivante :
@Column(name="JLTS_TAUX", precision=7, scale=4, nullable=false)
private BigDecimal taux;
Notez l'attribut nulllable positionné à false par défaut : il est fort probable que par défaut, un taux soit positionné à 0 et non pas laissé à Null. La valeur Null rend délicat les opérations réalisée par défaut alors qu'avec la valeur 0, le résultat serait fonctionnellement correct dans de nombreux cas.
| Base de données | Type Base | Remarque |
| Oracle | NUMBER(7,4) | Le nombre est tronqué si la précision est plus grande |
| MySql | decimal(7,4) | Une exception est envoyée si la précision est plus grande |
| HsqlDb | numeric | |
| Sql Server | numeric(7,4) | Le nombre est tronqué si la précision est plus grande, le nombre est complété pour respecter la précision |
Type Quantité entière
Il s'agit de valeurs entières comme le nombre d'articles achetés. On optera pour un type Long si la valeur peut être Null autrement on optera pour un long. Attention ! dans ce cas, il faut préciser l'attribut nullable à false. Il est fort probable que dans de nombreux cas, le défaut d'une quantité soit la valeur 0 : Dans ce cas, le long sera plus approprié.
@Column(name="JLTS_QUANTITY", nullable=false)
private long longNumeric;
ou bien
@Column(name="JLTS_QUANTITYNULL")
private Long longObject;
| Base de données | Type Base | Remarque |
| Oracle | NUMBER(19,0) | Le nombre est tronqué si la précision est plus grande |
| MySql | bigint | |
| HsqlDb | bigint | |
| Sql Server | numeric(19,0) | Le nombre est tronqué si la précision est plus grande |
Type Chaîne de caractères
Il s'agit de toutes les chaînes de caractères classiques qui correspondent à une information relativement structurée comme un nom, un prénom, un nom de rue... Ces chaînes sont d'une taille raisonnable : inférieure à 250 caractères. Au delà, ce sera probablement une donnée de type Commentaire.
Le stockage d'une chaîne de caractères est des plus simples : une String.
@Column(name="JLTS_NAME", length=40)
private String name;
| Base de données | Type Base | Remarque |
| Oracle | varchar2(40 char) | Où 40 est la length. Oracle ne distingue pas les chaînes vide des chaînes Null. Il ne stocke que des chaînes Null. L'attribut char après la longueur indique la prise en compte des caractères UTF-8. |
| MySql | varchar(40) | MySql fait la différence entre une chaîne vide et une chaîne Null. |
| HsqlDb | varchar(40) | Il n'envoie aucune exception en cas de chaine trop grande |
| Sql Server | varchar(40) |
Type Code
Certaines informations sont stockées sous la forme d'un code :
- code postaux
- code monnaie
- code d'un état
- code d'une banque
- Siren
- Siret
En règle général les codes ont une longueur fixe et relativement courte (inférieur à 10 caractères). Ils peuvent être alphanumériques ou numériques.
Ils peuvent également correspondre au type "Liste déroulante" décris plus loin. Les distinguer n'a pas d'intérêt car le résultat sera le même. On utilise un type String.
@Column(name="JLTS_SHORTCODE", nullable=true, length=2)
private String ShortCode;
| Base de données | Type Base | Remarque |
| Oracle | varchar2(2 char) | |
| MySql | varchar(2) | |
| HsqlDb | varchar(2) | |
| Sql Server | varchar(2) |
Type Commentaire
Il y a de forte chance que toute information qui dépasse la centaine de caractères soit un champ de type commentaire. Même s'il est confortable de limiter ce champ à un type String et un stockage en base limité à un ou deux milles caractères, cette approche à de forte chance de ne pas résister aux besoins futures des utilisateurs. Il est donc préférable d'utiliser une représentation sous forme de CLOB.
@Column(name="JLTS_COMMENT")
private String commentaire;
| Base de données | Type Base | Remarque |
| Oracle | CLOB | |
| MySql | longtext | |
| HsqlDb | longvarchar | |
| Sql Server | text |
Type Booléen Vrai-Faux
Il s'agit des informations qui sont affichées généralement sous forme de case à cocher (CheckBox). La valeur peut être Vrai ou Fausse.
Toutes les bases mais surtout la plus connue et la plus employée (Oracle) ne possèdent pas de type Booléen. L'information devra être stockée sous forme d'un caractère (Y,N par exemple) ou d'un nombre (0,1).
L'annotation standard JPA est celle là.
private boolean booleanType;
| Base de données | Type Base | Remarque |
| Oracle | number(1,0) | 0 pour false et 1 pour true |
| MySql | bit | correspondance directe avec true et false |
| HsqlDb | bit | |
| Sql Server | tinyint | 0 pour false et 1 pour true |
Il existe une annotation propre à Hibernate (non JPA) qui permet d'indiquer le stockage en base :
@Column(name="JLTS_YESNO")
public boolean yesNo;
@Type(type="true_false")
@Column(name="JLTS_TF")
public boolean trueFalse;
| Base de données | Type Base | Remarque |
| Oracle | char(1 char) | Stockage sous forme de lettre Y et N pour yes_no |
| MySql | CHAR(1) | T et F pour true_false, |
| HsqlDb | ||
| Sql Server | char(1) |
Type Liste déroulante et énumération
Il s'agit du cas représenté généralement par une liste déroulante (ComboBox ou composant SELECT HTML). Il y a une série de libellés qui correspondent à un code.
Le stockage en base est un code.
Généralement le code appartient à une autre colonne (notion de Foreign Key).
Le stockage aura lieu sous forme de chaîne de caractère classique :
@Column(name="JLTS_COMBOBOX", nullable=true, length=2)
private String selection;
Je ne recommande pas l'utilisation de many-to-one pour ce type de relation : ce type de relation alourdit l'affichage quand il s'agit d'une information qui correspond à une table de paramètre. Le many-to-one doit être réservé aux liaisons entre véritables objets.
| Base de données | Type Base | Remarque |
| Oracle | varchar2(2 char) | |
| MySql | varchar(2) | |
| HsqlDb | varchar(2) | |
| Sql Server | varchar(2) |
Type Date précise au jour
Le cas des dates est très particulier, le type Date est d'une précision bien supérieure au besoin de stockage des dates qui se limite à une précision au jour près.
JPA définit une annotation Temporal qui permet de limiter la précision au jour (DATE), à l'heure (TIME) où une précision extrême et complète (TIMESTAMP)
La précision est importante au niveau des dates : il est souhaitable qu'une recherche sur les enregistrements ayant une colonne Date qui correspond à la date du jour 22/09/2009 renvoie tous les enregistrements en passant par une égalité.
Si la précision est trop grande (au niveau de la seconde) cela ne fonctionnera pas.
@Column(name="JLTS_DATE", nullable=true)
private Date dayDate;
| Base de données | Type Base | Remarque |
| Oracle | date | Le stockage est sous une précision dd/MM/yyyy. L'égalité au niveau jour fonctionne dans les requêtes Oracle (en utilisant To_Date) |
| MySql | date | L'égalité au niveau jour fonctionne dans les requêtes MySql (en utilisant str_to_date) |
| HsqlDb | date | |
| Sql Server | datetime | Le stockage est sous une précision dd/MM/yyyy. L'égalité au niveau jour fonctionne dans les requêtes Sql Server(en utilisant Cast) |
Type Heure
Il s'agit d'une heure dans la journée, indépendante du jour. Ce type est rarement utilisé. Son manque de portabilité me pousse à préférer le TimeStamp.
@Temporal(TemporalType.TIME)
@Column(name="JLTS_TIME", nullable=true)
private Date time;
| Base de données | Type Base | Remarque |
| Oracle | date | Ce format ne fonctionne pas en Oracle |
| MySql | time | |
| HsqlDb | time | |
| Sql Server | datetime |
Type TimeStamp
Il s'agit d'une date précise souvent à la milliseconde qui permet de conserver une trace d'un événement. Ce type de colonne est souvent dans des tables d'historiques ou d'audit qui conservent une trace des modifications faites par les utilisateurs.
Ce type de trace sert principalement au besoin d'audit et de traçabilité de l'information.
Sa précision à la seconde ne permet pas de rechercher simplement les enregistrements dont la valeur du TimeStamp est égale à une date.
@Column(name="JLTS_TIMESTAMP", nullable=true)
private Date timeStampAudit;
| Base de données | Type Base | Remarque |
| Oracle | timestamp | |
| MySql | datetime | La précision de ce format est plus faible qu'en Oracle |
| HsqlDb | timestamp | |
| Sql Server | datetime | La précision de ce format est plus faible qu'en Oracle |
Type Identifiant Séquence
L'identifiant le plus simple est une clé numérique automatique. Oracle possède la notion de séquence pour cela. Dans d'autre cas, il faut passer par une autre table qui permet de stocker le compteur.
Pour la portabilité, il est souhaitable d'utiliser le type automatique suivant : GenerationType.AUTO.
GenerationType.SEQUENCE fonctionne bien avec Oracle mais pas avec MySql.
GenerationType.IDENTITY : fonctionne bien avec MySql mais pas avec Oracle.
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
@Column(name="LONG_ID", nullable=false)
private long id=0;
Cela correspond à une séquence
| Base de données | Type Base | Remarque |
| Oracle | number(19,0) | Il y a création est utilisation d'une Séquence Oracle hibernate_sequence. |
| MySql | bigint | |
| HsqlDb | bigint generated by default as identity (start with 1) | |
| Sql Server | numeric(19,0) identity |
Type Numéro de dossier
Il existe des informations qui sous une apparence numérique (elles ne sont composées que de chiffres) sont plus souvent manipulées comme une chaîne de caractères que comme un nombre.
Par exemple, on ne fait pas d'opération arithmétique sur un numéro de dossier ou le numéro de sécurité sociale : additionner un nombre, le multiplier, le diviser ou le comparer non pas de sens.
Il est plus courant de faire des concaténations de chaînes : le code sexe, le code département pour un numéro de sécurité sociale.
Pour un numéro de dossier, on peut trouver pratique de faire une recherche en ne tapant qu'une partie du numéro en utilisant un Like.
Pour ces champs, il est souhaitable d'utiliser un type String.
@Column(name="JLTS_NUMERO", nullable=true, length=10)
private String NumeroDossier;
| Base de données | Type Base | Remarque |
| Oracle | varchar2(10 char) | |
| MySql | varchar(10) | |
| HsqlDb | varchar(10) | |
| Sql Server | varchar(10) |
Nullité d'une colonne
Une colonne peut être nulle ce qui est différent de la valeur 0 pour un nombre et d'une chaîne vide "" pour une String. Oracle est une exception notoire pour les String : Oracle insère un Null dans une colonne quand on lui passe une chaîne vide. Ce point est encore d'actualité avec un Oracle 10G et le driver ojdc14.jar.
Les types Java acceptent s'ils sont d'un type objets (BigDecimal, Double, Long...) la valeur Null ce qui n'est pas le cas des types natifs (double, long, integer...). Le type String accepte la valeur Null.
D'un point de vu technique, il n'est pas judicieux d'utiliser un type natif si la colonne ne possède pas l'attribut Not Null : on risque des problèmes ou des incohérences si on récupère une valeur Null.
D'un point de vu fonctionnel, le Null doit servir à distinguer une valeur non saisie des autres : On utilisera le Null s'il est pertinent de distinguer 0 de l'absence de saisie.
Par exemple, il n'est pas souhaitable que la note d'un enfant soit par défaut 0. Il est important d'être capable de distinguer l'absence de notation (le Null) d'une note nulle (0).
Dans le cas, d'un rabais, c'est probablement l'inverse, on souhaite que par défaut la valeur soit 0. Cela permet de faire simplement les calculs. La valeur peut rester à 0 sans grand impact. En règle générale, le changement de cette valeur nécessite des attributions spéciales (par exemple être manager).
Pour les chaînes de caractères on évitera de distinguer le cas des null des chaînes vides afin de conserver la portabilité vers Oracle. Cela n'est pas nécessairement un problème important. Le statut d'un objet (qui peut être détecté par la différence entre un champ Null et vide) peut également et probablement mieux rendu par une colonne spécifique STATUT/ETAT (il s'agit du type Code).
Pour les nombres, le choix est plus délicat :
- l'absence de null facilite les calculs : le calcul n'a pas beson de tester l'existence d'un Null
- le 0 peut souvent être pris comme une valeur par défaut
- il existe des cas (comme pour une note) ou le 0 ne peut être considéré comme une valeur par défaut, dans ce cas, le Null est indispensable.
Il ne faut pas perdre de vu que le créateur de l'objet métier n'a pas nécessairement tous les droits pour saisir toutes les valeurs. il peut s'agir d'un opérateur de saisi qui n'a pas les habilitations pour remplir certaines valeurs sensibles. Dans ce type de process, il peut être important que la personne qui possède les droits pour compléter le dossier puisse distinguer d'un rapide coup d'oeil les informations qui lui reste à saisir. Dans ce cas, les Null peuvent être utiles même si dans d'autres cas, la valeur 0 sera très pertinente.
Dans notre exemple précédent, le rabais pourra rester à 0.
S'il faut une règle simple : je donnerai la règle suivante : Le null est utile si la valeur par défaut du champ ne peut pas être 0, dans les autres cas, il sera préférable de ne pas utiliser de Null.
Conclusion
La phase de conception du modèle de données peut être grandement simplifiée en limitant la définition des types à la liste précédemment donnée :
| Type | Notation JPA | Oracle | Mysql | HsqlDb | Sql Server |
| Montant monétaires | @Column(name="JLTS_AMOUNT", nullable=true, precision=19, scale=2) private BigDecimal amount; | NUMBER(19,2) | decimal(19,2) | numeric | numeric(19,2) |
| Mesure | @Column(name="JLTS_MESURE", nullable=false) private double mesure; | double precision | double precision | double | double precision |
| Taux et pourcentages | @Column(name="JLTS_TAUX", nullable=true, precision=7, scale=4) private BigDecimal taux; | NUMBER(7,4) | decimal(7,4) | numeric | numeric(7, 4) |
| Quantité | @Column(name="JLTS_LONG", nullable=false) private long longNumeric; | NUMBER(19,0) | bigint | bigint | numeric(19,0) |
| Chaînes de caractères | @Column(name="JLTS_STRING", length=40) private String textString; | VARCHAR2(40 CHAR) | varchar(40) | varchar(40) | varchar(40) |
| Code | @Column(name="JLTS_SHORTCODE", nullable=true, length=2) private String shortCode; | VARCHAR2(2 CHAR) | varchar(2) | varchar(2) | varchar(2) |
| Commentaire | @Lob @Column(name="JLTS_COMMENT") private String commentaire; | CLOB | longtext | longvarchar | text |
| Vrai-Fausse et Booléen | @Column(name="JLTS_BOOLEAN") private boolean booleanType; | NUMBER(1,0) | bit | bit | tinyint |
| Liste déroulante | @Column(name="JLTS_SHORTCODE", nullable=true, length=2) private String shortCode; | VARCHAR2(2 CHAR) | varchar(2) | varchar(2) | varchar(2) |
| Date | @Temporal(TemporalType.DATE) @Column(name="JLTS_DATE", nullable=true) private Date dayDate; | DATE | date | date | datetime |
| Heure | @Temporal(TemporalType.TIME) @Column(name="JLTS_TIME", nullable=true) private Date time; | DATE | time | time | datetime |
| Tags horaires (TimeStamp) | @Temporal(TemporalType.TIMESTAMP) @Column(name="JLTS_TIMESTAMP", nullable=true) private Date timeStampAudit; | TIMESTAMP | datetime | timestamp | datetime |
| Numéro de dossier | @Column(name="JLTS_NUMERO", nullable=true, length=10) private String numero; | VARCHAR2(10 CHAR) | varchar(10) | varchar(10) | varchar(10) |
| Séquence | @Id @GeneratedValue(strategy=GenerationType.AUTO) @Column(name="LONG_ID", nullable=false) private long id=0; | NUMBER(19,0) | bigint | bigint | numeric(19,0) identity |
Dans le cas d'une phase de ré-ingénierie d'un modèle de données existant, il sera probablement possible de se baser sur les mêmes types en changeant peut être leur représentation JPA afin de l'adapter à l'existant en base.
Par exemple, si en base les montants sont des NUMBER(11,2), le type montant pourra être de type
@Column(name="JLTS_MONTANT", precision=11, scale=2, nullable=false)
private BigDecimal montant;
Dans la pratique, on ne devrait pas voir apparaître plus de deux déclinaisons d'un même type dans un modèle existant.
J'espère que ce tutorial simplifiera la conception de vos prochains modèles de données. Un prochain billet présentera un projet de test permettant de mieux appréhender les différents comportements.
DiggIt!
Enregistrer sur Del.icio.us