2012-10-09 13 views
6

Ho una relazione molti-a-molti con una colonna aggiuntiva nella tabella dei collegamenti. L'ho configurato in modo che il lato proprietario recuperi i bambini desiderosi (quindi non ottengo LazyInitializationException) e nella direzione opposta è pigro. Questo funziona.@Transactional (readOnly = true) porta a LazyInitializationException

ora voluto per mettere a punto le transazioni (prima c'era solo @Transactional sul livello di classe di classi DAO e Service ho impostato il metodo getById a readOnly = true:.

@Transactional(readOnly = true) 
public Compound getById(Long id) { 
    return compoundDAO.getById(id); 
} 

Dopo questa modifica ho un LazyInitializationException in seguente frammento di codice:

Compound compound = compoundService.getById(6L);   
Structure structure = compound.getComposition().get(0).getStructure(); 
System.out.println("StructureId: "+ structure.getId()); // LazyInitializationException 

Se rimuovo (readOnly = true) questo funziona qualcuno può spiegare questo comportamento io uso Primavera + Hibernate tipo di confusione, come non vedo alcuna ragione per cui esimo!?. dovrebbe influenzare quali dati sono stati caricati?


EDIT:

Frammenti di definizioni delle relazioni. Questo è un molti-a-molti con una colonna nella tabella dei collegamenti.

lato Possedere (ad es composto contiene Structures):

@OneToMany(fetch = FetchType.EAGER, mappedBy = "pk.compound", 
    cascade = CascadeType.ALL, orphanRemoval = true) 
@OrderBy("pk.structure.id ASC") 
private List<CompoundComposition> composition = new ArrayList<>(); 

Appartiene a lato:

@OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.structure", 
cascade = CascadeType.ALL) 
@OrderBy("pk.compound.id ASC") 
private List<CompoundComposition> occurence; 

molti-a-uno in @Embeddable classe ID

@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) 
public Compound getCompound() { 
    return compound; 
} 

@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) 
public Structure getStructure() { 
    return structure; 
} 

EDIT 2 :

St ack Trace

org.hibernate.LazyInitializationException: could not initialize proxy - no Session 
    at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:165) ~[hibernate-core-4.1.7.Final.jar:4.1.7.Final] 
    at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:272) ~[hibernate-core-4.1.7.Final.jar:4.1.7.Final] 
    at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:185) ~[hibernate-core-4.1.7.Final.jar:4.1.7.Final] 
    at org.bitbucket.myName.myApp.entity.Structure_$$_javassist_0.getId(Structure_$$_javassist_0.java) ~[classes/:na] 
    at org.bitbucket.myName.myApp.App.main(App.java:31) ~[classes/:na] 

EDIT 3:

veda anche il mio commento:

Log è molto diverso con readOnly e manca la parte erano i rapporti sono stati caricati, per esempio. alcuni selezioni mancano nel registro.

EDIT 4:

Così mi stanco con un DriverManagerDataSource di base e senza piscina Connection. Il problema è esattamente lo stesso. Per me sembra un problema in Hibernate.

+0

Qual è la definizione della transazione a livello di classe? –

+0

Solo @Transactional. –

+0

Puoi pubblicare frammenti delle classi del modello? E hai ancora l'annotazione '@ Transactional' nella parte superiore della classe DAO? – sbzoom

risposta

3

Questo è solo wow. Sto iniziando a capire perché alcune persone odiano gli ORM ... Mi sembra di dover passare ore e ore a risolvere un problema strano e la soluzione è un insieme molto specifico di annotazioni + un codice per aggirare i limiti di detta annotazioni.

In primo luogo perché questo accade (perché il significato con cui le annotazioni, ma non in termini di senso logico, quale è il vero problema qui come usare il buon senso è inutile.) Solo le prove e gli errori aiutano). Nella parte proprietaria, in @OneToMany ho orphanRemoval = true (che ho scoperto è necessario per la coerenza, si potrebbe pensare che i vincoli del database dovrebbero gestirlo ... solo una delle tante cose che possono far impazzire.). Sembra che se l'operazione non è di sola lettura, questa impostazione porta ad alcuni dati essere recuperati anche così il suo pigro, vale a dire qui:

@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) 
public Structure getStructure() { 
    return structure; 
} 

In una transazione di sola lettura, questo recupero non accade. Suppongo che, se non è possibile modificare nulla, non sarà necessario rimuovere gli orfani e quindi tutti i dati richiesti dalla logica di questa impostazione non sono necessari in un tx di sola lettura.

Quindi la soluzione ovvia sarebbe in relazione sopra per passare a FetchType.EAGER. Sbagliato! Se lo fai, non sarai in grado di aggiornare il lato proprietario (Compound) usando session.merge. Ciò porterà a StackOverFlowError.

La vera soluzione era già stata menzionata. Basta lasciare la configurazione così com'è, ma caricare esplicitamente i rapporti desiderati nel livello di servizio:

@Transactional(readOnly = true) 
@Override  
public Compound getById(Long id) { 

    Compound compound = compoundDAO.getById(id); 
    for (CompoundComposition composition : compound.getComposition()){ 
     Hibernate.initialize(composition.getStructure()); 
    }   
    return compound; 
} 

ammetto che sto tendendo a cadere nella trappola prematura ottimizzazione. Questo non sembra molto efficiente e sembra anche rompere come funziona SQL in primo luogo. Ma poi sono nella fortunata posizione che nella maggior parte dei casi CompoundComposition conterrà solo 1 o 2 elementi.

+1

I tuoi primi due paragrafi riassumono i 6 anni trascorsi con Hibernate/JPA/ORMs. – millhouse

0

Forse si potrebbe mettere

value.getComposition().get(i).getStructure(); 

nel corpo del metodo getById(), in modo che il lazy loading avviene all'interno della transazione. Mi rendo conto che in questo caso dovresti fare un ciclo su i che potrebbe essere sconveniente.