我正在尝试在 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/