Impossible d'utiliser la génération de clé de colonne d'identité avec <union-subclass> (TABLE_PER_CLASS)

96

com.quelque chose.SuperClass:

@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class SuperClass implements Serializable {
    private static final long serialVersionUID = -695503064509648117L;

    long confirmationCode;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO) // Causes exception!!!
    public long getConfirmationCode() {
        return confirmationCode;
    }

    public void setConfirmationCode(long confirmationCode) {
        this.confirmationCode = confirmationCode;
    }
}

com.quelque chose.Sous-classe:

@Entity
public abstract class Subclass extends SuperClass {
    private static final long serialVersionUID = 8623159397061057722L;

    String name;

    @Column(nullable = false)
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Me donne cette exception:

Caused by: org.hibernate.MappingException: Cannot use identity column key
generation with <union-subclass> mapping for: com.something.SuperClass

Quel est le moyen le meilleur et le plus pratique pour moi de générer les identifiants? Je ne veux pas changer ma stratégie de succession.

Martijn Pieters
la source

Réponses:

230

Le problème ici est que vous mélangez l'héritage «table par classe» et GenerationType.Auto. Considérez une colonne d'identité dans MsSQL. Il est basé sur des colonnes. Dans une stratégie «table par classe», vous utilisez une table par classe et chacune a un ID.

Essayer:

@GeneratedValue(strategy = GenerationType.TABLE)

zoidbeck
la source
2
Solution parfaite. Même les forums Hibernate ne semblaient pas avoir cette solution, et ils tournaient
Spring Monkey
1
Ce problème concerne-t-il MySql uniquement ou est-il régulier alors que je regarde l'une d'une vidéo pour l'approche Table par classe et que cela fonctionnait bien dans ce postgres a été utilisé
Prashant
1
J'ai rencontré cela récemment lors du test d'une application Dropwizard. Dans mon cas, je l'ai abordé en veillant à utiliser les mêmes options de configuration que celles utilisées par DW pour créer la fabrique de session. Je suis sûr que la définition de la propriété "hibernate.id.new_generator_mappings" sur true est ce qui l'a corrigée. Il s'agit de DW 0.7.0, Hibernate 4.3.1, DB était H2.
sfitts
1
Cela fonctionne parfaitement. Cependant, il est fortement recommandé de ne pas préférer l'utilisation de la stratégie de génération d'id «TABLE» car elle déclenche de nombreuses requêtes (sélection, insertion et mise à jour) et maintient des verrous pour générer des valeurs uniques pour la clé primaire. Alors, quelle serait une solution alternative à cela si nous avons un gros scénario d'héritage? Cela aura certainement un impact important sur les performances.
MAC
8

Je me demande s'il s'agit d'un problème spécifique à un dialecte de base de données, depuis que j'ai regardé un tutoriel youtube avec PostgreSQL comme base de données sous-jacente, j'ai vu que le créateur de la vidéo exécutait avec succès une application avec la valeur par défaut @GeneratedValue. Dans mon cas (la base de données sous-jacente est MySQL), j'ai dû modifier la stratégie @GeneratedValue en GenerationType.TABLE exactement comme le propose zoidbeck.

Voici la vidéo: https://www.youtube.com/watch?v=qIdM4KQOtH8

skiabox
la source
Postgres est capable de faire de l'héritage donc vous avez peut-être raison de dire que c'est spécifique à la base de données: postgresql.org/docs/8.1/static/ddl-inherit.html La vidéo n'explique pas (ou je l'ai ratée) comment le schéma est généré. Alors peut-être que le dialecte NHibernate postgres est capable de le faire seul ou à la place vous devez ajouter les 'INHERITS' manuellement. En fait, je ne peux pas dire.
zoidbeck
6
Sur PostgreSQL, Hibernate utilise par défaut GenerationType.SEQUENCE. C'est pourquoi cela fonctionne automatiquement là-bas. Cela n'a absolument rien à voir avec PostgreSQLs INHERITS.
Henning
J'ai utilisé le même didacticiel et l'utilisation de @Generated causait un problème car j'utilise MySql.Spens beaucoup de temps au débogage jusqu'à ce que je voie ce post
Deen John
5

D'accord avec la réponse de zoidbeck. Vous devez changer de stratégie pour:

@GeneratedValue(strategy = GenerationType.TABLE)

Mais ce n'est pas tout, vous devez créer une nouvelle table, ce qui contiendra la séquence de clés primaires de votre table abstraite. Modifiez votre mappage en

@Id
@GeneratedValue(strategy = GenerationType.TABLE, generator = "ConfirmationCodeGenerator")
@TableGenerator(table = "SEQUENCES", name = "ConfirmationCodeGenerator")
public long getConfirmationCode() {
   return confirmationCode;
}

Et une nouvelle table dans la base de données devrait ressembler à ceci: entrez la description de l'image ici

Lorsque vous avez exécuté votre application, Hibernate insérera une ligne où sequence_namesera le nom de l'entité ( SuperClassdans cet exemple) et la sequence_next_hi_valuevaleur sera automatiquement incrémentée et utilisée pour les nouveaux enregistrements de toutes les tables des sous-classes implémentées.

Gondy
la source
2

Dans notre cas, nous utilisons une base de données PostreSQL pour le développement et la production et une base de données hsqldb en mémoire pour les tests. Nous utilisons une séquence dans les deux cas pour générer un identifiant. Apparemment, la valeur par GenerationType.AUTOdéfaut est SEQUENCEpour postgres, mais a échoué dans nos tests locaux (doit par défaut être quelque chose d'autre pour hsqldb).

Donc, la solution qui a fonctionné pour nous, utilisez explicitement GenerationType.SEQUENCE.

Ryan Walls
la source
1

vous pouvez utiliser @MappedSuperclass pour l'héritage

leimbag
la source
0

Il existe une conformité standard SQL entre MySQL et PostgreSQL. PostgreSQL Postgres comprend un bon sous-ensemble de SQL92 / 99 ainsi que certaines fonctionnalités orientées objet de ces sous-ensembles. Postgres est capable de gérer des routines et des règles complexes sous forme de requêtes SQL déclaratives, de sous-requêtes, de vues, de prise en charge multi-utilisateur, de transactions, d'optimisation de requête, d'héritage et de tableaux. Ne prend pas en charge la sélection de données dans différentes bases de données.

MySQL MySQL utilise SQL92 comme base. Fonctionne sur d'innombrables plates-formes. Mysql peut construire des requêtes qui peuvent joindre des tables à partir de différentes bases de données. Prend en charge les jointures externes gauche et droite à l'aide de la syntaxe ANSI et ODBC. À partir de MySQL 4.1 à partir de cette version, MySQL gérera les sous-requêtes. Vues prises en charge à partir de la version 5.

Pour une description détaillée, veuillez visiter. http://www-css.fnal.gov/dsg/external/freeware/pgsql-vs-mysql.html

Coderac
la source
S'il vous plaît, ne vous sentez pas mal à ce sujet, mais je pense que la comparaison entre MySQL et PostgreSQL n'est pas pertinente pour le sujet (même si les valeurs par défaut d'Hibernate sont différentes pour eux).
mrts