2010-11-30 25 views
0

我有兩個表混合代理鍵:JPA,與外鍵和序列號

DOCUMENT 
-------- 
DOC_ID (PK) 
. 
. 
. 

SECTION 
------- 
DOC_ID (FK, PK) 
SECTION_NUM (PK) 
. 
. 
. 

數據庫中的條目可能是這樣的:

文件:

DOC_ID | . . . 
-------------- 
1  | . . . 
2  | . . . 

部分:

DOC_ID | SECTION_NUM | . . . 
--------------------------- 
1  | 1   | . . . 
1  | 2   | . . . 
1  | 3   | . . . 
2  | 1   | . . . 

Document在DOC_ID上有生成的Id,而Section在DOC_ID和SECTION_NUM上有複合主鍵。

SECTION_NUM是本地(應用程序)生成的序列號,從每個文檔開始新鮮。

我的實體類,如下所示:

@Entity 
@Table(name = "DOCUMENT") 
public class Document implements java.io.Serializable { 
    @Id 
    @Column(name = "DOC_ID", nullable = false) 
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "DocIdSeq") 
    @SequenceGenerator(name = "DocIdSeq", sequenceName = "DOC_ID_SEQ", allocationSize = 1) 
    private Long docId; 
} 


@Entity 
@Table(name = "SECTION") 
@IdClass(SectionId.class) 
public class Section implements java.io.Serializable { 
    @Id 
    @Column(name = "DOC_ID", nullable = false) 
    private Long docId; 

    @Id 
    @Column(name = "SECTION_NUM", nullable = false) 
    private Integer sectionNum; 

    @ManyToOne(fetch = FetchType.LAZY) 
    @JoinColumn(name = "DOC_ID") 
    private Document document; 
} 

public class SectionId implements java.io.Serializable { 
    private Long docId; 
    private Integer sectionNum; 
} 

當插入一個新的文檔和相關部門,我做到以下幾點:

Document doc = new Document(); 

Section section = new Section(); 
section.setDocument(doc); 
section.setSectionNum(1); 

entityManager.persist(doc); 

當堅持我得到一個異常,說明空不允許列SECTION_NUM。 我使用OpenEJB(依靠OpenJPA在幕後進行單元測試),並在逐步完成OpenJPA代碼時發現它成功保留了Document對象,但是當涉及到Section對象時,它會反射地創建一個新實例並設置所有字段都爲空,因此在將它鏈接到Document對象之前保持較早的狀態之前丟失了sectionNum值。

不幸的是我無法更改數據庫模式,因爲它是一個遺留系統。 有沒有人做過類似的事情,並得到它的工作?

回答

0

我一直更新了一段時間,但一直太忙......

好了,事實證明,這是不是真的有可能使用JPA。 但是,有一種解決方法。

以前我提到過,Document類看起來像這樣。

@Entity 
@Table(name = "DOCUMENT") 
public class Document implements java.io.Serializable { 
    @Id 
    @Column(name = "DOC_ID", nullable = false) 
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = 
    "DocIdSeq") 
    @SequenceGenerator(name = "DocIdSeq", sequenceName = "DOC_ID_SEQ", allocationSize = 1) 
    private Long docId; 
} 

這只是一個縮短版本,以澄清問題。 真正的類有節的集合太:

@Entity 
@Table(name = "DOCUMENT") 
public class Document implements java.io.Serializable { 
    @Id 
    @Column(name = "DOC_ID", nullable = false) 
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = 
"DocIdSeq") 
    @SequenceGenerator(name = "DocIdSeq", sequenceName = "DOC_ID_SEQ", allocationSize = 1) 
    private Long docId; 

    @OneToMany 
    private Set<Section> sections = new HashSet<Section>(0); 
} 

中頻部分有一個簡單的主鍵,JPA將很容易處理的關係,因爲它會從應用程序接受一個I​​D,或者從一個序列生成它,但它不會同時使用一個ID。

所以,解決的辦法是自己管理的關係,並添加一個生命週期函數:

@Entity 
@Table(name = "DOCUMENT") 
public class Document implements java.io.Serializable { 
    @Id 
    @Column(name = "DOC_ID", nullable = false) 
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = 
    "DocIdSeq") 
    @SequenceGenerator(name = "DocIdSeq", sequenceName = "DOC_ID_SEQ", allocationSize = 1) 
    private Long docId; 

    @Transient 
    private Set<Section> sections = new HashSet<Section>(0); 

    @PostPersist 
    public void updateChildIds() { 
     for (Section section : this.sections) { 
      section.getId().setDocId(this.docId); 
     } 
    } 
} 

正如你可以看到,該科的關係現在是暫時的,這意味着JPA不會對其進行管理。 持久化文檔後,框架將調用updateChildIds函數,在該函數中您使用新保留的文檔ID手動更新Section ID。

這可以在下面的門面證明:

@Stateless 
public void DocumentFacade implements DocumentFacadeLocal { 

    @PersistenceContext 
    private EntityManager entityManager; 

    public void save(Document entity) throws Exception { 
     this.entityManager.persist(entity); 
     this.entityManager.flush(); 
     this.persistTransientEntities(entity); 
     this.entityManager.flush(); 
    } 

    private void persistTransientEntities(CaseInstructionSheet entity) { 
     for (Section section : entity.getSections()) { 
      this.entityManager.persist(section); 
     } 
    } 
} 
0

其實,JPA是完全能夠處理這個問題。您正在查找的註釋是MapsId

在你的情況,你Section,在docId你只需要添加以下內容:

@MapsId("docId") 

MapsId標註的值是您的複合主鍵的屬性名(在這種情況下,是一樣的)

+0

你認爲@MapsId可能對我有用嗎? https://stackoverflow.com/questions/46159867/與原來的海報的主要區別是使用OneToOne而不是ManyToOne。 – gouessej 2017-09-14 08:38:10