2015-05-06 9 views
5

Sto riscontrando qualche problema nel rendere l'associazione @ManyToOne caricata in Brasile. Sto usando il recupero = LAZY ma non funziona quando join non è fatto dalla colonna chiave primaria.@ManyToOne (fetch = FetchType.LAZY) non funziona sulla colonna con chiave non primaria di riferimento

So che questa domanda era già asked ma penso che non è stata data una risposta adeguata, quindi fornisco informazioni dettagliate per chiarire il problema.

Questo è il mio modello:

DummyB -> DummyA 

Queste sono le tabelle:

create table dummyA (
    id number(18,0), --pk 
    name varchar2(20) -- unique field 
); 

create table dummyB (
    id number(18,0), 
    dummya_id number(18,0), 
    dummya_name varchar2(20) 
); 

E queste sono le entità:

@Entity 
public class DummyA implements Serializable { 

    private Long id; 
    private String name; 

    @Id 
    public Long getId() { 
     return id; 
    } 

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

    public String getName() { 
     return name; 
    } 

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

} 

@Entity 
public class DummyB implements Serializable { 

    private Long id; 
    private DummyA dummyA; 

    @Id 
    public Long getId() { 
     return id; 
    } 

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

    /* Case 1: mapping DummyB -> DummyA by DummyA NON primary key (field name) */ 
    // @ManyToOne(fetch = FetchType.LAZY) 
    // @JoinColumn(name = "dummya_id") 
    // public DummyA getDummyA() { 
    // return dummyA; 
    // } 

    /* Case 2: mapping DummyB -> DummyA by DummyA primary key */ 
    @ManyToOne(fetch = FetchType.LAZY) 
    @JoinColumn(name = "dummya_name", referencedColumnName = "name") 
    @LazyToOne(LazyToOneOption.PROXY) 
    public DummyA getDummyA() { 
     return dummyA; 
    } 

    public void setDummyA(DummyA dummyA) { 
     this.dummyA = dummyA; 
    } 

} 

Nota metodo getDummyA in entità DummyB è duplicato per prova due casi per unirti alle entità.

Caso 1: mappatura DummyB -> DummyA da DummyA chiave primaria

@ManyToOne (prendere = FetchType.LAZY) @JoinColumn (name = "dummya_id")

Questo funziona bene, basta una query viene eseguita per recuperare oggetti DummyB.

Caso 2: mappatura DummyB -> DummyA da DummyA NON chiave primaria (nome del campo)

@ManyToOne (prendere = FetchType.LAZY) @JoinColumn (name = "dummya_name", referencedColumnName = "nome ")

La stessa selezione di dummyB è eseguita, ma subito dopo, un dummyA select viene eseguito il filtro per nome =? per recuperare il relativo oggetto A.

sto usando un molto semplice JUnit per eseguire il filtraggio:

public class DummyTest { 

    @Autowired 
    HibernateTransactionManager transactionManager; 

    @Test 
    @Transactional 
    public void testFindDummyB() throws DAOException { 
     Long idDummyB = 2L; 

     Session session = getCurrentHibernateSession(); 

     List lst = session.createCriteria(DummyB.class) 
       .add(Restrictions.eq("id", idDummyB)).list(); 

     assertTrue(lst.size() > 0); 
    } 

    private Session getCurrentHibernateSession() { 
     return this.transactionManager.getSessionFactory().getCurrentSession(); 
    } 

} 

mie librerie:

  • org.hibernate: hibernate-core: jar: 4.2.17.Final: compilare
  • org.hibernate.common: hibernate-commons-annotations: jar: 4.0.2.Final: compile
  • org.hibernate.javax.persistence: hibernate-jpa-2.0-api: jar: 1.0.1.Final: compilare
  • org.hibernate: hibernate-validator: jar: 4.3.2.Final: fornito

Altre cose che ho già provato:

  • Aggiunta @LazyToOne di hiberante a getDummyA() metodo doesn non ha alcun effetto

    @LazyToOne (LazyToOneOption.PROXY) @ManyToOne (prendere = FetchType.LAZY, optional = true) @JoinColumn (name = "dummya_name", referencedColumnName = "nome") @LazyToOne (LazyToOneOption.PROXY)

  • La creazione di una chiave esterna dalla tabella DummyB a dummyA (e un vincolo univoco nel campo dummya.name) non ha alcun effetto.

  • L'aggiunta di @Column (unique = true) sul metodo DummyA getName() non è riuscita.
  • Imposta facoltativo = vero o falso come suggerito here non ha alcun effetto.
  • Provare a forzare il caricamento lazy utilizzando setFetchMode nei criteri non ha funzionato, DummyA select continua a essere in esecuzione.

    Elenco lst = session.createCriteria (DummyB.class) .add (Restrictions.eq ("id", idDummyB)). setFetchMode ("dummyA", FetchMode.SELECT) .list();

Non riesco a trovare nella documentazione di Hibernate un punto in cui ci si riferisce a questo comportamento, quindi mi chiedo se c'è qualcosa è sbagliato nelle mie annotazioni o mi sono imbattuto in un bug di Hibernate.

Qualcuno può dirlo?

AGGIORNATO richiesta md: Per impostare più chiaro:

È questo il comportamento previsto o è un bug? se questo è il comportamento previsto, dove è documentato?

Grazie.

risposta

0

Non è possibile utilizzare il nome come colonna di join, perché non esiste un vincolo univoco per questo. Quindi è possibile che si traduca in una mappatura ManyToMany anziché ManyToOne. Non ho idea se Hibernate lo accetti, ma a lungo andare finirebbe con qualcosa di inaspettato. Inoltre non vedo il caso d'uso per quello. Ti consiglio di usare sempre un ID lungo come chiave primaria e mappare automaticamente tramite questo campo. Tale gestione speciale è necessaria solo se si dispone di un sistema non ortodosso o deve essere compatibile con un db legacy.

+0

Il caso esposto sopra è una semplificazione del mio problema, nel mio caso reale la colonna a cui partecipare è una colonna univoca. A proposito se questo non ha senso, è complesso da spiegare e non è il punto della mia domanda, sto chiedendo "è questo il comportamento previsto? Se sì, dove è documentato?" – kothvandir

+0

Rispondere per favore alla tua domanda, perché quella sopra è sbagliata. –

+0

Questo è esattamente quello che chiedo nel mio ultimo paragrafo, ma come hai chiesto, l'ho chiarito meglio. – kothvandir

3

Vedere esattamente lo stesso comportamento con Hibernate 5.0.4. @ManyToOne (con un reciproco OneToMany) e Lazy il recupero funziona perfettamente se la colonna di join è la chiave primaria. Se non lo è, lazy loading breaks e Hibernate prelevano avidamente tutti gli ManyToOne ogni volta che un oggetto viene istanziato. Questo può essere catastroficamente lento se si fa un Criteria.list() per, diciamo, 1000 record. Ciò che è iniziato come una singola query per 1000 record può generare una mongolfiera in 5000 query per ottenere con impazienza una varietà di @ManyToOne utilizzando selezioni individuali.

Assolutamente nulla che ho potuto testare/cambiare lo ha spiegato in alcun modo e posso riprodurlo in modo affidabile.

La soluzione che ho dovuto implementare nella mia app che utilizza non-PK per unisce era nel cestino appena @ManyToOne/@OneToMany coppie di annotazione e scrivere collezione recupera manualmente (caching dei risultati con una variabile transitoria). È molto più lavoro, ma le prestazioni sono sostanzialmente più elevate dato che alcuni dei miei oggetti hanno 5 o 6 oggetti @ManyToOne e tutti questi sono stati recuperati con impazienza con selezioni individuali da Hibernate.

Sfortunatamente, non riesco a riorganizzare il mio schema per sistemare questa stranezza in Hibernate. Sto facendo un progetto che coinvolge Heroku Connect e i join tra tabelle quando si uniscono i dati da Salesforce.com vengono eseguiti utilizzando una colonna "sfid" nella tabella che non è la chiave primaria.La chiave primaria è un valore separato univoco per il record nel database Heroku Postgres e non può essere utilizzata per eseguire join in quanto nessun'altra tabella nel database fa riferimento a questa chiave primaria.

Suppongo che si tratti di un bug in Hibernate; nulla di ciò che ho letto o che ho potuto modificare ha influenzato questo comportamento in alcun modo e, come ho detto, posso far funzionare il sistema esattamente come previsto se le colonne di join sono le chiavi primarie.

1

Nel caso in cui qualcuno ha ancora dei problemi con questo abbiamo ottenuto di lavorare nel modo seguente:

@ManyToOne(fetch = FetchType.LAZY) 
@JoinColumn(name = "dummya_name", referencedColumnName = "name", insertable = false, updatable = false), 
@LazyToOne(LazyToOneOption.NO_PROXY) 
public DummyA getDummyA() { 
    if (fieldHandler != null) { 
     return (DummyA) fieldHandler.readObject(this, "dummyA", dummyA); 
    } 
    return dummyA; 
} 

public void setDummyA(DummyA dummyA) { 
    if (fieldHandler != null) { 
     this.dummyA = (DummyA) fieldHandler.writeObject(this, "dummyA", this.dummyA, dummyA); 
     return; 
    } 
    this.dummyA= dummyA; 
} 

@Override 
public void setFieldHandler(FieldHandler fieldHandler) { 
    this.fieldHandler = fieldHandler; 
} 

@Override 
public FieldHandler getFieldHandler() { 
    return fieldHandler; 
} 

È stato spiegato bene in Hibernate lazy loading for reverse one to one workaround - how does this work?

0

@peach soluzione ha funzionato per me. Solo alcune cose non è stato menzionato:

@Entity 
public class DummyB implements Serializable { 

dovrebbe essere

@Entity 
public class DummyB implements Serializable, FieldHandled { 

e se si utilizza @JsonIgnoreProperties si dovrebbe aggiungere fieldHandler

@JsonIgnoreProperties({"hibernateLazyInitializer", "handler", "fieldHandler"}) 
Problemi correlati