web-dev-qa-db-fra.com

Hibernate, @SequenceGenerator et allocationSize

Nous connaissons tous le comportement par défaut d’Hibernate lorsqu’on utilise @SequenceGenerator - il augmente la séquence réelle de la base de données de un , multiplie cette valeur par 50 (valeur par défaut). allocationSize value) - et utilise ensuite cette valeur comme ID d'entité.

C'est un comportement incorrect et en conflit avec spécification qui dit:

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

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

Je me soucie des ID qui ne sont pas cohérents avec la séquence de base de données sous-jacente. Par exemple: toute autre application (qui utilise par exemple JDBC en clair) peut vouloir insérer de nouvelles lignes sous des ID obtenus à partir de la séquence - mais toutes ces valeurs peuvent déjà être utilisées par Hibernate! La démence.

Est-ce que quelqu'un connaît une solution à ce problème (sans régler allocationSize=1 Et donc dégrader les performances)?

EDIT:
Pour clarifier les choses. Si le dernier enregistrement inséré avait un ID = 1, Alors HB utilisera 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 définie sur 2. Ce qui peut facilement conduire à des erreurs lorsque d'autres applications utilisent cette séquence.

D'un autre côté: la spécification indique (à ma connaissance) que la séquence de la base de données aurait dû être définie sur 51 Et qu'entre-temps, HB devrait utiliser des valeurs comprises dans la plage 2, 3 ... 50


UPDATE:
Comme Steve Ebersole l’a mentionné ci-dessous: le comportement décrit par moi (et aussi le plus intuitif pour beaucoup) peut être activé en définissant hibernate.id.new_generator_mappings=true.

Merci à vous tous.

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

@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;
}

persistence.xml

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

Pour être absolument clair ... ce que vous décrivez n'entre pas en conflit avec la spécification de quelque manière que ce soit. Les spécifications parlent des valeurs que Hibernate attribue à vos entités, pas des valeurs réellement stockées dans la séquence de base de données.

Cependant, il existe une option pour obtenir le comportement que vous recherchez. Voir d’abord ma réponse sur Y at-il un moyen de choisir de manière dynamique une stratégie @GeneratedValue en utilisant des annotations JPA et Hibernate? Cela vous donnera les bases. Tant que vous êtes configuré pour utiliser ce SequenceStyleGenerator, Hibernate interprétera allocationSize à l'aide de "l'optimiseur en pool" du SequenceStyleGenerator. "L'optimiseur en pool" est destiné à être utilisé avec des bases de données qui permettent une option "incrémentation" lors de la création de séquences (toutes les bases de données prenant en charge des séquences ne prennent pas en charge l'incrémentation). Quoi qu'il en soit, renseignez-vous sur les différentes stratégies d'optimisation disponibles.

36
Steve Ebersole

allocationSize=1 C’est une micro optimisation avant d’obtenir une requête Hibernate essaie d’attribuer une valeur dans la plage de allocationSize et évite donc d’interroger la base de données. Mais cette requête sera exécutée à chaque fois si vous la définissez sur 1. Cela ne fait guère de différence, car si votre base de données est accédée par une autre application, cela créera des problèmes si le même identifiant est utilisé par une autre application entre-temps.

La prochaine génération de Sequence Id est basée sur allocationSize.

Par défaut, il est conservé comme 50 qui est trop. Cela n’aidera que si vous allez avoir environ 50 enregistrements dans une session qui ne sont pas conservés et qui le seront à l’aide de cette session et de cette transation particulières.

Donc, vous devriez toujours utiliser allocationSize=1 en utilisant SequenceGenerator. Comme pour la plupart des bases de données sous-jacentes, la séquence est toujours incrémentée de 1.

12
Amit Deshpande

Après avoir creusé dans le code source hibernate et dans la configuration ci-dessous, la base de données Oracle passe à la valeur suivante après 50 insertions. Donc, faites que votre INST_PK_SEQ incrémente de 50 à chaque appel.

Hibernate 5 est utilisé pour la stratégie ci-dessous

Voir aussi ci-dessous http://docs.jboss.org/hibernate/orm/5.1/userguide/html_single/Hibernate_User_Guide.html#identifiers-generators-sequence

@Id
@Column(name = "ID")
@GenericGenerator(name = "INST_PK_SEQ", 
strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator",
parameters = {
        @org.hibernate.annotations.Parameter(
                name = "optimizer", value = "pooled-lo"),
        @org.hibernate.annotations.Parameter(
                name = "initial_value", value = "1"),
        @org.hibernate.annotations.Parameter(
                name = "increment_size", value = "50"),
        @org.hibernate.annotations.Parameter(
                name = SequenceStyleGenerator.SEQUENCE_PARAM, value = "INST_PK_SEQ"),
    }
)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "INST_PK_SEQ")
private Long id;
1
fatih tekin

Steve Ebersole et d'autres membres,
Pourriez-vous expliquer la raison d’un identifiant avec un écart plus important (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 que cela touche l'intérieur de l'instruction if, sa valeur devient de plus en plus grande. Ainsi, mon identifiant lors des tests avec le redémarrage fréquent du serveur génère les identifiants de séquence suivants:
1, 2, 3, 4, 19, 250, 251, 252, 400, 550, 750, 751, 752, 850, 1100 et 1150.

Je sais que vous avez déjà dit que cela n'entrait pas en conflit avec la spécification, mais je pense que cette situation sera très inattendue pour la plupart des développeurs.

La contribution de quiconque sera très utile.

Jihwan

UPDATE: ne1410s: Merci pour l'édition.
cfrick: OK. Je le ferai. C'était mon premier post ici et je ne savais pas comment l'utiliser.

Maintenant, j'ai mieux compris pourquoi maxLo était utilisé à deux fins: étant donné que l'hibernate appelle la séquence de bases de données une fois, maintenez l'option d'augmentation Java) et enregistrez-la dans la base de données, Java doit prendre en compte le nombre de modifications effectuées sans appeler la séquence de base de données lors de son prochain appel.

Par exemple, l'ID de séquence était égal à 1 en un point et l'hibernation a saisi 5, 6, 7, 8, 9 (avec allocationSize = 5). La prochaine fois, lorsque nous aurons 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 un identifiant suivant 10 parmi les 2 renvoyés par la séquence de base de données. Il semble que seul le problème soit survenu lors du redémarrage fréquent du serveur, ce qui était mon problème avec l'écart plus important.

Ainsi, lorsque nous utilisons l'ID SEQUENCE, l'ID inséré dans la table ne correspond pas au numéro SEQUENCE dans la base de données.

1
Jihwan

Je vérifierais le DDL pour la séquence dans le schéma. L'implémentation JPA est uniquement responsable de la création de la séquence avec la taille d'allocation correcte. Par conséquent, si la taille d'allocation est 50, votre séquence doit avoir l'incrément de 50 dans sa DDL.

Ce cas peut généralement se produire lors de la création d'une séquence avec une taille d'allocation 1, puis configuré ultérieurement pour une taille d'allocation 50 (ou par défaut), mais la séquence DDL n'est pas mise à jour.

0
Hasan Ceylan

Moi aussi j'ai fait face à ce problème dans Hibernate 5:

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = SEQUENCE)
@SequenceGenerator(name = SEQUENCE, sequenceName = SEQUENCE)
private Long titId;

Vous avez un avertissement comme celui-ci ci-dessous:

Utilisation trouvée du générateur d'identifiant basé sur une séquence [obsolète [org.hibernate.id.SequenceHiLoGenerator]]; utilisez plutôt org.hibernate.id.enhanced.SequenceStyleGenerator. Consultez le Guide de mappage de modèle de domaine Hibernate pour plus de détails.

Puis changé mon code à SequenceStyleGenerator:

@Id
@GenericGenerator(name="cmrSeq", strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator",
        parameters = {
                @Parameter(name = "sequence_name", value = "SEQUENCE")}
)
@GeneratedValue(generator = "sequence_name")
private Long titId;

Cela a résolu mes deux problèmes:

  1. L'avertissement déconseillé est corrigé
  2. Maintenant, l'id est généré selon la séquence Oracle.
0
Mohamed Afzal