2013-05-24 11 views
8

Mam dziwny problem ze Spring i Hibernate podczas uruchamiania wielu wątków w aplikacji. Używam wiosny 3.2.0.RELEASE i hibernacji 4.1.12.Final. Problem polega na tym, że dla niektórych obiektów, gdy są one pobierane z bazy danych, odzyskiwanie się powiedzie, ale wszystkie zmapowane kolekcje nie są ustawiane. Oto przykład z mojego repo:Wyjątek NullPointerException podczas uzyskiwania dostępu do leniwego zbioru w ramach metody 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; 
    } 

Dla niektórych obiektów, ta metoda zawsze wyrzuca następujący błąd, gdy próbuje uzyskać dostęp do listy CollectionMember:

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

Problem polega na tym, że session.get() call tworzy obiekty PersistentBag dla wszystkich leniwych załadowanych kolekcji, ale nigdy nie ustawia zawartości. Dzieje się tak tylko wtedy, gdy włączona jest wielowątkowość. Czy ktoś może wyjaśnić, dlaczego tak się dzieje?

EDIT: Oto odnośny bit class 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>(); 

I klasa 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

Śledzenie całego stosu (nawet jeśli jest trochę bardziej szczegółowe) może pomóc w lepszym rozwiązywaniu problemu. – Subhas

+0

jest szansa, że ​​możesz publikować klasy Foo i CollectionMember lub przynajmniej bity, które pokazują adnotacje mapowania (lub plik hbm.xml, jeśli korzystasz z xml config). –

+0

Nie mogę opublikować pełnego stosu, chyba że przejdę i ręcznie zmienię mnóstwo nazw pakietów, więc nie możesz powiedzieć, skąd się wzięły. Jeśli naprawdę sądzisz, że pełny stacktrace dostarczy więcej informacji, zrobię to, ale nie jestem przekonany, że potrzebujesz więcej, niż już masz w tym sensie. Dodam jednak mapowania. – Rockenstein

Odpowiedz

0

Skąd wielowątkowość wchodzić w grę? W jaki sposób zapewnia, że ​​pojedynczy wątek zawsze używa tej samej sesji hibernacji?

Sposób, w jaki rozumiem hibernację i leniwy ładunek, podczas przechodzenia przez kolekcję Hibernate będzie wyglądał w CurrentSession. W środowisku wielowątkowym ta sesja mogła już być używana przez inny wątek. Nie jestem pewien, czy @Transactional wystarczy, aby zachować tę samą sesję Hibernate.

Może to być konfiguracja Hibernacja dla tego.

0

Spróbuj wykonać następujące czynności.

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

Hibernacja oznacza wszystkie * Wiele mapowań kolekcji jako leniwych domyślnie, chyba że zaznaczono inaczej. Nie jest możliwe, aby jeden wątek wczytał obiekt i nie był w stanie odczytać powiązanej leniwej kolekcji wewnątrz jednej metody @Transactional.

Co to jest wielowątkowość w twoim przypadku? Czy odradzasz wątki ręcznie, czy jest to aplikacja internetowa? Jak ustawić ograniczenia sesji?

BTW, jeśli naprawdę chcesz rozwiązać problem, radziłbym opublikować stacktrace ... może to potrwać kilka sekund, aby zastąpić komercyjne nazwy pakietów, na wypadek, gdyby to dotyczyło.

0

Podczas korzystania z trybu hibernacji w środowisku wiosennym, nie zaleca się uzyskiwania sesji bezpośrednio przez fabrykę sesji, ponieważ może to prowadzić do wycieku sesji. Spróbuj zastąpić następującym wzorem:

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

Problem jest @NotFound(action = NotFoundAction.IGNORE)

To stworzy zwrócony obiekt NULL zamiast pustej kolekcji w przypadku nie zostaną znalezione żadne rekordy zbiórki. Usuń i przetestuj to:

2

Myślę, że powinieneś używać trzeciej tabeli złączenia i używać Kryteriów do ładowania kolekcji.

@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; 
Powiązane problemy