Jpa 作为主键/id一部分的鉴别器列 处境
我有一个带有Jpa 作为主键/id一部分的鉴别器列 处境,jpa,orm,composite-primary-key,discriminator,Jpa,Orm,Composite Primary Key,Discriminator,我有一个带有DiscriminatorColumn的实体,配置为单表继承: @Entity @Inheritance(strategy=InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name="TYPE") public class ContainerAssignment{ ... } “ContainerAsignment”引用了另一个实体: @JoinColumn(name="CONTAINER_ID&q
DiscriminatorColumn
的实体,配置为单表继承:
@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="TYPE")
public class ContainerAssignment{
...
}
“ContainerAsignment”引用了另一个实体:
@JoinColumn(name="CONTAINER_ID")
private Container container;
每种类型的容器都可以有一个containerSignature
。这意味着containerSignment
表的主键由CONTAINER\u ID
和类型定义
ContainerAssignment
有一些子类,例如
@Entity
@DiscriminatorValue("SOME_TYPE")
public class SomeTypeOfContainerAssignment extends ContainerAssignment{
...
}
对于给定的容器ID
,只有一个SomeTypeOfContainerAssignment
实例
问题
如果我将JPA@Id
定义为ContainerAssignment表上的容器,那么我可以执行entityManager.find(SomeTypeOfContainerAssignment.class,containerId)
,这很好。这将沿着SELECT*fromcontainer\u赋值运行,其中CONTAINER\u ID=1,TYPE='SOME\u TYPE'代码>。它知道它需要在这里签入类型,因为实体上有@DiscriminatorValue(“SOME_TYPE”)
注释
然而,这意味着从容器到容器的反向引用会中断,因为容器实际上不是主键。例如,如果容器有一个@OneToOne(mappedBy=Container)私有SomeTypeOfContainerAssignment代码>,当您在容器中读取时,它将通过类似以下内容在分配中读取:SELECT*FROM container\u assignment,其中container\u ID=1代码>,不进行类型检查。这为它提供了一个容器的所有分配,然后它随机选择一个,可能是错误的类型,在这种情况下,它抛出一个异常
相反,如果我使用container和type将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
如果容器
有一个带有SomeTypeOfContainerAssignment
的双向OneTONE,该OneTONE扩展了ContainerAssignment
,则不应在ContainerAssignment
中定义和映射容器字段,但在某种类型的容器分配中
:
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;
}
如果所有类型的容器指定都与容器有这样的OneToOne关联,则可以将容器定义为
public abstract class ContainerAssignment {
@Id
private Long id;
public abstract Container getContainer();
public abstract void setContainer(Container container);
}
老实说,我不知道是否允许您在表中使用相同的联接列来映射每个子类的@OneToOne container
字段
我想这是你能得到的最好的了。如果将container字段放在基类中,那么必须将该关联定义为OneToMany/manytone关联,因为它实际上就是这样
我不认为你想做什么是可能的,我也不会搞砸复合PK,因为它们被劝阻是有充分理由的,而且使用起来是一场噩梦。我将假设ContainerSignation的复合主键工作正常(我真的认为它可能依赖于JPA实现!),所有仍然困扰您的是对entityManager.find和PK实例化的恼人调用
我的解决方案是定义独立于jpaapi的finder方法。不要把自己锁定在JPA。
最简单的方法是在您的域类中定义一个静态查找器(或者,如果您想保持域不耦合,请定义另一个只包含查找器的类,然后进行JPA.Dig以了解如何做到这一点)
在ContainerSignment(或您的finder类):
请注意,使类型成为PK的一部分意味着您可以有两个具有相同id的不同类型的ContainerAssignment实例。如果您不知道ContainerAssignment的类型,则需要一个查询来检索ContainerAssignment。但是,如果您的id是从序列生成的,您可以编写另一个finder方法来隐藏对entity framework的内部调用,返回resultset的第一个结果。如果您同意特定于提供程序的扩展,Hibernate会提供注释@DiscriminatorOptions
它帮助我解决了一个问题,即鉴别器列是复合主键的一部分。谢谢你的回答,但它并没有真正解决问题-有没有办法将鉴别器列
放在PK中?我同意人工PK是最佳选择,但在许多系统中,我们无法控制模式,必须处理复合PK。此外,根据Bill Karwin的书,请注意,从不使用复合主键是一种反模式。
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);