2013-05-24 5 views
8

Sto avendo un problema strano con Spring e Hibernate quando si eseguono più thread in un'applicazione. Sto usando la molla 3.2.0.RELEASE e ibernazione 4.1.12.Finale. Il problema è che per alcuni oggetti, quando vengono recuperati dal db, il recupero ha esito positivo, ma tutte le raccolte mappate non vengono impostate. Ecco un esempio del mio repo:NullPointerException durante l'accesso alla raccolta lenta caricata nel metodo DAO

@Repository("fooRepository") 
public class FooRepository { 

    private static final Logger log = Logger.getLogger(FooRepository.class); 

    @Autowired 
    private SessionFactory sessionFactory; 

    @Override 
    @Transactional 
    public Foo retrieve(final Long id) { 
     Foo instance = (Foo) sessionFactory.getCurrentSession().get(Foo.class, id); 
     for (CollectionMember member : instance.getCollectionMembers()) { 
      log.debug(member.getId());  
     } 
     return instance; 
    } 

Per alcuni oggetti, questo metodo genera sempre il seguente errore quando si tenta di accedere alla lista CollectionMember:

Caused by: java.lang.NullPointerException 
at org.hibernate.collection.internal.PersistentBag.toString(PersistentBag.java:501) 

Il problema è che il session.get() call crea oggetti PersistentBag per tutte le raccolte pigre ma non imposta mai il contenuto. Ciò si verifica solo quando il multithreading è abilitato. Qualcuno può spiegare perché questo sta accadendo?

EDIT: Ecco il bit rilevante della classe foo:

@Entity 
@Table(name = "FOO") 
@XmlRootElement(name = "foo") 
public class Foo { 

    @EmbeddedId 
    private FooPK id; 

    @OneToMany 
    @NotFound(action = NotFoundAction.IGNORE) 
    @JoinColumns({ 
      @JoinColumn(name = "COLLECTION_ID", referencedColumnName = "COLLECTION_ID"), 
      @JoinColumn(name = "FOO_ID", referencedColumnName = "FOO_ID")}) 
    private List<CollectionMember> collectionMembers = new ArrayList<CollectionMember>(); 

E la classe CollectionMember:

@Entity 
@Table(name = "COLLECTION_MEMBER") 
@XmlRootElement(name = "collectionMember") 
public class CollectionMember { 

    @EmbeddedId 
    private CollectionMemberPK primaryKey; 

    @ManyToOne 
    @JoinColumn(name = "COLL_CODE") 
    private CollectionCode collectionCode; 
+3

Fornire traccia completa dello stack (anche se è un po 'prolisso) potrebbe aiutare a risolvere meglio il problema. – Subhas

+0

ogni possibilità di pubblicare le classi Foo e CollectionMember, o almeno i bit che mostrano annotazioni di mappatura (o il file hbm.xml se si utilizza la configurazione xml) –

+0

Non riesco a pubblicare lo stacktrace completo a meno che non passi e cambi manualmente un sacco di nomi di pacchetti in modo da non poter dire da dove viene. Se pensi davvero che lo stack completo fornirà più informazioni, lo farò, ma non sono convinto che ti serva più di quanto tu abbia già ottenuto in questo senso, ma aggiungerò i mapping. – Rockenstein

risposta

0

Da dove viene il multithreading entrano in gioco? Come garantire che un singolo thread utilizzi sempre la stessa sessione di sospensione?

Il modo in cui comprendo il caricamento in stato di ibernazione e pigro, quando si attraversa la raccolta di Hibernate verrà visualizzato in CurrentSession. In un ambiente con multithreading la sessione potrebbe essere già stata utilizzata da un altro thread. Non sono sicuro che @Transactional sia sufficiente per preservare la tua stessa Hibernate Session.

Potrebbe esserci una configurazione Hibernate per questo.

0

Provare quanto segue.

Foo instance = (Foo) sessionFactory.getCurrentSession().get(Foo.class, id); 

Hibernate.initialize(instance.getCollectionMembers()) 

for (CollectionMember member : instance.getCollectionMembers()) { 

     log.debug(member.getId());  
} 

return instance; 
0

marchi Hibernate tutti * mapping di raccolta pressi di molte come pigri per impostazione predefinita, salvo diversa indicazione. È impossibile che un thread carichi l'oggetto e non sia in grado di leggere la raccolta lazy associata all'interno di un unico metodo @Transactional.

Che cos'è il multi-threading nel tuo caso? Si generano i thread manualmente o si tratta di un'applicazione Web? Come si impostano i limiti di sessione?

BTW, se vuoi davvero risolvere il problema, ti consiglio di postare lo stacktrace .. potrebbero essere necessari solo pochi secondi per sostituire i nomi dei pacchetti commerciali, nel caso sia preoccupante.

0

Quando si utilizza la sospensione in ambiente di primavera, non è consigliabile ottenere la sessione direttamente tramite la produzione di sessione, è possibile che si verifichi una perdita di sessione. provare a sostituire con il seguente schema:

getHibernateTemplate().execute(new HibernateCallback() { 
     public Object doInHibernate(Session session) throws HibernateException, SQLException { 
      Foo instance = (Foo) session.get(Foo.class, id); 
      //whatever logic goes here 
      return instance; 
     } 
    }); 
2

problema è @NotFound(action = NotFoundAction.IGNORE)

Ciò creerà un nullo oggetto restituito invece di un insieme vuoto nel caso non vengono trovati record di raccolta. Rimuoverlo e testarlo

2

Penso che dovresti usare la terza tabella di join e usare i criteri per caricare la raccolta.

@OneToMany(fetch = FetchType.LAZY) 
    @JoinTable(name = "table", joinColumns = {@JoinColumn(name = "COLLECTION_ID", nullable = false)}, inverseJoinColumns = { @JoinColumn(name = "FOO_ID", nullable = true) }) 
    private List<CollectionMember> collectionMembers = new ArrayList<CollectionMember>(); 


@ManyToOne(mappedBy="collectionMembers") 
private CollectionCode collectionCode;