2012-06-15 12 views
9

Situación

Tengo una entidad con una DiscriminatorColumn, configurado para la herencia simple tabla:DiscriminatorColumn como parte de la clave primaria/Identificación del

@Entity 
@Inheritance(strategy=InheritanceType.SINGLE_TABLE) 
@DiscriminatorColumn(name="TYPE") 
public class ContainerAssignment{ 
... 
} 

'ContainerAssignment' tiene una referencia a otra entidad:

@JoinColumn(name="CONTAINER_ID") 
private Container container; 

Un contenedor puede tener un ContainerAssignment de cada TIPO. Esto significa que la clave principal de la tabla ContainerAssignment está definida por CONTAINER_ID y TYPE.

ContainerAssignment tiene algunas subclases, por ejemplo.

@Entity 
@DiscriminatorValue("SOME_TYPE") 
public class SomeTypeOfContainerAssignment extends ContainerAssignment{ 
... 
} 

Sólo habrá un solo SomeTypeOfContainerAssignment ejemplo para un determinado CONTAINER_ID.

Problema

Si defino la APP @Id tan sólo el recipiente sobre la mesa ContainerAssignment, no puedo hacer entityManager.find(SomeTypeOfContainerAssignment.class, containerId), que es grande. Esto ejecuta algo a lo largo de las líneas de SELECT * FROM CONTAINER_ASSIGNMENT WHERE CONTAINER_ID = 1 AND TYPE = 'SOME_TYPE';. Sabe que necesita la verificación TYPE aquí, debido a la anotación @DiscriminatorValue("SOME_TYPE") en la entidad.

Sin embargo, esto significa que las referencias anteriores de Container a ContainerAssignment se rompen porque Container no es realmente la clave principal. Por ejemplo, si Container tiene @OneToOne(mappedBy=container) private SomeTypeOfContainerAssignment assignment;, cuando lee en un contenedor, se leerá en la asignación por algo como SELECT * FROM CONTAINER_ASSIGNMENT WHERE CONTAINER_ID = 1;, sin la verificación de tipo. Esto le da todas las asignaciones para un contenedor, y luego elige uno aparentemente al azar, potencialmente del tipo incorrecto, en cuyo caso arroja una excepción.

Si, en cambio, defino el JPA @Id de ContainerAssignment como un identificador compuesto utilizando contenedor y tipo, las referencias a las subclases de ContainerAssignment funcionan bien. No se puede hacer entityManager.find(SomeTypeOfContainerAssignment.class, containerId), porque containerId no es la identificación Tengo que hacer entityManager.find(SomeTypeOfContainerAssignment.class, new MyPk(containerId, "SOME_TYPE")), que parece rechazar el punto de @DiscriminatorValue("SOME_TYPE"). También podría usar una única Entidad de Asignación de Contenedor si tuviera que especificar el tipo en buscar de todos modos.

Pregunta

¿Hay una manera de tener referencias de trabajo a las subclases de una entidad única herencia de tablas donde la clave principal en la tabla es compuesta en la columna discriminadora y que también sea capaz de EntityManager.find por sólo el parte (s) de la clave principal que no son el discriminador?

Respuesta

0

Si Container tiene una OneToOne bidireccional con SomeTypeOfContainerAssignment, que se extiende ContainerAssignment, entonces el campo contenedor no debe ser definida y asignada en ContainerAssignment, pero en SomeTypeOfContainerAssignment:

public class Container { 
    @Id 
    private Long id; 

    @OneToOne(mappedBy = "container") 
    private SomeTypeOfContainerAssignment someTypeOfContainerAssignment; 
} 

public class ContainerAssignment { 
    @Id 
    private Long id; 
} 

public class SomeTypeOfContainerAssignment extends ContainerAssignment { 
    @OneToOne 
    private Container container; 
} 

Si todos los tipos de tareas de contenedores tienen tal una asociación con OneToOne contenedor, puede definir el recipiente como el

public abstract class ContainerAssignment { 
    @Id 
    private Long id; 

    public abstract Container getContainer(); 
    public abstract void setContainer(Container container); 
} 

para ser honesto, no sé si está permitido el uso de la misma jo en la columna en la tabla para mapear los campos @OneToOne container de cada subclase.

Creo que esto es lo mejor que puede tener. Si coloca el campo contenedor en la clase base, debe definir la asociación como una asociación OneToMany/ManyToOne, ya que es lo que realmente es.

No creo que lo que quieras hacer sea posible, y no me equivocaría con compuestos PK, ya que están desanimados por buenas razones y son una pesadilla para usar.

+1

Gracias por su respuesta, pero realmente no resuelve la pregunta: ¿hay alguna manera de poner el 'DiscriminatorColumn' dentro del PK? Estoy de acuerdo en que una PK artificial es la mejor opción, pero en muchos sistemas no podemos controlar el esquema y tenemos que lidiar con PK compuestos. –

+0

También observe que NUNCA usar claves primarias compuestas es un antipatrón, según el libro de Bill Karwin. – atorres

0

Voy a suponer que la clave primaria compuesta de ContainerAssignment está funcionando bien (¡realmente creo que puede ser la implementación de JPA!), Y todo lo que todavía te molesta es la molesta llamada a la entidadManager.find y PK instanciación.

Mi solución es definir métodos de búsqueda independientes de la API de JPA. No te encierres en JPA. La manera más simple es simplemente definir un buscador estático en su clase de dominio (o, definir otra clase con solo buscadores, si desea mantener el dominio desacoplado hacer JPA. Excavar en IoC para saber cómo hacerlo).

En ContainerAssignment (o su clase buscador):

public static <T extends ContainerAssignment> T findByPK(EntityManager manager,Class<T> type,long id) { 
    DiscriminatorValue val = type.getAnnotation(DiscriminatorValue.class); // this is not optimal...can be cached... 
    return (T) manager.find(type, new MyPk(containerId, val.getValue())); 
} 

En su código:

SomeTypeOfContainerAssignment ca = ContainerAssignment.findByPK(entityManager,SomeTypeOfContainerAssignment.class,containerId); 

en cuenta que hacer la parte tipo de PK significa que usted puede tener dos instancias ContainerAssignment de distinta tipos con la misma identificación. Necesitará una consulta para recuperar ContainerAssignment si no conoce su tipo. Sin embargo, si su ID se genera a partir de una secuencia, puede simplemente escribir otro método de búsqueda que oculte las llamadas internas a la infraestructura de la entidad, devolviendo el primer resultado del conjunto de resultados.

Cuestiones relacionadas