Nous connaissons tous le comportement par défaut de Hibernate lors de l'utilisation @SequenceGenerator
- il augmente la séquence de base de données réelle de un , multiplie cette valeur par 50 (valeur par défaut allocationSize
) - puis utilise cette valeur comme ID d'entité.
C'est un comportement incorrect et entre en conflit avec la spécification qui dit:
allocationSize - (Facultatif) Le montant à incrémenter lors de l'allocation des numéros de séquence à partir de la séquence.
Pour être clair: je ne me soucie pas des écarts entre les identifiants 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 brut) peut vouloir insérer de nouvelles lignes sous les ID obtenus à partir de la séquence - mais toutes ces valeurs peuvent déjà être utilisées par Hibernate! La démence.
Quelqu'un connaît-il une solution à ce problème (sans réglage allocationSize=1
et donc dégradation des performances)?
EDIT:
Pour clarifier les choses. Si le dernier enregistrement inséré avait ID = 1
, alors HB utilise des 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 dit (à ma connaissance) que la séquence de la base de données aurait dû être définie sur 51
et, entre-temps, HB devrait utiliser des valeurs de range 2, 3 ... 50
MISE
À JOUR: 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.
MISE À JOUR 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;
}
persistence.xml
<persistence-unit name="testPU">
<properties>
<property name="hibernate.id.new_generator_mappings" value="true" />
</properties>
</persistence-unit>
save
doit interroger la base de données pour la valeur suivante de la séquence.SequenceGenerator
Hibernate interrogera la base de données uniquement lorsque le nombre d'ID spécifiés parallocationsize
s'épuise. Si vous configurez,allocationSize = 1
c'est la raison pour laquelle Hibernate interroge la base de données pour chaque insertion. Modifiez cette valeur et vous avez terminé.hibernate.id.new_generator_mappings
cadre est vraiment important. J'espère que c'est le paramètre par défaut que je n'ai pas à passer autant de temps à rechercher pourquoi le numéro d'identification devient sauvage.Réponses:
Pour être absolument clair ... ce que vous décrivez n'est en aucun cas en conflit avec la spécification. 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 base de données.
Cependant, il existe la 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 en
allocationSize
utilisant «l'optimiseur groupé» dans SequenceStyleGenerator. L '"optimiseur groupé" est destiné à être utilisé avec des bases de données qui permettent une option "incrémenter" sur la création de séquences (toutes les bases de données prenant en charge les séquences ne prennent pas en charge un incrément). Quoi qu'il en soit, renseignez-vous sur les différentes stratégies d'optimisation.la source
org.hibernate.id.enhanced.SequenceStyleGenerator
. Vous m'avez surpris.allocationSize=1
Il s'agit d'une micro-optimisation avant d'obtenir la requête Hibernate essaie d'attribuer une valeur dans la plage d'allocationSize et essaie donc 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 définissez sur 1. Cela ne fait pratiquement aucune différence puisque 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 d'ID de séquence est basée sur allocationSize.
Par défaut, il est conservé comme
50
ce qui est trop. Cela ne vous aidera également que si vous allez avoir près d'50
enregistrements dans une session qui ne sont pas persistants et qui seront conservés en utilisant cette session et cette transition particulières.Vous devez donc toujours utiliser
allocationSize=1
lors de l'utilisationSequenceGenerator
. Comme pour la plupart des bases de données sous-jacentes, la séquence est toujours incrémentée de1
.la source
allocationSize=1
Hibernate, chaquesave
opération doit faire le voyage vers la base de données afin d'obtenir une nouvelle valeur d'identification.allocationSize
et donc 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 définissez sur 1. Cela ne fait pratiquement aucune différence puisque 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 entreSteve Ebersole et autres membres,
pourriez-vous expliquer la raison d'un identifiant 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.
Chaque fois qu'il atteint l'intérieur de l'instruction if, la valeur hi devient beaucoup 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, 1150.
Je sais que vous avez déjà dit que cela n'était pas en conflit avec les spécifications, mais je pense que ce sera une situation très inattendue pour la plupart des développeurs.
La contribution de n'importe qui sera très utile.
Jihwan
MISE À JOUR: ne1410s: Merci pour la modification.
cfrick: OK. Je le ferai. C'était mon premier message ici et je ne savais pas comment l'utiliser.
Maintenant, j'ai mieux compris pourquoi maxLo a été utilisé à deux fins: comme la mise en veille prolongée appelle la séquence DB une fois, continuez d'augmenter l'id au niveau Java et l'enregistre dans la base de données, la valeur de l'id de niveau Java devrait considérer combien a été changé sans appeler la séquence DB lors de l'appel de la séquence la prochaine fois.
Par exemple, l'ID de séquence était 1 à un point et la mise en veille prolongée a entré 5, 6, 7, 8, 9 (avec allocationSize = 5). La prochaine fois, 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 un prochain identifiant 10 à partir des 2 renvoyés par la séquence DB. Il semble que la seule chose gênante ait été 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 DB.
la source
Après avoir fouillé dans le code source de mise en veille prolongée et la configuration ci-dessous, accédez à Oracle db pour la valeur suivante après 50 insertions. Faites donc votre INST_PK_SEQ incrémenter de 50 chaque fois qu'il est appelé.
Hibernate 5 est utilisé pour la stratégie ci-dessous
Vérifiez également ci-dessous http://docs.jboss.org/hibernate/orm/5.1/userguide/html_single/Hibernate_User_Guide.html#identifiers-generators-sequence
la source
allocationSize
niinitialValue
globalement pour toutes les entités (à moins d'utiliser un seul générateur, mais à mon humble avis, ce n'est pas très lisible).J'ai également rencontré ce problème dans Hibernate 5:
Vous avez un avertissement comme celui-ci ci-dessous:
Ensuite, j'ai changé mon code en SequenceStyleGenerator:
Cela a résolu mes deux problèmes:
la source
Je vérifierais le DDL pour la séquence dans le schéma. L'implémentation JPA est responsable uniquement de la création de la séquence avec la taille d'allocation correcte. Par conséquent, si la taille d'allocation est de 50, votre séquence doit avoir l'incrément de 50 dans sa DDL.
Ce cas peut généralement se produire avec la création d'une séquence avec la taille d'allocation 1 puis configurée ultérieurement à la taille d'allocation 50 (ou par défaut) mais la séquence DDL n'est pas mise à jour.
la source
ALTER SEQUENCE ... INCREMENTY BY 50;
ne résoudra rien, car le problème reste le même. La valeur de séquence ne reflète toujours pas les ID d'entités réels.