web-dev-qa-db-fra.com

JPA GenerationType.AUTO ne prenant pas en compte la colonne avec incrémentation automatique

J'ai une table avec une colonne simple d'id int avec l'incrémentation automatique d'identité dans SQL Server.

L'identité de l'entité est annotée avec @Id et @GeneratedValue

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id", length = 4, precision = 10, nullable = false)
private Integer id;

Dans SQL Server, la colonne est correctement définie en tant qu'identité avec Seed et Increment égal à 1.

Lorsque j'essaie de conserver une instance de cette entité, Hibernate essaie d'interroger la table hibernate_sequence pour obtenir la valeur de l'ID. Comme je n'ai pas créé cette table dans mon schéma, je reçois une erreur:

could not read a hi value: com.Microsoft.sqlserver.jdbc.SQLServerException: Invalid object name 'MySchema.hibernate_sequence'

Si je change le type de génération en IDENTITÉ, tout fonctionne comme prévu

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", length = 4, precision = 10, nullable = false)
private Integer id;

Je ne peux pas le changer de cette façon, car mon application s'exécutera à la fois sur MS SQL et Oracle, et ce dernier ne prend pas en charge les colonnes incrémentées automatiquement.

Autant que je sache, le type AUTO doit utiliser le comportement d'incrémentation automatique si la base de données sous-jacente est prise en charge. Je ne sais donc pas pourquoi ne fonctionne pas.

METTRE À JOUR:

Cela m'a pris du temps mais j'ai pu comprendre exactement ce qui se passait.

Je travaille avec des bases de données existantes avec les comportements suivants:

  • MSSQL: la génération d'id utilise la table IDENTITY
  • Oracle: la génération d'identifiant utilise un déclencheur. Le déclencheur interroge et met à jour une table personnalisée dans laquelle sont stockés tous les "prochains identifiants". Cette table s'appelle SEQ.

Voici le résultat de l'utilisation de certaines stratégies de génération d'identifiant:

  • AUTO: ne fonctionne pas dans MSSQL, comme expliqué ci-dessus
  • IDENTITY: fonctionne dans MSSQL mais n'est pas supporté par Oracle
  • "native": fonctionne dans MSSQL mais échoue dans Oracle. Il échoue car Hibernate active sa stratégie de séquence par défaut, qui utilise hibernate_sequences.nextval. Comme il s’agit d’une application héritée, les valeurs de la table SEQ (mentionnées ci-dessus) et des hibernate_sequences ne sont pas synchronisées (la valeur de SEQ pour cette table particulière est 6120, et hibernate_sequences 'est égal à 1, ce qui est attendu depuis pas utilisé jusqu'à présent).

Il me faut donc trouver un moyen de configurer cette entité pour:

  • Utiliser la fonction d’identité MSSQL OU
  • Lors de l'utilisation d'Oracle, ne définissez aucune valeur sur la variable ID et laissez le tout au déclencheur préexistant.

Cela peut entraîner de graves problèmes sur Oracle lorsque je dois insérer des entités dépendant de l'entité principale (via une clé étrangère), car Hibernate ne saura pas quelle valeur d'ID a été générée par le déclencheur "external".

9
pipo_dev

J'ai eu un problème similaire et j'ai trouvé cette information (expliqué plus en détail dans ici ).

L'ajout de cette propriété dans mon fichier persistence.xml a résolu le problème suivant:

<property name="hibernate.id.new_generator_mappings" value="false" />
9
Daniel Rodríguez

Orcale 12c prend en charge IDENTITY et SQL SERVER 2012 prend en charge SEQUENCES. Je crois qu'un SEQUENCE est toujours un meilleur choix qu'un IDENTITY . IDENTITY désactive le traitement par lots et SEQUENCES vous permet de fournir des optimiseurs, tels que la stratégie d'optimisation pooled-lo .

Voici comment le générateur d'identifiant réel est choisi pour la valeur GenerationType configurée:

switch ( generatorEnum ) {
    case IDENTITY:
        return "identity";
    case AUTO:
        return useNewGeneratorMappings
                ? org.hibernate.id.enhanced.SequenceStyleGenerator.class.getName()
                : "native";
    case TABLE:
        return useNewGeneratorMappings
                ? org.hibernate.id.enhanced.TableGenerator.class.getName()
                : MultipleHiLoPerTableGenerator.class.getName();
    case SEQUENCE:
        return useNewGeneratorMappings
                ? org.hibernate.id.enhanced.SequenceStyleGenerator.class.getName()
                : "seqhilo";
}
  • Si vous utilisez les nouveaux identificateurs générateurs :

    properties.put ("hibernate.id.new_generator_mappings", "true");

    L’automate utilisera en fait un SequenceStyleGenerator et si la base de données ne prend pas en charge les séquences, vous finissez par utiliser un générateur TABLE (solution portable mais moins efficace que IDENTITY ou SEQUENCE).

  • Si vous utilisez les générateurs d'identifiants hérités, vous obtenez alors la stratégie de génération "native", à savoir:

    public Class getNativeIdentifierGeneratorClass() {
        if ( supportsIdentityColumns() ) {
            return IdentityGenerator.class;
        }
        else if ( supportsSequences() ) {
            return SequenceGenerator.class;
        }
        else {
            return TableHiLoGenerator.class;
        }
    }   
    

Si un nouvel Oracle12gDialect doit être ajouté et prend en charge IDENTITY, alors AUTO peut basculer sur IDENTITY plutôt que sur SEQUENCE, ce qui pourrait éventuellement dépasser vos attentes actuelles. À l'heure actuelle, aucun dialecte de ce type n'est disponible, vous avez donc SEQUENCE sur Oracle et IDENTITY dans MSSQL.

Conclusion:

Essayez comme ça:

 @Id
 @GenericGenerator(name = "native_generator", strategy = "native")
 @GeneratedValue(generator = "native_generator")
 private Long id;
  • faites de l'id un long au lieu d'un entier, et vous pouvez laisser le HBMDDL gérer le type de colonne de clé primaire.
  • forcer Hibernate à utiliser le générateur "natif"

Si votre système existant utilise une table pour générer des valeurs de séquence et qu'aucune optimisation hilo n'a été utilisée, vous pouvez utiliser un générateur d'identificateur de table:

@Id
@GeneratedValue(generator = "table", strategy=GenerationType.TABLE)
@TableGenerator(name = "table", allocationSize = 1
)
private Long id;

Vous pouvez également utiliser le générateur de table JPA. Assurez-vous simplement de configurer le bon optimiseur. Pour plus d'informations, consultez mon tutoriel Hibernate

5
Vlad Mihalcea

parce que 

@GeneratedValue(strategy = GenerationType.AUTO) 

utiliser SequenceStyleGenerator par défaut dans les versions antérieures 

vous devez regarder ceci https://hibernate.atlassian.net/browse/HHH-11014

0
Muhammad Rfeaat