2014-11-05 7 views
8

Si verifica un problema strano quando si utilizza JPA/Hibernate. Vedo voci duplicate durante il test.Ottenimento di voci duplicate utilizzando Hibernate

Ecco la mia classe BaseEntity:

@Entity 
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS) 
public abstract class BaseEntity { 

    @Id 
    @Column(name = "ID", updatable = false, nullable = false) 
    @GenericGenerator(name = "uniqueIDGenerator", strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator", parameters = { 
      @org.hibernate.annotations.Parameter(name = "sequence_name", value = "ID_SEQUENCE"), 
      @org.hibernate.annotations.Parameter(name = "increment_size", value = "100"), 
      @org.hibernate.annotations.Parameter(name = "optimizer ", value = "pooled") }) 
    @GeneratedValue(generator = "uniqueIDGenerator") 
    @NotNull 
    protected int id; 

    public int getId() { 
     return id; 
    } 

    public void setId(int id) { 
     this.id = id; 
    } 

} 

Qui è la mia principale classe compagno:

@Entity 
@Table(name = "partner") 
public class Partner extends BaseEntity{ 

    @Column(name = "name") 
    private String name; 

    @OneToMany(mappedBy="partner", fetch=FetchType.EAGER) 
    @Cascade(CascadeType.DELETE) 
    private List<Receipt> receipts; 

    @OneToMany(mappedBy="partner", fetch=FetchType.EAGER) 
    @Cascade(CascadeType.DELETE) 
    private List<Delivery> deliveries; 

    public Partner() { 
     setReceipts(new ArrayList<Receipt>()); 
     setDeliveries(new ArrayList<Delivery>()); 
    } 

    public Partner(String name){ 
     this(); 

     this.setName(name); 
    } 

    public String getName() { 
     return name; 
    } 

    public void setName(String name) { 
     this.name = name; 
    } 

    public List<Receipt> getReceipts() { 
     return receipts; 
    } 

    public void setReceipts(List<Receipt> receipts) { 
     this.receipts = receipts; 
    } 

    public List<Delivery> getDeliveries() { 
     return deliveries; 
    } 

    public void setDeliveries(List<Delivery> deliveries) { 
     this.deliveries = deliveries; 
    } 
} 

Partner ha due sottoclassi, Ricevimento e consegna. Qui è la classe Receipt:

@Entity 
@Table(name = "receipt") 
public class Receipt extends BaseEntity{ 

    @ManyToOne(fetch=FetchType.LAZY) 
    @JoinColumn(name="partnerId") 
    private Partner partner; 

    @Column(name = "name") 
    private String name; 
    @Column(name = "json") 
    private String json; 

    @Transient 
    private ReceiptContainer receiptContainer; 


    public Receipt() { 
    } 
    public Receipt(String name){ 
     this(); 

     this.setName(name); 
    } 

    public String getName() { 
     return name; 
    } 

    public void setName(String name) { 
     this.name = name; 
    } 

    public String getJson() { 
     return json; 
    } 

    public void setJson(String json) { 
     this.json = json; 
    } 

    public ReceiptContainer getReceiptContainer() { 
     return receiptContainer; 
    } 

    public void setReceiptContainer(ReceiptContainer receiptContainer) { 
     this.receiptContainer = receiptContainer; 
    } 

    public Partner getPartner() { 
     return partner; 
    } 

    public void setPartner(Partner partner) { 
     this.partner = partner; 
    } 

} 

Qui è la classe di consegna:

@Entity 
@Table(name = "delivery") 
public class Delivery extends BaseEntity{ 

    @ManyToOne(fetch=FetchType.LAZY) 
    @JoinColumn(name="partnerId") 
    private Partner partner; 

    @Column(name = "name") 
    private String name; 
    @Column(name = "json") 
    private String json; 

    @Transient 
    private DeliveryContainer deliveryContainer; 


    public Delivery() { 
    } 
    public Delivery(String name){ 
     this(); 

     this.setName(name); 
    } 

    public String getName() { 
     return name; 
    } 

    public void setName(String name) { 
     this.name = name; 
    } 

    public String getJson() { 
     return json; 
    } 

    public void setJson(String json) { 
     this.json = json; 
    } 

    public DeliveryContainer getDeliveryContainer() { 
     return deliveryContainer; 
    } 

    public void setDeliveryContainer(DeliveryContainer deliveryContainer) { 
     this.deliveryContainer = deliveryContainer; 
    } 

    public Partner getPartner() { 
     return partner; 
    } 

    public void setPartner(Partner partner) { 
     this.partner = partner; 
    } 

} 

sto ottenendo i miei oggetti come questo:

@Transactional 
public Partner get(int id){ 
    Partner partner = entityManager.find(Partner.class, id); 
    return partner; 
} 

Come si può vedere, la classe socio include un elenco di oggetti e un elenco di oggetti, che sono memorizzati nelle rispettive tabelle, ricevuta e consegna, rispettivamente.

--- Prima di andare oltre, desidero notare che il numero corretto di voci è visualizzato nel database. Questo problema si verifica solo durante il recupero degli oggetti nell'app Java .---

Quando aggiungo un oggetto e non sono presenti oggetti, viene visualizzato un oggetto e non viene visualizzato alcun oggetto, il che è previsto. Se esistono un oggetto e un oggetto, vengono visualizzati nuovamente un oggetto e un oggetto, che è previsto. Il problema si verifica quando sono presenti più di uno o di un oggetto. Ad esempio, se vengono aggiunti due oggetti ma viene aggiunto un solo oggetto, vengono visualizzati due oggetti E due oggetti.

Per meglio illustrare ciò, vedere la tabella seguente. I risultati imprevisti sono circondati da trattini.

<Receipt> Obj Added | <Delivery> Obj Added | <Receipt> Obj Displayed | <Delivery> Obj Displayed 
     0   |   0   |   0    |   0 
     0   |   1   |   0    |   1 
     1   |   0   |   1    |   0 
     1   |   1   |   1    |   1 
     1   |   2   |  ---2---   |   ---2--- 
     2   |   1   |  ---2---   |   ---2--- 
     2   |   2   |  ---4---   |   ---4--- 

La mia domanda è, perché sto vedendo questi risultati inaspettati?

+0

Hai bisogno di altre informazioni per aiutarti: mostra come stai generando l'ID per queste classi (pubblica la tua classe di baseentity), mostra come stai persistendo e recuperando i dati – zmf

+0

Appena aggiunto, grazie a – user1472409

risposta

10

La ragione per cui si vedono i duplicati è perché si stanno recuperando con impazienza i bambini, che sta creando un join esterno.

(È possibile utilizzare @Fetch (FetchMode.SELECT), ma questo si tradurrà in ulteriori domande per recuperare gli oggetti)

Hibernate API reference indicating outer join takes place.

Ecco un a risposta approfondita alla tua domanda: Hibernate Criteria returns children multiple times with FetchType.EAGER

È possibile passare dall'elenco a Set (essere sicuri di override equals/compareTo/hashCode se lo fai). Oppure puoi aggiungere un trasformatore di risultati ai risultati della query se stai estraendo questi elementi dal database tramite criteri.

Per quello che il suo valore, Hibernate consiglia di utilizzare set sulle liste

+0

qualcuno dovrebbe utilizzare List o Set in base al fatto che vogliano elencare o impostare la semantica, non in base al fatto che una soluzione di persistenza ne "raccomandi". La soluzione di persistenza dovrebbe essere trasparente a tutto –

+0

È una critica alla mia raccomandazione di utilizzare set o di ibernazione? – zmf

+0

È una semplice dichiarazione del motivo per cui uno sviluppatore dovrebbe selezionare un particolare tipo di raccolta: la scelta della soluzione di persistenza non dovrebbe entrare nell'argomento, ma solo nel modello. Se le persone scelgono di prenderlo come "critica" dipende da loro –

6

Forse puoi provare a fare la tua query usando la proprietà DISTINCT.

È sufficiente aggiungere questo ai vostri criteri:

criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY); 

Se non si utilizza criteri:

SELECT DISTINCT <QUERY> 

Se non risolve il problema, mostrare il proprio codice di query.

0

È possibile memorizzare il risultato della query in un Set, verrà rimosso il duplicato per voi.

0

Se si utilizza JPA:

CriteriaBuilder cb = entityManager.getCriteriaBuilder(); 
CriteriaQuery<Receipt> query = cb.createQuery(Receipt.class); 
query.distinct(true); 

altrimenti vedere la risposta di Gernan.

0

Hibernate non restituisce risultati distinti per una query con recupero raccolta esterno abilitato per una raccolta (anche se utilizzo la parola chiave distinta)? leggi more, potrebbe aiutarti ..

Problemi correlati