Hibernate 对象引用未保存的临时实例-保存新的子/孙引用时出现问题
我在hibernate中遇到了级联保存问题,并且在跟踪源代码时运气不太好 简言之,我映射了一个三级父/子/孙关系,当我保存一个对一个有两个子的父/子/孙关系的引用,并且每个子/子都有几个孙子时,第一次持久化成功,每个人都会获得相应的ID:Hibernate 对象引用未保存的临时实例-保存新的子/孙引用时出现问题,hibernate,jpa,save,cascade,transient,Hibernate,Jpa,Save,Cascade,Transient,我在hibernate中遇到了级联保存问题,并且在跟踪源代码时运气不太好 简言之,我映射了一个三级父/子/孙关系,当我保存一个对一个有两个子的父/子/孙关系的引用,并且每个子/子都有几个孙子时,第一次持久化成功,每个人都会获得相应的ID: Parent (id:p1) child1 (id:parent,c1) grandchild (id:child1,g1) grandchild (id:child1,g2) child2 (id:parent
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>)
我省略了重载的equals和hashcode方法,但实际上它们都匹配键字段
我所能找到的最好的根本原因是这个方法:AbstractEntityPersister:4480
公共布尔值CanExtractDoutofEntity(){返回hasIdentifierProperty()| | hasEmbeddedCompositeIdentifier()| | hasIdentifierMapper();
} 这就是hibernate(1.5.2.RELEASE)确定要保存的孙子是暂时的,需要从其父(child3)中提取它的id的地方。在搜索id时,它执行上面的函数,所有引用方法返回null,导致调用返回false。此时将抛出异常 如果能洞察到我可能做错了什么,我们将不胜感激,而且我们非常欢迎找到解决方案 谢谢,
Jason为了其他人的利益,我将用我如何解决这个问题来回答我自己的问题 基本问题是,我依赖@ManyToOne映射关系将父ID提供给子对象。这样做并不是直接将父id字段映射到子id字段 虽然这可能适用于两级层次结构,但它的意思似乎是,低于此层次结构的任何后续级别都会在第二级子级中看到对父id的依赖,但看不到要映射到的显式列。我觉得这是一个bug,但我对自己的理解不够自信,无法做出这样的假设 无论如何,为了解决这个问题,我执行了以下三个更改: 1) 我将所有parent@Id列直接映射到每个子级(每个级别随后添加了越来越多的键)。仅复制了字段和@Column方面 2) 每个子项中的@ManyToOne父项映射未标记为ID 3) 我为每个子对象添加了@PrePersist映射,以从@ManyToOne映射的父对象中提取父id,并将值复制到映射的字段中 一直以来,我觉得我需要显式地映射列,但不知道如何从父母那里获取ID。只有当我把二和二放在一起时,我才意识到一个非id@ManyToOne映射和一个@PrePersist组合可以实现这一点 我怀疑@MapsId可能与@PrePersist代码块中的逻辑具有相同的影响,因此必须进行实验,但目前。。。它起作用了 希望有一天这能帮助别人
@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;
}
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;
}
}