2017-06-02 56 views
0

我有層疊保存與休眠問題,並沒有太多運氣跟蹤源代碼。對象引用一個未保存的瞬態實例 - 麻煩保存新的子/孫引用

簡而言之,我有一個三級父母/子女/孫子關係映射,當我保存一個有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。在這一點上拋出異常

任何深入瞭解我可能會做錯什麼將不勝感激,並且一個解決方案將是非常受歡迎的。

感謝, 傑森

回答

1

爲他人謀取利益,我會回答我自己的我是如何設法解決這個問題的問題。

基本的問題是,我依靠@ManyToOne映射關係將父標識提供給子對象。在這樣做的時候,我並沒有直接將父母身份字段映射到孩子身上。

儘管這可能適用於兩級別層次結構,但它表示的任何後續級別都會在第二級別子級上看到對父級ID的依賴關係,但沒有看到要映射到的明確列。我覺得這是一個錯誤,但是我沒有足夠的信心去理解我的想法。

總之,要解決問題,我進行了以下三個轉變:

1)我映射所有父@Id列直接進入每個孩子(每一級隨後添加越來越多的鍵)。只有字段和@Column方面被複制。

2)在每個子的@ManyToOne父映射未標記爲ID的

3)I加入@PrePersist映射到每個子拉從@ManyToOne映射父對象的父ID(一個或多個)和將這些值複製到映射字段中。

一直以來,我覺得我需要明確地映射列,但不知道如何從父母填充的ID。只有當我將兩個和兩個放在一起時,我才意識到,與@PrePersist結合使用的非id @ManyToOne映射可以實現此目的。

我懷疑@MapsId可能與@PrePersist代碼塊中的邏輯具有相同的影響,因此將不得不進行試驗,但現在...它工作。

希望這有助於某人某一天...

相關問題