151 votes

Hibernate, @SequenceGenerator et allocationSize

Nous connaissons tous le comportement par défaut d'Hibernate lors de l'utilisation de @SequenceGenerator - il augmente la séquence réelle de la base de données de un multiple de cette valeur par 50 (par défaut) allocationSize ) - et utilise ensuite cette valeur comme identifiant de l'entité.

Ce comportement est incorrect et entre en conflit avec spécification qui dit :

allocationSize - (Facultatif) Le montant à incrémenter lors de l'allocation des numéros de séquence de la séquence.

Pour être clair : je ne me préoccupe pas des écarts entre les identifiants générés.

Je m'intéresse aux identifiants qui sont pas de cohérence avec la séquence de base de données sous-jacente. Par exemple, toute autre application (qui utilise par exemple JDBC) peut vouloir insérer de nouvelles lignes sous les ID obtenus à partir de la séquence - mais toutes ces valeurs peuvent être déjà utilisées par Hibernate ! La folie.

Est-ce que quelqu'un connaît une solution à ce problème (sans paramétrer allocationSize=1 et donc une dégradation des performances) ?

EDITAR:
Pour que les choses soient claires. Si le dernier enregistrement inséré avait un ID = 1 alors HB utilise les valeurs 51, 52, 53... pour ses nouvelles entités MAIS en même temps : la valeur de la séquence dans la base de données sera fixée à 2 . Ce qui peut facilement conduire à des erreurs lorsque d'autres applications utilisent cette séquence.

D'autre part, la spécification indique (d'après ce que j'ai compris) que la séquence de la base de données aurait dû être définie comme suit 51 et dans l'intervalle, HB devrait utiliser les valeurs de l'intervalle 2, 3 ... 50

UPDATE :
Comme Steve Ebersole l'a mentionné ci-dessous : le comportement que j'ai décrit (et qui est aussi le plus intuitif pour beaucoup) peut être activé en paramétrant hibernate.id.new_generator_mappings=true .

Merci à vous tous.

UPDATE 2 :
Pour les futurs lecteurs, vous trouverez ci-dessous un exemple fonctionnel.

@Entity
@Table(name = "users")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "USERS_SEQ")
    @SequenceGenerator(name = "USERS_SEQ", sequenceName = "SEQUENCE_USERS")
    private Long id;
}

persistance.xml

<persistence-unit name="testPU">
  <properties>
    <property name="hibernate.id.new_generator_mappings" value="true" />
  </properties>
</persistence-unit>

4 votes

"sans mettre allocationSize=1 et donc dégrader les performances" pourquoi dégrader les performances si vous le mettez à 1 ?

3 votes

@sheidaei voir mai commentaire ci-dessous :-) C'est parce que chaque save doit interroger la base de données pour connaître la valeur suivante de la séquence.

0 votes

Merci, j'étais confronté au même problème. Au début, j'ajoutais allocationSize = 1 à chaque @SequenceGenerator. L'utilisation de hibernate.id.new_generator_mappings=true empêche cela. Bien que JPA interroge toujours la base de données pour obtenir l'identifiant pour chaque insertion ...

1voto

Jihwan Points 1

Steve Ebersole & autres membres,
Auriez-vous l'amabilité d'expliquer la raison d'un id avec un écart plus grand (par défaut 50) ? J'utilise Hibernate 4.2.15 et j'ai trouvé le code suivant dans org.hibernate.id.enhanced.OptimizerFactory cass.

if ( lo > maxLo ) {
   lastSourceValue = callback.getNextValue();
   lo = lastSourceValue.eq( 0 ) ? 1 : 0;
   hi = lastSourceValue.copy().multiplyBy( maxLo+1 ); 
}  
value = hi.copy().add( lo++ );

Chaque fois qu'il atteint l'intérieur de l'instruction if, sa valeur devient beaucoup plus grande. Ainsi, mon id pendant le test avec le redémarrage fréquent du serveur génère les ids de séquence suivants :
1, 2, 3, 4, 19, 250, 251, 252, 400, 550, 750, 751, 752, 850, 1100, 1150.

Je sais que vous avez déjà dit qu'il n'y avait pas de conflit avec la spécification, mais je crois que ce sera une situation très inattendue pour la plupart des développeurs.

L'avis de chacun sera très utile.

Jihwan

MISE À JOUR : ne1410s : Merci pour la modification.
cfrick : OK. Je vais le faire. C'était mon premier message ici et je ne savais pas trop comment l'utiliser.

Maintenant, je comprends mieux pourquoi maxLo était utilisé à deux fins : Puisque Hibernate appelle la séquence DB une fois, garde l'augmentation de l'id au niveau Java, et l'enregistre dans la DB, la valeur de l'id au niveau Java devrait prendre en compte combien a été changé sans appeler la séquence DB quand il appelle la séquence la fois suivante.

Par exemple, le numéro de séquence était 1 à un moment donné et hibernate a entré 5, 6, 7, 8, 9 (avec allocationSize = 5). La fois suivante, lorsque nous obtenons le numéro de séquence suivant, DB renvoie 2, mais hibernate doit utiliser 10, 11, 12... C'est pourquoi "hi = lastSourceValue.copy().multiplyBy( maxLo+1 )" est utilisé pour obtenir le prochain id 10 à partir du 2 renvoyé par la séquence DB. Il semble que la seule chose gênante était lors du redémarrage fréquent du serveur et c'était mon problème avec le plus grand écart.

Ainsi, lorsque nous utilisons l'ID de SEQUENCE, l'ID inséré dans la table ne correspondra pas au numéro de SEQUENCE dans la BD.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X