No se puede usar la generación de claves de columna de identidad con <union-subclass> (TABLE_PER_CLASS)

97

com.something.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.something.SubClass:

@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 da esta excepción:

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

¿Cuál es la mejor y más conveniente forma de generar los ID? No quiero cambiar mi estrategia de herencia.

Martijn Pieters
fuente

Respuestas:

231

El problema aquí es que se mezcla la herencia "tabla por clase" y GenerationType.Auto . Considere una columna de identidad en MsSQL. Está basado en columnas. En una estrategia de "tabla por clase", usa una tabla por clase y cada una tiene una identificación.

Tratar:

@GeneratedValue(strategy = GenerationType.TABLE)

Zoidbeck
fuente
2
Solución perfecta. Incluso los foros de Hibernate no parecían tener esta solución, y estaban dando vueltas al tema forum.hibernate.org/…
Spring Monkey
1
¿Este problema es solo con MySql o es normal? Ya que veo uno de un video para el enfoque de Tabla por clase y funcionaba bien porque se usó postgres
Prashant
1
Me encontré con esto recientemente cuando probaba una aplicación Dropwizard. En mi caso, lo abordé asegurándome de usar las mismas opciones de configuración que usa DW para crear la fábrica de sesiones. Estoy bastante seguro de que establecer la propiedad "hibernate.id.new_generator_mappings" en verdadero es lo que lo solucionó. Esto es DW 0.7.0, Hibernate 4.3.1, DB era H2.
sfitts
1
Funciona perfectamente. Sin embargo, se recomienda no preferir el uso de la estrategia de generación de ID 'TABLE', ya que desencadena muchas consultas (seleccionar, insertar y actualizar) y mantiene bloqueos para generar valores únicos para la clave principal. Entonces, ¿cuál sería una solución alternativa a esto si tenemos un escenario de herencia algo grande? Sin duda tendrá un gran impacto en el rendimiento.
MAC
8

Me pregunto si este es un problema específico del dialecto de la base de datos, ya que al ver un tutorial de YouTube con PostgreSQL como base de datos subyacente, vi que el creador del video ejecutó correctamente una aplicación con el valor predeterminado @GeneratedValue. En mi caso (la base de datos subyacente es MySQL) tuve que modificar la estrategia @GeneratedValue a GenerationType.TABLE exactamente como propone zoidbeck.

Aquí está el video: https://www.youtube.com/watch?v=qIdM4KQOtH8

skiabox
fuente
Postgres puede hacer herencia, por lo que es posible que tenga razón en que esto es específico de la base de datos: postgresql.org/docs/8.1/static/ddl-inherit.html El video no explica (o me lo perdí) cómo se genera el esquema. Entonces, tal vez el dialecto postgres de NHibernate pueda hacerlo por sí solo o, en su lugar, tenga que agregar el 'HEREDA' manualmente. De hecho, no puedo decirlo.
zoidbeck
6
En PostgreSQL, Hibernate utiliza de forma predeterminada GenerationType.SEQUENCE. Por eso funciona automáticamente allí. No tiene absolutamente nada que ver con PostgreSQL INHERITS.
Henning
He estado usando el mismo tutorial y el uso de @Generated estaba causando problemas ya que estoy usando MySql. Pasé mucho tiempo depurando hasta que vi esta publicación
Deen John
5

De acuerdo con la respuesta de Zoidbeck. Necesita cambiar de estrategia para:

@GeneratedValue(strategy = GenerationType.TABLE)

Pero eso no es todo, necesita crear una nueva tabla, que contendrá la secuencia de clave primaria de la tabla de su resumen. Modifica tu mapeo para

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

Y una nueva tabla en la base de datos debería verse como sigue: ingrese la descripción de la imagen aquí

Cuando ejecutó su aplicación, Hibernate insertará una fila donde sequence_nameestará el nombre de la entidad ( SuperClassen este ejemplo) y el sequence_next_hi_valuevalor se incrementará automáticamente y se usará para los nuevos registros de todas las tablas de subclases de implementación.

Gondy
fuente
2

En nuestro caso, usamos una base de datos PostreSQL para desarrollo y producción y una base de datos hsqldb en memoria para pruebas. Estamos usando una secuencia en ambos casos para generar una identificación. AparentementeGenerationType.AUTO defecto esSEQUENCE para postgres, pero falló en nuestras pruebas locales (debe tomar otro valor predeterminado para hsqldb).

Entonces, la solución que funcionó para nosotros, usar explícitamente GenerationType.SEQUENCE.

Ryan Walls
fuente
1

puedes usar @MappedSuperclass para la herencia

leimbag
fuente
0

Existe un cumplimiento estándar de SQL entre MySQL y PostgreSQL. PostgreSQL Postgres comprende un buen subconjunto de SQL92 / 99 más algunas características orientadas a objetos para estos subconjuntos. Postgres es capaz de manejar rutinas y reglas complejas como consultas SQL declarativas, subconsultas, vistas, soporte multiusuario, transacciones, optimización de consultas, herencia y matrices. No admite la selección de datos en diferentes bases de datos.

MySQL MySQL usa SQL92 como base. Funciona en innumerables plataformas. Mysql puede construir consultas que pueden unir tablas de diferentes bases de datos. Admite uniones externas tanto a la izquierda como a la derecha utilizando sintaxis ANSI y ODBC. A partir de MySQL 4.1 a partir de esa versión, MySQL manejará las subconsultas. Vistas admitidas a partir de la versión 5.

Para obtener una descripción detallada, visite. http://www-css.fnal.gov/dsg/external/freeware/pgsql-vs-mysql.html

Coderac
fuente
Por favor, no se sienta mal por esto, pero creo que la comparación de MySQL y PostgreSQL es irrelevante para el tema (aunque los valores predeterminados de Hibernate son diferentes para ellos).
mrts