我有层叠保存与休眠问题,并没有太多运气跟踪源代码。对象引用一个未保存的瞬态实例 - 麻烦保存新的子/孙引用
简而言之,我有一个三级父母/子女/孙子关系映射,当我保存一个有2个孩子的父母的引用,并且每个孩子有少数孙子时,首先坚持是成功的,每个人都得到适当的ID :
Parent (id:p1)
child1 (id:parent,c1)
grandchild (id:child1,g1)
grandchild (id:child1,g2)
child2 (id:parent,c2)
grandchild (id:child2,g1)
grandchild (id:child2,g2)
如果我然后加载父(急切地),并添加一个新的孩子数它自己的新的大孩子,我得到的未保存的瞬态错误,当我试图坚持通过父对象的变化:
Parent (id:p1)
child1 (id:parent,c1)
grandchild1 (id:child1,g1)
grandchild2 (id:child1,g2)
child2 (id:parent,c2)
grandchild3 (id:child2,g1)
grandchild4 (id:child2,g2)
child3 (id:<new>)
grandchild5 (id:child3,<new>)
grandchild6 (id:child3,<new>)
这个是我用来保存对象两次的基本JPA样式语法:
rulesRepository.save(parent)。
在hibernate代码中,我可以看到代码决定父代是否是临时代码并执行相应的保存或合并方法。第一遍执行保存,第二次调用合并。
追踪hibernate代码我看到它要保存新孙子的位置,意识到它需要父项(child3),然后尝试获取子项的id字段。只是因为孩子也是新的,我得到了未保存的瞬态错误。
问题是为什么hibernate无法解析child3的key(通过检查它的父级),因为它在merge过程中解析了grandchild的id,当它明显能够做到这一点时,整个模型是瞬态的保存?
增加可能的问题是中间层(子)实际上是一个JOINED子类型。孙子对象与所有子类型相关联,因此映射到鉴别器类。
这里是我制订:
家长:
@Entity
@Transactional
@EntityListeners(AuditingEntityListener.class)
@Table(name="DS_EXTENDED_DATA_SOURCE")
public final class Rule {
@Id
@GeneratedValue(generator="IdentityProvider")
@GenericGenerator(name="IdentityProvider", strategy="com.teradata.tac.domain.common.IdentityProvider")
@Column(name="Extended_Data_Source_Id", nullable=false, length=MAX_ID_LENGTH, updatable=false, insertable=true)
private String extendedDataSourceId;
@OneToMany(cascade=CascadeType.ALL, orphanRemoval=true, fetch=FetchType.EAGER, mappedBy="rule")
@Fetch(FetchMode.SELECT)
@OrderBy("Display_X_Position_Num,Display_Y_Position_Num,Extended_Data_Source_Id")
private List<Node<?>> nodes = new ArrayList<>();
}
儿童 - 鉴别:
@Entity
@IdClass(NodeId.class)
@Table(name="DS_XDS_NODE")
@Inheritance(strategy=InheritanceType.JOINED)
@DiscriminatorColumn(name="Xds_Node_Type_Cd")
public class Node<T extends Node<T>> extends BaseDomain<Node<T>> {
@Id
@GeneratedValue(generator="IdentityProvider")
@GenericGenerator(name="IdentityProvider", strategy="com.teradata.tac.domain.common.IdentityProvider")
@Column(name="Xds_Node_Id", nullable=false, length=MAX_ID_LENGTH, updatable=false, insertable=false)
protected String xdsNodeId;
@Column(name="Xds_Node_Type_Cd", nullable=false, insertable=false, updatable=false)
protected short xdsNodeTypeCd;
@Id
@ManyToOne(fetch=FetchType.EAGER)
@JoinColumn(name="Extended_Data_Source_Id", insertable=false, updatable=false, nullable=false)
@ApiModelProperty(hidden=true)
@JsonBackReference(value="rule")
protected Rule rule;
// Selected Columns
@OneToMany(cascade=CascadeType.ALL, orphanRemoval=true, fetch=FetchType.EAGER, mappedBy="node")
@Fetch(FetchMode.SELECT)
@OrderBy("Display_Ord")
protected List<SelectedColumn> selectedColumns = new ArrayList<>();
}
例儿童亚纲:
@Entity
@Table(name="DS_XDS_JOIN_NODE")
@DiscriminatorValue("60")
public class JoinNode extends Node<JoinNode> {
}
孙:
@Entity
@IdClass(SelectedColumnId.class)
@Table(name = "DS_XDS_SELECTED_COLUMN")
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
public class SelectedColumn {
@Id
@GeneratedValue(generator = "IdentityProvider")
@GenericGenerator(name = "IdentityProvider", strategy = "com.teradata.tac.domain.common.IdentityProvider")
@Column(name = "Xds_Selected_Column_Id", nullable = false, length = MAX_ID_LENGTH, updatable = false, insertable = false)
protected String xdsSelectedColumnId;
@Id
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumns({
@JoinColumn(name = "Extended_Data_Source_Id", insertable = false, updatable = false, nullable = false),
@JoinColumn(name = "Xds_Node_Id", insertable = false, updatable = false, nullable = false) })
protected Node<?> node;
}
所有使用的IdClasses都有类似的布局:
public class NodeId {
private Rule rule; // Parent object reference
private String xdsNodeId; // local instance id (not guaranteed to be unique)
public NodeId() {}
public NodeId(Rule rule, String xdsNodeId) {
this.rule = rule;
this.xdsNodeId = xdsNodeId;
}
public String getId() {
return this.xdsNodeId;
}
public Rule getRule() {
return this.rule;
}
}
我省略了重载equals和hashCode方法但本质上它们都匹配的重点领域。
最好的我已经设法追查根本原因是这种方法:AbstractEntityPersister:4480
公共布尔canExtractIdOutOfEntity(){
回报hasIdentifierProperty()|| hasEmbeddedCompositeIdentifier()|| hasIdentifierMapper();
}
这是休眠(1.5.2.RELEASE)已确定被保存的孙子是短暂的,需要它被拉到ID从它的父(child3)。当它搜索id时,它会执行上面的函数,并且所有引用方法都返回null,从而导致调用返回false。在这一点上抛出异常
任何深入了解我可能会做错什么将不胜感激,并且一个解决方案将是非常受欢迎的。
感谢, 杰森