java - 带有继承的 JPA 映射树结构

标签 java jpa inheritance tree eclipselink

我正在尝试在 JPA 中实现树结构,我想使用 EclipseLink 将其映射到 H2 数据库。树的节点可能是基节点类的子类。发生的事情是,EL 正在为 children 创建一个脑死亡链接表,如下所示:

[EL Fine]: sql: 2015-04-10 13:26:08.266--ServerSession(667346055)--Connection(873610597)--CREATE TABLE ORGANIZATIONNODE_ORGANIZATIONNODE (OrganizationNode_IDSTRING VARCHAR NOT NULL, children_IDSTRING VARCHAR NOT NULL, Device_IDSTRING VARCHAR NOT NULL, monitors_IDSTRING VARCHAR NOT NULL, PRIMARY KEY (OrganizationNode_IDSTRING, children_IDSTRING, Device_IDSTRING, monitors_IDSTRING))

OrganizationNode 是 Device 的正确父类(super class)。这两个都是@Entity,OrganizationNode扩展了AbstractEntity,它是一个@MappedSuperclass,其中定义了@Id(它是一个字符串)。更奇怪的是,虽然有一个 Monitor 类不在树结构中,但唯一出现“monitors”复数的地方是作为 Device 的字段......什么?

现在,使用这样的表来实现树结构很好,但我不希望每个子类都有一个具有单独的 Id 字段实例的复合主键!这必须打破 - 因为有些 child 不是设备,因此没有“Device_IDSTRING”,果然:

Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.5.0.v20130507-3faac2b): org.eclipse.persistence.exceptions.DatabaseException|Internal Exception: org.h2.jdbc.JdbcSQLException: NULL not allowed for column "DEVICE_IDSTRING"; SQL statement:|INSERT INTO ORGANIZATIONNODE_ORGANIZATIONNODE (children_IDSTRING, OrganizationNode_IDSTRING) VALUES (?, ?) [23502-186]|Error Code: 23502|Call: INSERT INTO ORGANIZATIONNODE_ORGANIZATIONNODE (children_IDSTRING, OrganizationNode_IDSTRING) VALUES (?, ?)|?bind => [2 parameters bound]|Query: DataModifyQuery(name="children" sql="INSERT INTO ORGANIZATIONNODE_ORGANIZATIONNODE (children_IDSTRING, OrganizationNode_IDSTRING) VALUES (?, ?)")

这看起来确实是很奇怪的行为。我已经尝试了我能想到的所有映射注释组合来修复它。有什么想法吗?

接下来是类(class)。

AbstractEntity.java:

@MappedSuperclass
public abstract class AbstractEntity {
// @Converter(name="uuidConverter",converterClass=UUIDConverter.class)
transient UUID id = null;
@Id String idString;

static long sequence = 1;

static long GREGORIAN_EPOCH_OFFSET = 12219292800000L;

public AbstractEntity() {
    ThreadContext tctx = ThreadContext.getThreadContext();
    long msb = tctx.getNodeID();
    long lsb = (System.currentTimeMillis()+GREGORIAN_EPOCH_OFFSET) * 1000 + ((sequence++) % 1000);
    lsb = (lsb & 0xCFFFFFFFFFFFFFFFL) | (0x8000000000000000L);  
    msb = (msb & 0xFFFFFFFFFFFF0FFFL) | (0x0000000000001000L);
    id = new UUID(msb,lsb);
    idString = id.toString();
}

@Id
public UUID getUUID() {
    return id;
}

public String getIdString() {
    return idString;
}

public void setIdString(String idString) {
    this.idString = idString;
    this.id = UUID.fromString(idString);
}

void setUUID(UUID id) {
    this.id = id;
    this.idString = id.toString();
}

@Override
public String toString() {
    return "["+this.getClass().getCanonicalName()+" "+this.getUUID()+"]";
}
}

组织节点.java:

@Entity
public class OrganizationNode extends AbstractEntity {

@ManyToOne(cascade=CascadeType.ALL)
NodeType nodeType;

@Column(nullable=true)
String name;

@OneToMany(cascade=CascadeType.ALL)
Set<OrganizationNode> children;

public OrganizationNode() {}

public OrganizationNode(NodeType nt, String name) {
    this.nodeType = nt;
    this.name = name;
    children = new HashSet<>();
}

public void setNodeType(NodeType nt) {
    nodeType = nt;
}

public NodeType getNodeType() {
   return nodeType;
}

public String getName() { 
    if ((name == null) || (name.equals(""))) return null;
    return name;
} 

public void setName(String name) { 
    this.name = name; 
}

public Set<OrganizationNode> getChildren() {
    return children;
}

public void setChildren(Set<OrganizationNode> children) {
    this.children = children;
} 

public void addNode(OrganizationNode node) {
    children.add(node);
}

public void removeNode(OrganizationNode node) {
    children.remove(node);
}
}

设备.java:

@Entity
public class Device extends OrganizationNode {

Set<Monitor> monitors;

public Device() {
   super();
}

public Device(NodeType nt, String name) {
    super(nt, name);
    monitors = new HashSet<>();
}

public Set<Monitor> getMonitors() {
    return monitors;
}

public void setMonitors(Set<Monitor> monitors) {
    this.monitors = monitors;
}

public void addMonitor(Monitor monitor) {
    monitors.add(monitor);
}

}

最佳答案

您需要决定要使用哪种继承策略。 默认的通常是“单表继承”,因此所有子类都在一个具有合并列的表中表示。

@Inheritance
@Entity
public class OrganizationNode extends AbstractEntity {
...
}

你看到了它的sql。

您可以拥有连接的多表继承,其中每个子类都有自己的表并与父表连接:

@Inheritance(strategy=InheritanceType.JOINED)

最后一个选项是“每个类继承表”,其中表结构中没有反射(reflect)“继承”树,并且每个对象都有其完整表,其中包含来自类和父类(super class)的所有列。

@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)

最后一个效率最低。

  • 您可以只有一个策略,该策略是在继承 (OrganizationNode) 的顶部定义的,不能在子类中更改。

  • 默认的单表继承通常是最有效的,除非确实有很多列在类之间不共享

  • 您可能应该显式声明将用于破坏实际类类型的列:@DiscriminatorColumn(name="NODE_TYPE") 并为每个实体定义值: @DiscriminatorValue("TYPE1")

关于java - 带有继承的 JPA 映射树结构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29567589/

相关文章:

java - 如何将 @Query 保留在属性文件中并在 JPA 中访问这些查询

c++ - 当我收到 `slicing` 的案例时?

C# 不能覆盖继承的成员

scala - 为什么在 scala 中不能覆盖可变变量?

java - Hibernate锁导致脏收集异常

java - JBOSS V/s apache tomcat

java - 更新 JDialog 时焦点丢失

java - 将裁剪后的照片保存到 FirebaseStorage 时应用崩溃

java - 来自 JavaSE 和 JPA 的 DBCP(数据库连接池)

java - 传播级联删除引发外键约束失败