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 ...

63voto

Steve Ebersole Points 3544

Pour être absolument clair... ce que vous décrivez n'est pas no entrer en conflit avec la spécification de quelque manière que ce soit. La spécification parle des valeurs qu'Hibernate attribue à vos entités, et non des valeurs réellement stockées dans la séquence de la base de données.

Cependant, il existe une possibilité d'obtenir le comportement que vous recherchez. Voir d'abord ma réponse sur Existe-t-il un moyen de choisir dynamiquement une stratégie @GeneratedValue en utilisant les annotations JPA et Hibernate ? Cela vous donnera les bases. Tant que vous êtes configuré pour utiliser ce SequenceStyleGenerator, Hibernate interprétera les éléments suivants allocationSize en utilisant l'"optimiseur groupé" dans le SequenceStyleGenerator. L'"optimiseur groupé" est utilisé avec les bases de données qui autorisent une option d'"incrémentation" lors de la création de séquences (toutes les bases de données qui prennent en charge les séquences n'autorisent pas l'incrémentation). Quoi qu'il en soit, renseignez-vous sur les différentes stratégies d'optimisation.

0 votes

Merci Steve ! La meilleure réponse. Aussi, votre autre poste a été utile.

6 votes

J'ai aussi remarqué que vous êtes co-auteur de org.hibernate.id.enhanced.SequenceStyleGenerator . Vous m'avez surpris.

34 votes

Vous avez été surpris comment ? Je suis le principal développeur d'Hibernate. J'ai écrit/coécrit de nombreuses classes Hibernate ;)

25voto

AmitD Points 12541

allocationSize=1 Il s'agit d'une micro-optimisation. Avant d'obtenir la requête, Hibernate essaie d'assigner une valeur dans la plage de allocationSize et ainsi d'éviter d'interroger la base de données pour la séquence. Mais cette requête sera exécutée à chaque fois si vous la fixez à 1. Cela ne fait guère de différence car si votre base de données est accessible 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 ce qui est trop. Il ne sera également utile que si vous avez près de 50 les enregistrements d'une session qui ne sont pas persistants et qui seront persistants en utilisant cette session et cette transation particulières.

Vous devez donc 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 par 1 .

18 votes

Rien à voir avec les performances ? Est-ce que vous vraiment sûr ? On m'a appris qu'avec allocationSize=1 Hibernation sur chaque save doit se rendre dans la base de données afin d'obtenir une nouvelle valeur d'identification.

2 votes

Il s'agit d'une micro-optimisation avant d'obtenir la requête, Hibernate essaie d'assigner une valeur dans la gamme de allocationSize et donc essayer d'éviter d'interroger la base de données pour la séquence. Mais cette requête sera exécutée à chaque fois si vous la mettez à 1. Cela ne fait guère de différence car si votre base de données est accessible par une autre application, cela créera des problèmes si le même identifiant est utilisé par une autre application entre-temps.

0 votes

Et oui, le fait qu'une taille d'allocation de 1 ait un impact réel sur les performances dépend entièrement de l'application. Dans un micro benchmark, bien sûr, cela va toujours apparaître comme un impact énorme ; c'est le problème avec la plupart des benchmarks (micro ou autres), ils ne sont tout simplement pas réalistes. Et même s'ils sont suffisamment complexes pour être quelque peu réalistes, vous devez toujours regarder à quel point le benchmark est proche de votre application réelle pour comprendre dans quelle mesure les résultats du benchmark sont applicables aux résultats que vous verriez dans votre application. En bref, testez-le vous-même.

3voto

Hasan Ceylan Points 460

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

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

0 votes

Vous ne comprenez pas mon point de vue. ALTER SEQUENCE ... INCREMENTY BY 50; ne résoudra rien, car le problème reste le même. La valeur de la séquence ne reflète toujours pas les ID des entités réelles.

0 votes

Veuillez partager un cas de test afin que nous puissions mieux comprendre le problème.

1 votes

Cas d'essai ? Pourquoi ? La question que j'ai postée n'était pas très compliquée et a déjà reçu une réponse. Il semble que vous ne sachiez pas comment fonctionne le générateur HiLo. Quoi qu'il en soit, je vous remercie d'avoir sacrifié votre temps et vos efforts.

1voto

fatih tekin Points 154

Après avoir creusé dans le code source d'Hibernate et dans la base de données d'Oracle, j'ai décidé d'utiliser la configuration suivante. La configuration ci-dessous va chercher la valeur suivante dans la base de données Oracle après 50 insertions. Il faut donc que votre INST_PK_SEQ incrémente de 50 à chaque fois qu'il est appelé.

Hibernate 5 est utilisé pour la stratégie suivante

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;

4 votes

Désolé, mais c'est une façon extrêmement verbeuse de configurer quelque chose, qui peut être exprimé facilement avec deux paramètres pour tout Hibernate et donc pour toutes les entités.

0 votes

C'est vrai, mais quand j'ai essayé avec d'autres méthodes, aucune n'a fonctionné. Si vous avez réussi à le faire fonctionner, envoyez-moi votre configuration.

0 votes

J'ai mis à jour ma réponse - maintenant elle inclut aussi un exemple de travail. Bien que mon commentaire ci-dessus soit partiellement erroné : malheureusement, vous ne pouvez pas définir ni l'un ni l'autre des éléments suivants allocationSize ni initialValue globalement pour toutes les entités (sauf si vous n'utilisez qu'un seul générateur, mais, à mon avis, ce n'est pas très lisible).

1voto

Mohamed Afzal Points 101

J'ai également rencontré ce problème dans Hibernate 5 :

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

J'ai reçu un avertissement comme celui-ci ci-dessous :

L'utilisation du générateur d'identifiants basé sur la séquence [org.hibernate.id.SequenceHiLoGenerator] est obsolète ; utilisez plutôt org.hibernate.id.enhanced.SequenceStyleGenerator. Voir le Guide de mappage du modèle de domaine Hibernate pour plus de détails.

J'ai ensuite changé mon code en 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 obsolète est corrigé
  2. Maintenant l'identifiant est généré selon la séquence de l'oracle.

0 votes

Vous pouvez utiliser votre mappage d'origine à condition d'indiquer à Hibernate d'utiliser ses "nouveaux générateurs" - définir hibernate.id.new_generator_mappings à vrai. Et pour répondre de manière proactive à ce que je vois comme un suivi... n'oubliez pas qu'Hibernate a littéralement 20 ans, qu'il existe de NOMBREUX déploiements existants et que nous ne pouvons pas simplement changer la façon dont la génération d'identifiants fonctionne. par défaut sans casser toutes ces applications existantes...

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