我有層疊保存與休眠問題,並沒有太多運氣跟蹤源代碼。對象引用一個未保存的瞬態實例 - 麻煩保存新的子/孫引用
簡而言之,我有一個三級父母/子女/孫子關係映射,當我保存一個有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。在這一點上拋出異常
任何深入瞭解我可能會做錯什麼將不勝感激,並且一個解決方案將是非常受歡迎的。
感謝, 傑森