5

Usamos herencia de tabla única para cada tabla en nuestra aplicación. Esto permite que distintas instancias de la misma pila de aplicaciones funcionen con los mismos DAO, mientras que sus entidades pueden diferir ligeramente y potencialmente pueden contener información exclusiva de esa instancia. Una clase abstracta define la estructura básica mesa y una extensión define columnas adicionales, si es necesario por esa instancia:¿Puedo eliminar la columna del discriminador en una herencia de tabla única de Hibernate?

@Entity 
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) 
@Table(name = "client") 
public abstract class Client extends AbstractPersistable<Long> { 
    // ... 
} 

aplicación A:

@Entity 
public class ClientSimple extends Client { 
    private String name; 
    // getter, setter 
} 

aplicación B:

@Entity 
public class ClientAdvanced extends Client { 
    private String description; 
    // getter, setter 
} 

Ahora una DAO puede trabajar con los objetos Client para las aplicaciones A y B, pero la aplicación B puede definir información adicional para su objeto cliente que puede leerse mediante un método de gestión uniqu e para la aplicación B:

aplicación A:

Client client = new ClientSimple(); 
clientDao.save(client); 

la aplicación B:

Client client = new ClientAdvanced(); 
clientDao.save(client); 

Desafortunadamente esto significa que hay una columna DTYPE en cada mesa (o cualquier otro nombre que pudiera elegir) . ¿Hay alguna forma de librarse de esto? No lo necesitamos y está usando el espacio de DB ...

¡Gracias!


EDITAR

Importante tener en cuenta: @MappedSuperclass no va a funcionar. Estamos usando QueryDSL como nuestra capa de abstracción HQL. Esto requiere clases de tipo de consulta generadas automáticamente para el tipo de consulta de guardado. Sin embargo, estos solo se generarán correctamente si la clase abstracta está anotada con @Entity.

Ésta es neccessairy porque queremos consulta en la clase abstracta Client mientras que en verdad consultar ClientSimple de aplicación A y ClientAdvanced en la aplicación B:

Así que en cualquier aplicación que esto funcionará:

query.where(QClient.client.name.equals("something"); 

y en aplicación B que esto funcionará:

query.where(QClientSimple.client.description.equals("something else"); 

Edit2 - se reducen

Se parece reducirse a esto: ¿Puedo configurar hibernación en el momento del despliegue para establecer el tipo discriminador para una entidad inhertited a un valor fijo. Entonces, yendo con mi ejemplo a Client siempre será ClientSimple en una aplicación y ClientAdvanced en la otra para que no tenga que almacenar esa información en la base de datos?

Como dije: cada aplicación será una instancia de la pila de aplicaciones base. Cada aplicación puede definir columnas adicionales para su base de datos local, pero TODOS los objetos serán del mismo tipo para esa instancia, por lo que le garantizamos que el discriminador es siempre el mismo, lo que lo hace redundante en la base de datos y un caso de uso para la configuración de hibernación.

+0

¿Qué versión de Querydsl estás usando? –

+0

Estamos usando QueryDSL 2.2.3 pero podríamos actualizar si las versiones más recientes admiten superclases mapeadas como objetivos de consulta: así: '@MappedSuperclass @Table public abstract class Client ...' + '@Entity public class ClientSimple extends Client' == > generar tipos de consulta ... ==> consulta: 'QClient.client.name' – Pete

+1

No es compatible directamente, pero puede agregarle un ticket en GitHub. Se implementa fácilmente. –

Respuesta

1

Si nunca necesita utilizar tanto ClientSimple y ClientAdvanced en la misma aplicación se puede declarar como Client@MappedSuperclass en lugar de @Entity.

+0

Desafortunadamente eso no es posible. Estamos utilizando consulta DSL como nuestra capa de abstracción HQL. Esto requiere que las clases abstractas se anoten @Entity para que se pueda ejecutar una consulta en contra de la clase abstracta mientras se consulta la extensión. Editaré mi publicación para reflejar esta limitación. – Pete

1

En Hibernate, la tabla única por jerarquía de clases siempre necesitaría una columna de discriminador para distinguir entre las entidades, ya que todas las clases en una jerarquía se almacenan en una tabla. Aquí hay un ejemplo de Hibernate Single Table per Class Hierarchy.

Pero es posible que desee considerar un esquema de jerarquía diferente, como a continuación:

Hibernate Single Table per Subclass

Ventajas

  • El uso de esta jerarquía, no requiere cambios complejos a la base de datos esquema cuando se modifica una clase principal única.
  • Funciona bien con una jerarquía poco profunda.

Desventajas

  • Como la jerarquía crece, puede afectar negativamente al rendimiento.
  • También aumenta el número de combinaciones necesarias para construir una subclase.

Hibernate Single Table per Concrete class

Ventajas

  • Este es el método más fácil de Herencia mapeo de implementar.

Desventajas

  • eso de datos pertenece a una clase padre se dispersa a través de una serie de tablas de subclase, que representa a clases concretas.
  • Esta jerarquía no se recomienda para la mayoría de los casos.
  • Los cambios en una clase padre se refleja a gran número de tablas
  • es probable que cause una gran número de operaciones de selección
  • Una consulta formulada en términos de clase padre

yo sugeriría que tener una mira el esquema de tabla única por subclase.Aunque no estoy seguro acerca de su requisito exacto. Pero esto puede ayudar.

+0

¿Está absolutamente seguro de que no hay forma de escribir algún deserializador personalizado? No soy un experto en Hibernate, pero apuesto a que si observamos la forma en que hibernate convierte el ResultSet en objeto, hay una forma de personalizarlo de la manera que Pete quiere. –

+0

Gracias por los enlaces viralpatel, buena lectura pero desafortunadamente no ayuda. Tal vez esto no se puede hacer solo con anotaciones, como sugerencias de chico mograbi ...?! – Pete

+0

Hola, bueno, es cierto que puedes anular muchas funciones predeterminadas de marcos abiertos como Hibernate. Pero el problema con este (tabla por jerarquía) es que sus datos se almacenan en una sola tabla. Y debe haber una columna para identificar qué fila de datos pertenece a qué clase en la jerarquía. –

7

Lo sé, esta es una pregunta muy antigua, pero me encontré con este problema recientemente y esto podría ser útil para alguien.

Esto se puede hacer utilizando la anotación de Hibernate @DiscriminatorFormula. La siguiente descripción se basa en el libro Java Persistence with Hibernate, sección 5.1.3; la parte correspondiente comienza en la página del último párrafo de la página 202.

Con @DiscriminatorFormula puede proporcionar una declaración SQL que determina el valor de la discriminator al recuperar las filas correspondientes de la base de datos. En su caso, tendría que ser una cadena simple que evalúa a algún valor seleccionado arbitrariamente. Para que esto funcione, debe decidir sobre un nombre que se usaría para su entidad Client. Supongamos que selecciona 'GenericClient' como el nombre de entity. Este es el nombre que debe aparecer dentro de la anotación @Entity como el valor del atributo name. Entonces, el ejemplo completo, en su caso se vería como el siguiente.

@Entity 
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) 
@Table(name = "client") 
@DiscriminatorFormula("'GenericClient'") // *1* 
public abstract class Client extends AbstractPersistable<Long> { 
    // ... 
} 

// Application A 
@Entity 
@DiscriminatorValue("GenericClient") // *2* 
public class SimpleClient extends Client { 
    // ... 
} 


// Application B 
@Entity 
@DiscriminatorValue("GenericClient") // *3* 
public class AdvancedClient extends Client { 
    // ... 
} 

La línea que se denota por ' 1' es una parte del fragmento de SQL que siempre va a volver 'GenericClient' como su valor. El subclasses del Client siempre debe anotarse con el @DiscriminatorValue("GenericClient"). Lo que esto significa es que cuando Hibernate obtiene las filas de la base de datos, el tipo de objeto que se construirá siempre será la subclase específica de Client.

Si el paquete donde las subclases de Client reside, y el nombre de las subclases se fijan: no se requeriría

En ese caso, el @DiscriminatorValue("GenericClient") en las subclases, todo lo que se necesita que hacer es:

@Entity 
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) 
@Table(name = "client") 
@DiscriminatorFormula("'com.example.fixed.path.FixedSubClassName'") 
public abstract class Client extends AbstractPersistable<Long> { 
    // ... 
} 

Las subclases no necesitarían ninguna anotación. El discriminator-value se establece de manera predeterminada en entity-name, que de forma predeterminada tiene el nombre de clase completo.

Nota: La declaración SQL dentro @DiscriminatorFormula() puede ser cualquier SQL declaración válida para el servidor de base de datos de destino.

Cuestiones relacionadas