Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to lazy load collection when using spring-data-jpa, with hibernate, from an console application

I have an small console application and I am using spring-data-jpa with hibernate. I really can not figure out how to lazy initialize collections when using spring-data-jpa with its repositories, in an standalone console application. Here is some of my code:

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

repository:

public interface UserRepository extends PagingAndSortingRepository<User, Long> {      public ArrayList<User> findByFirstNameIgnoreCase(String firstName); } 

service impl:

@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; } 

my main method:

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

the foreach loop gets an exception:

EVERE: failed to lazily initialize a collection of role: com.aki.util.User.orders, no session or session was closed org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role:

Please note that I do not have this problem when running this from an webapp with some kind of OpenSessionInViewFilter.

like image 284
aki Avatar asked Feb 18 '13 14:02

aki


People also ask

How do you load a lazy object in Hibernate?

To enable lazy loading explicitly you must use “fetch = FetchType. LAZY” on an association that you want to lazy load when you are using hibernate annotations. @OneToMany( mappedBy = "category", fetch = FetchType.

How lazy loading works internally in Hibernate?

Hibernate now can "lazy-load" the children, which means that it does not actually load all the children when loading the parent. Instead, it loads them when requested to do so. You can either request this explicitly or, and this is far more common, hibernate will load them automatically when you try to access a child.

Can we use Spring data JPA with Hibernate?

This is the type of code Spring Data JPA helps to avoid. Spring Data JPA is really a set of dependencies that makes it easier to work with a JPA provider. Hibernate is one of several JPA providers. This means you can use Spring Data JPA without using Hibernate (if you really wanted to).

How lazy loading works in JPA?

By default, JPA uses the lazy fetch strategy in associations of type @ElementCollection. Thus, any access to the collection in a closed Persistence Context will result in an exception. This test throws an exception when we try to access the phone list because the Persistence Context is closed.


1 Answers

One solution can be to make User.orders an eagerly fetched collection by

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

Entity associations are lazily loaded by default. This means that the orders Set is actually just a proxy object that won't get initialized until you invoke a method on it. This is good, because the associated Order objects won't get loaded unless they are needed. However, this can cause problems, if you try to access the uninitialized collection outside of a running transaction.

If you know that in most of the cases you will need the User's Orders, it makes sense to make the association eagerly fetched. Otherwise you will have to ensure that the collection gets initialized/loaded inside a transaction. The OpenSessionInViewFilter you mentioned makes sure that the transaction stays open during the request processing, that is why you don't have this issue in yout webapp.

In case you must keep it lazily loaded, try using Spring's TransactionTemplate to wrap the code in your main method:

TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager); transactionTemplate.execute(new TransactionCallbackWithoutResult() {     @Override     protected void doInTransactionWithoutResult(TransactionStatus status) {     ...     } }); 
like image 200
zagyi Avatar answered Sep 19 '22 02:09

zagyi