2014-06-11 43 views
0

的名單上有在Java中2 POJO類:短語和標籤,在許多一對多的關係:JPA許多一對多的關係,保持IDS

  1. Phrase.java

    @Entity 
    @EntityListeners(value={PhraseListener.class}) 
    public class Phrase { 
        @Id 
        @GeneratedValue 
        @Column(name="id") 
        private Long phraseId; 
        @Column(nullable=false) 
        private String text; 
        @ManyToMany(cascade=CascadeType.ALL) 
        @JoinTable(name="phrase_has_tag", 
          joinColumns={@JoinColumn(name="phrase_id",referencedColumnName="id")}, 
          inverseJoinColumns={@JoinColumn(name="tag_uname",referencedColumnName="uname")}) 
        private Collection<Tag> tagObjects; 
        @Transient 
        private Set<String> tags; 
    
        public Phrase() { 
        tagObjects = new ArrayList<Tag>(); 
        tags = new HashSet<String>(); 
        } 
    
        // getters and setters 
        // … 
    
        public void addTagObject(Tag t) { 
        if (!getTagObjects().contains(t)) { 
         getTagObjects().add(t); 
        } 
        if (!t.getPhrases().contains(this)) { 
         t.getPhrases().add(this); 
        } 
        } 
    
        public void addTag(String tagName) {  
        if (!getTags().contains(tagName)) { 
         getTags().add(tagName); 
        }  
        } 
    
  2. Tag.java

    @Entity 
    public class Tag { 
        @Id 
        @Column(name="uname") 
        private String uniqueName; 
        private String description; 
        @ManyToMany(mappedBy="tagObjects") 
        private Collection<Phrase> phrases; 
    
        public Tag() { 
        phrases = new ArrayList<Phrase>(); 
        } 
    
        // getters and setters 
        // … 
    

標記實體的主鍵是它的名字。我想保留Phrase.java一個Set與多對多關係的tagObjects字段「同步」的標籤名稱,反之亦然。這樣做,我添加一個偵聽Phrase.java

public class PhraseListener { 
    @PostLoad 
    public void postLoad(Phrase p) { 
    System.out.println("In post load"); 
    for (Tag tag : p.getTagObjects()) { 
     p.addTag(tag.getUniqueName()); 
    }   
    } 

    @PrePersist 
    public void prePersist(Phrase p) { 
    System.out.println("In pre persist"); 
    EntityManagerFactory emf = Persistence.createEntityManagerFactory("TestJPA"); 
    EntityManager em = emf.createEntityManager(); 
    for (String tagName : p.getTags()) { 
     Tag t = em.find(Tag.class, tagName); 
     if (t == null) t = new Tag(tagName); 
     p.addTagObject(t); 
    } 
    } 
} 

其加載後,會創建一組來自標籤的物體標記名稱,並堅持它讀取組標籤名稱之前,並獲取或創建標籤對象。

我的問題是,如果我嘗試創建多個共享標記的短語,JPA不是僅創建關係(插入連接表),而是創建違反主鍵約束的標記對象。

transaction.begin(); 
Phrase p = new Phrase("Never ask what sort of computer a guy drives. If he's a Mac user, he'll tell you. If not, why embarrass him?", "Tom Clancy"); 
p.addTag("apple"); 
p.addTag("macintosh"); 
em.persist(p); 
transaction.commit(); 

transaction.begin(); 
p = new Phrase("It's better to be a pirate than to join the Navy.", "Steve Jobs"); 
p.addTag("apple"); 
em.persist(p); 
transaction.commit(); 

異常在線程 「主」 javax.persistence.RollbackException:異常[的EclipseLink-4002](Eclipse持久服務 - 2.5.0.v20130507-3faac2b):org.eclipse.persistence.exceptions.DatabaseException 內部異常:java.sql.SQLException:[SQLITE_CONSTRAINT]由於違反約束而中止(列uname不是唯一的) 錯誤代碼:0 調用:插入到標記(uname,DESCRIPTION)VALUES(?,?) bind = > [apple,null]

+0

您可以查看是否JPA按預期工作?我有直覺認爲即使元素在數據庫中,em.find(Tag.class,tagName)也會返回null。你可以調試嗎? –

回答

0

您還沒有顯示在你的短語方法addTag,但我假設你有地方在那裏表達new Tag()和它看起來在某種程度上與此類似:

public void addTag(String tagName) { 
    Tag tag = new Tag(); 
    tag.setUniqueName(tagName); 
    tag.getPhrases().add(this); 
    this.tagObjects.add(tag); 
} 

在這種情況下,方法addTag將創建新的對象類型Tag每次調用該方法時,這將導致關係表中的條目不同,導致Hibernate保留整個對象,而不僅僅是他們的特定字段,而不管這些字段是否是主鍵。 兩次調用方法addTag後,您將創建兩個不同的對象,而Hibernate無法知道這兩個對象是否與DB中的相同條目相關。這意味着即使它們具有相同的uniqueName,它們也可以有不同的描述。

試想以下情形:

transaction.begin(); 
Phrase p = new Phrase("Never ask what sort of computer a guy drives. If he's a Mac user, he'll tell you. If not, why embarrass him?", "Tom Clancy"); 
Tag t = new Tag(); 
t.setUniqueName("apple"); 
t.setDescription("This is an example apple"); 
p.getTagObjects().add(t); 
em.persist(p); 
transaction.commit(); 

transaction.begin(); 
p = new Phrase("It's better to be a pirate than to join the Navy.", "Steve Jobs"); 
t = new Tag(); 
t.setUniqueName("apple"); 
t.setDescription("Another description of the apple"); 
em.persist(p); 
transaction.commit(); 

有了這個例子中的差異應該是比較明顯的,應該說明爲什麼它是不可能休眠時,你指的是在DB同一條目有兩個或知道更多不同的對象。

作爲一個解決方案,我建議你改變方法addTag,使其具有以下簽名public void addTag(Tag tag) {...和跟蹤現有標記的地方集中,或者你可以嘗試em.merge(p);代替em.persist(p);

+0

用兩種方法addTag/addTagObject更新。我只在顯示的監聽器中調用'new Tag()'。因此,首先我檢查db上是否已經存在一個標籤,否則就會創建一個新的標籤。 – matax