具有继承性的 JPA 映射树结构
JPA mapped tree structure with inheritance
我正在尝试在 JPA 中实现树结构,我想使用 EclipseLink 将其映射到 H2 数据库。树的节点可能是基节点 class 的子class。发生的事情是 EL 正在为 children 创建一个 brain-dead link table,如下所示:
[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 的正确上级class。这两个都是@Entity,OrganizationNode extends AbstractEntity,这是一个@MappedSuperclass,其中定义了@Id(它是一个字符串)。更奇怪的是,虽然有一个 Monitor class 不在树结构中,但 "monitors" 复数出现的唯一地方是作为 Device 的字段......什么?
现在,使用这样的 table 来实现树结构很好,但我不希望复合主键与每个子 class 的 Id 字段的单独实例!那必须打破 - 因为一些 children 不是设备,因此没有 "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 (?, ?)")
这看起来确实很奇怪。我已经尝试了我可能想到的所有映射注释组合来修复它。有什么想法吗?
类关注
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()+"]";
}
}
OrganizationNode.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);
}
}
Device.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);
}
}
您需要决定要使用的继承策略。
默认值通常是 "Single Table Inheritance",因此所有子 class 都表示在一个 table 中,并带有合并列。
@Inheritance
@Entity
public class OrganizationNode extends AbstractEntity {
...
}
你看到了 sql。
您可以加入多重 Table 继承,其中每个子 class 都有自己的 table 并与父 table:
相连
@Inheritance(strategy=InheritanceType.JOINED)
最后,最后一个选项是Table Per Class Inheritance,其中没有"inheritance"树反映在tables结构中,每个对象都有它的完整 table 包含 class 和 supperclasses.
中的所有列
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
最后一个效率最低。
你可以有只有一个策略,你定义在继承(OrganizationNode)的顶部,它不能在子classes.
默认的单一 table 继承通常是最有效的,除非确实有很多列在 classes
[ 之间不共享。 =37=]
您可能应该显式声明将用于破坏实际 class 类型的列:@DiscriminatorColumn(name="NODE_TYPE")
并为每个实体定义值:@DiscriminatorValue("TYPE1")
我正在尝试在 JPA 中实现树结构,我想使用 EclipseLink 将其映射到 H2 数据库。树的节点可能是基节点 class 的子class。发生的事情是 EL 正在为 children 创建一个 brain-dead link table,如下所示:
[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 的正确上级class。这两个都是@Entity,OrganizationNode extends AbstractEntity,这是一个@MappedSuperclass,其中定义了@Id(它是一个字符串)。更奇怪的是,虽然有一个 Monitor class 不在树结构中,但 "monitors" 复数出现的唯一地方是作为 Device 的字段......什么?
现在,使用这样的 table 来实现树结构很好,但我不希望复合主键与每个子 class 的 Id 字段的单独实例!那必须打破 - 因为一些 children 不是设备,因此没有 "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 (?, ?)")
这看起来确实很奇怪。我已经尝试了我可能想到的所有映射注释组合来修复它。有什么想法吗?
类关注
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()+"]";
}
}
OrganizationNode.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);
}
}
Device.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);
}
}
您需要决定要使用的继承策略。 默认值通常是 "Single Table Inheritance",因此所有子 class 都表示在一个 table 中,并带有合并列。
@Inheritance
@Entity
public class OrganizationNode extends AbstractEntity {
...
}
你看到了 sql。
您可以加入多重 Table 继承,其中每个子 class 都有自己的 table 并与父 table:
相连@Inheritance(strategy=InheritanceType.JOINED)
最后,最后一个选项是Table Per Class Inheritance,其中没有"inheritance"树反映在tables结构中,每个对象都有它的完整 table 包含 class 和 supperclasses.
中的所有列@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
最后一个效率最低。
你可以有只有一个策略,你定义在继承(OrganizationNode)的顶部,它不能在子classes.
默认的单一 table 继承通常是最有效的,除非确实有很多列在 classes
[ 之间不共享。 =37=]您可能应该显式声明将用于破坏实际 class 类型的列:
@DiscriminatorColumn(name="NODE_TYPE")
并为每个实体定义值:@DiscriminatorValue("TYPE1")