2013-02-18 8 views
19

Mam małą aplikację konsolową i używam spring-data-jpa z hibernacją. Naprawdę nie mogę wymyślić, jak leniwy zainicjować kolekcje podczas korzystania z spring-data-jpa z jego repozytoriami, w samodzielnej aplikacji konsolowej. Oto niektóre z moich kodu:jak leniwe ładowanie kolekcji przy użyciu spring-data-jpa, z hibernacją, z aplikacji konsolowej

@Entity 
public class User { 
... 
    @OneToMany(cascade=CascadeType.ALL) 
    @JoinColumn(name="USER_ORDER_ID") 
    private Set<Order> orders = new HashSet<Order>(); 
... 
} 

repozytorium:

public interface UserRepository extends PagingAndSortingRepository<User, Long> { 

    public ArrayList<User> findByFirstNameIgnoreCase(String firstName); 
} 

impl usługowa:

@Service 
@Repository 
@Transactional 
public class UserServiceImpl implements UserService { 
    @Autowired 
    private UserRepository userRepository; 

public ArrayList<User> findByFirstNameIgnoreCase(String firstName) { 
    ArrayList<User> users = new ArrayList<User>(); 
    users = userRepository.findByFirstNameIgnoreCase(firstName); 
    return users; 
} 

moim głównym metoda:

... 
user = userRepository.findByFirstNameIgnoreCase("john").get(0); 
orders = user.getOrders(); 
for (Order order : orders) { 
    LOGGER.info("getting orders: " + order.getId()); 
}  

pętla foreach dostaje wyjątek :

EVERE: nie udało się leniwie zainicjować kolekcję rola com.aki.util.User.orders, brak sesji lub sesja została zamknięta org.hibernate.LazyInitializationException: nie udało się leniwie zainicjować kolekcję Rola:

Należy zauważyć, że nie mam tego problemu podczas uruchamiania tego z aplikacji webowej z jakimś typem OpenSessionInViewFilter.

+0

Sprawdź to: http://stackoverflow.com/questions/15359306/how-to-load-lazy-fetched-items-from-hibernate-jpa-in- my-controller –

Odpowiedz

10

Jednym z rozwiązań jest, aby User.orderschętnie pobierane zbierania przez

@OneToMany(cascade=CascadeType.ALL, fetch = FetchType.EAGER) 
private Set<Order> orders = new HashSet<Order>(); 

związków Entity leniwie ładowane domyślnie. Oznacza to, że zestaw orders jest właściwie tylko obiektem proxy, który nie zostanie zainicjowany, dopóki nie zostanie wywołana metoda na nim. Jest to dobre, ponieważ powiązane obiekty nie zostaną załadowane, chyba że będą potrzebne. Może to jednak powodować problemy, jeśli spróbujesz uzyskać dostęp do niezainicjowanej kolekcji poza uruchomioną transakcją.

Jeśli wiesz, że w większości przypadków będziesz potrzebować Zleceń Użytkownika, to ma sens, aby stowarzyszenie było chętnie pobierane. W przeciwnym razie musisz upewnić się, że kolekcja zostanie zainicjowana/załadowana w ramach transakcji. Wspomniany wcześniej użytkownik zapewnia, że ​​transakcja pozostaje otwarta podczas przetwarzania żądania, dlatego ten problem nie występuje w przeglądarce internetowej.

W przypadku trzeba zachować go leniwie załadowany, spróbuj użyć sprężyny TransactionTemplate owinąć kod w swoim głównym sposobem:

TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager); 
transactionTemplate.execute(new TransactionCallbackWithoutResult() { 
    @Override 
    protected void doInTransactionWithoutResult(TransactionStatus status) { 
    ... 
    } 
}); 
+2

to nie jest to, czego szukam. być może nie było jasne, że chcę zrobić leniwą inicjację.Zmieniłem moje pytanie. – aki

+0

W takim przypadku zobacz moją zredagowaną odpowiedź dotyczącą użycia 'TransactionTemplate'. – zagyi

+0

Naprawdę nienawidzę pracy z TransactionTemplate, ale działa. Nie ma innych sugestii? – aki

7

Jednak znalazłem sposób. Metoda ta jest w moim realizacji usług:

public Set<Order> fetchUserOrders(Long userId) { 
    User user = userRepository.findOne(userId); 
    Hibernate.initialize(user.getOrders()); 
    Set<Order> orders = user.getOrders(); 
    return orders; 
} 

Uwaga: to jest jak @zagyi sugested w jednym ze swych komentarzy, ale także zwrócić uwagę na oświadczenie: Hibernate.initialize(user.getOrders()); bez tego kolekcja nadal nie być inicjowane i ci otrzyma błąd.

+6

Tylko uwaga: 'Hibernate.initialize()' przywiąże twoją aplikację do Hibernate. W porządku, jeśli cię to nie obchodzi. Jeśli masz na to ochotę, użyj zamiast tego 'user.getOrders(). Size()'. Chociaż nie będzie to odzwierciedlało twojego zamiaru, nie będzie ono zależało od wywołań innych niż JPA i zawsze możesz zadeklarować metodę pomocnika z bardziej opisową nazwą, która odbiera kolekcję i po prostu wywołuje na niej wartość 'size()'. – zagyi

+0

To działa. Masz rację, znacznie lepiej jest użyć user.getOrders(). Size. Nie to, że zamierzam się przełączyć z hibernacji, w najbliższym czasie. Mógłbym przysiąc, że próbowałem tego wcześniej. Chociaż nie jestem pewien, czy to z warstwy usługi :). W każdym razie bardzo dziękuję za pomoc. – aki

+0

Czy to oznacza, że ​​przechodzi to w pętlę for, aby uzyskać wszystkie usersWithOrders? Powiedzmy, że wyciągam FindAll na użytkowników, co oznacza usersWithOrders, czy muszę robić Hibernate.initialize na każdym użytkowniku przez zapętlenie? Wiem, że EAGER działałby, ale czy w przypadku Lazy to jedyny sposób? – PavanSandeep

-2

Ten pracował dla mnie:

@Component 
public class Test { 

    @Inject 
    OrderDAO orderDAO; 

    @PersistenceUnit 
    private EntityManagerFactory emf; 

    @PostConstruct 
    public void test(){ 
     EntityManager em= emf.createEntityManager(); 
     for (Order o:orderDAO.findAll()){ 
      o=em.find(o.getClass(),o.getId()); 
      System.out.println(o.getLazyField()); 
     } 
     em.close(); 
    } 
} 
Powiązane problemy