DiscriminatorColumn作为主键/ id的一部分

时间:2012-06-15 11:17:22

标签: jpa orm composite-primary-key discriminator

情况

我有一个DiscriminatorColumn的实体,配置为单表继承:

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

'ContainerAssignment'引用了另一个实体:

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

容器可能每个TYPE有一个ContainerAssignment。这意味着ContainerAssignment表的主键由CONTAINER_IDTYPE定义。

ContainerAssignment有一些子类,例如

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

对于给定的SomeTypeOfContainerAssignment,只会有一个CONTAINER_ID个实例。

问题

如果我将JPA @Id定义为ContainerAssignment表上的Container,我可以entityManager.find(SomeTypeOfContainerAssignment.class, containerId),这很棒。这有点像SELECT * FROM CONTAINER_ASSIGNMENT WHERE CONTAINER_ID = 1 AND TYPE = 'SOME_TYPE';那样。它知道它需要在这里进行TYPE检查,因为实体上有@DiscriminatorValue("SOME_TYPE")注释。

但是,这意味着从Container到ContainerAssignment的后引用中断,因为Container实际上不是主键。例如,如果Container有@OneToOne(mappedBy=container) private SomeTypeOfContainerAssignment assignment;,当您在容器中读取时,它将通过SELECT * FROM CONTAINER_ASSIGNMENT WHERE CONTAINER_ID = 1;之类的内容读取赋值,而不进行类型检查。这给了它一个容器的所有赋值,然后它看起来像一个看似随机的,可能是错误的类型,在这种情况下,它抛出一个异常。

相反,我使用容器和类型将ContainerAssignment的JPA @Id定义为复合id,对ContainerAssignment的子类的引用工作正常。

但是,我不能entityManager.find(SomeTypeOfContainerAssignment.class, containerId),因为containerId不是id。我必须做entityManager.find(SomeTypeOfContainerAssignment.class, new MyPk(containerId, "SOME_TYPE")),这似乎打败了@DiscriminatorValue("SOME_TYPE")。如果我必须在find上指定类型,我可能只使用一个ContainerAssignment实体。

问题

是否有办法对单个表继承实体的子类进行工作引用,其中表上的主键在鉴别器列上是复合的,同时也只能由EntityManager.find部分组成( s)主要密钥不是鉴别器?

3 个答案:

答案 0 :(得分:0)

如果Container具有SomeTypeOfContainerAssignment的双向OneToOne,其扩展ContainerAssignment,则不应在ContainerAssignment中定义和映射容器字段,而是{{1} }}:

SomeTypeOfContainerAssignment

如果所有类型的容器分配都与COntainer具有此类OneToOne关联,则可以将Container定义为

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;
}

说实话,我不知道你是否允许在表格中使用相同的连接列来映射每个子类的public abstract class ContainerAssignment { @Id private Long id; public abstract Container getContainer(); public abstract void setContainer(Container container); } 字段。

我认为这是你能做到的最好的。如果将容器字段放在基类中,则必须将关联定义为OneToMany / ManyToOne关联,因为它实际上就是它。

我不认为你想要做什么是可能的,我不会搞复杂的PK,因为他们因为充分的理由而气馁,并且使用噩梦。

答案 1 :(得分:0)

我将假设ContainerAssignment的复合主键工作正常(我真的认为它可能依赖于JPA实现!),所有仍然困扰你的是对entityManager.find和PK实例化的烦人调用

我的解决方案是定义独立于JPA API的finder方法。不要把自己锁在JPA上。 最简单的方法是在您的域类中定义一个静态查找器(或者,如果您想保持域解耦,请使用查找器定义另一个类做JPA。在IoC上挖掘以了解如何执行此操作。)

在ContainerAssignment(或您的finder类):

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()));
}

在你的代码中:

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

请注意,将类型作为PK的一部分意味着您可以拥有两个具有相同ID的不同类型的ContainerAssignment实例。如果你不知道它的类型,你将需要一个Query来检索ContainerAssignment。但是,如果您的id是从序列生成的,那么您可以编写另一个查找实体框架内部调用的finder方法,返回结果集的第一个结果。

答案 2 :(得分:0)

如果您对提供程序特定的扩展没问题,则Hibernate提供注释 @DiscriminatorOptions

它帮助我解决了鉴别符列是复合主键的一部分的问题。