2012-11-05 14 views
6

La mia domanda è abbastanza simile a questo Hibernate Bi-Directional ManyToMany Updates with Second Level cacheHibernate bidirezionale molti a molti associazione crea duplicati

ho classe come illustrato di seguito

@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL) 
@Entity 
public class A{ 
    private int id; 
    private List<B> listB; 

    ... 
    @Cache (usage = CacheConcurrencyStrategy.TRANSACTIONAL) 
    @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, targetEntity = B.class) 
    @JoinTable(name = "A_B", joinColumns = { @JoinColumn(name = "a_id") }, 
     inverseJoinColumns = { @JoinColumn(name = "b_id") }) 
    public List<B> getListB() { 
     return listB ; 
    } 
} 

@Cache (usage = CacheConcurrencyStrategy.TRANSACTIONAL) 
@Entity 
public class B{ 
    private int id; 
    private List<A> listA; 

    @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, targetEntity = A.class) 
    @JoinTable(name = "A_B", joinColumns = { @JoinColumn(name = "b_id") }, inverseJoinColumns = { @JoinColumn(name = "a_id") }) 
    public List<a> getListA() { 
     return listA ; 
    } 

    public void addToA(A a) { 
     a.getListB().add(this); 
    } 
} 

Come previsto in una relazione molti a molti, io Ho aggiornato entrambi i lati della relazione bidirezionale.

Ora il problema che devo affrontare è duplicazione delle voci popup quando tento di aggiungere/aggiornare un elemento nella raccolta. Quanto segue è il codice che uso a persistere l'entità ...

b.getA().clear() ; 
... 
... 
b.getListA().add(A) ; 
b.addToA(A) ; 
em.saveOrUpdate(b) ; 

Di seguito sono le query sparati da Hibernate, che si ottengono dai registri.

delete from A_B where b_id=? 

insert into A_B (b_id, a_id) values (?, ?) 
insert into A_B (b_id, a_id) values (?, ?) 

delete from A_B where a_id=? 

insert into A_B (a_id, b_id) values (?, ?) 
insert into A_B (a_id, b_id) values (?, ?) 
insert into A_B (a_id, b_id) values (?, ?) 
insert into A_B (a_id, b_id) values (?, ?) 

Dove sto andando storto qui? Come sbarazzarsi dei duplicati che vengono inseriti? La cache viene svuotata correttamente ma le voci duplicate sono l'unico problema!

risposta

10

Questo è un classico!

Il problema è che entrambi i mapping sono proprietari, quando uno dovrebbe essere il proprietario e uno dovrebbe essere inverso. Poiché entrambi sono proprietari, le modifiche a entrambi comporteranno inserimenti nel database; con un proprietario e un inverso, ci sarà solo un set di inserimenti.

Dovreste essere in grado di riscrivere B::getListA come:

@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, mappedBy = "listB") 
public List<A> getListA() 

E avere un lavoro tutto.

Si noti che solo il lato proprietario come annotazione @JoinTable. Di norma, ogni dato bit del database viene mappato nello esattamente in una posizione in un'applicazione JPA. Se ti ritrovi a mappare qualcosa due volte, dai un'occhiata lunga per vedere se c'è un modo migliore per farlo.

Per inciso, non è necessario gli attributi targetEntity; un fornitore JPA può farlo fuori dai generici.

+3

Grande! Pertanto, con l'attributo mappedBy, informiamo Hibernate su quale sia la tabella dei proprietari e la configurazione delle entità. La mia comprensione è corretta? – Venkat

+2

Sì, è esattamente così. –

+0

Qualche idea sul perché potresti ANCORA ottenere inserimenti duplicati quando hai mappato l'installazione? Nel mio caso sto usando l'accesso al campo invece dell'accesso al metodo. Potrebbe essere un problema? – JBCP