Page 1 of 2 12 LastLast
Results 1 to 10 of 11

Thread: [Spring Data JPA] ManyToMany, rg.hibernate.LazyInitializationException, design

  1. #1
    Join Date
    May 2011
    Location
    Madrid (Spain)
    Posts
    101

    Default [Spring Data JPA] ManyToMany, rg.hibernate.LazyInitializationException, design

    Hi, I have a ManyToMany relationship between two Entities (A and C), then our famous LazyInitializationException appears, after googling a lot, coding JPA directly, using EAGER (cannot simultaneously fetch multiple bags, sets instead of lists, ...), ... I think my best solution is to have 3 entities:
    A, B and C, where B only has 2 fields (ManyToOne to A and C). It works great!

    What's your opinion?

    Thanks in advance.

  2. #2
    Join Date
    Aug 2006
    Location
    Arequipa-Peru / South America
    Posts
    2,796

    Default

    If such link or bridge table would has its own properties, do it, if not, in what moment you got the LazyInitializationException?
    Could you post your annotation configuration?

    Read Hibernate documentation for to get better information
    - Manuel Jordan

    Kill Your Pride, Share Your Knowledge With All
    The Fear Of The LORD Is The Beginning Of Knowledge, But Fools Despise Wisdom And Discipline. Proverbs 1:7

    Blog


    Technical Reviewer of Apress

    • Pro SpringSource dm Server
    • Spring Enterprise Recipes: A Problem-Solution Approach
    • Spring Recipes: A Problem-Solution Approach, 2nd Edition
    • Pro Spring Integration
    • Pro Spring Batch
    • Pro Spring 3
    • Pro Spring MVC: With Web Flow
    • Pro Spring Security

  3. #3
    Join Date
    May 2011
    Location
    Madrid (Spain)
    Posts
    101

    Default

    Hi, with this configuration:

    Code:
    @Entity
    public class C {
    	@ManyToMany
    	private List<a> list;
    ...
    }
    the problem appears when I want to add a new element, I explain:

    Code:
    	c.getList().getElements().add(a);
    	repository.save(c);
    with the EAGER fetching I don't have this problem, but others appears.
    Another option is:

    Code:
    	aList.add(a);
    	c.setList(aList);
    	repository.save(c);
    But this produces delete and inserts.

    Thanks.
    Last edited by venosov; Mar 12th, 2012 at 02:50 AM.

  4. #4

    Default

    If you have a webapp, and you dont want the eager loading in most cases just a few, and holding the transaction open is undesirable you can look into OpenEntityManagerInViewFilter.

  5. #5
    Join Date
    May 2011
    Location
    Madrid (Spain)
    Posts
    101

    Default

    Quote Originally Posted by wgorder View Post
    If you have a webapp, and you dont want the eager loading in most cases just a few, and holding the transaction open is undesirable you can look into OpenEntityManagerInViewFilter.
    Thanks wgorder, but it isn't a webapp.

  6. #6

    Default

    Ok I will give you a couple other alternatives, this would be for the scenario where eager loading the relationship all the time is not what you want.

    Option 1-
    One common way of doing this all though it does look somewhat strange is to trigger the lazy load by accessing the field or relationship before the entity becomes detached.

    On a collection valued relationship usually calling .size() on the collection will have the desired effect. If you are not dealing with a collection say you have an Employee on your object, you would probably need to call getEmployee.getName(). This is because the entity returned from the lazy loading is actually a proxy that waits until a method is invoked on it before the entity is faulted in. So calling just getEmployee() would not have the desired effect as you would only have the proxy loaded at that point.

    Option 2-
    You could look at fetch joins to eagerly fetch your data as part of the query. If you are fetching collections you have to be careful for duplicate results. You would either need to put these in a Set to eliminate the duplicates or use the Distinct keyword. Using the distinct keyword (assuming JPQL not possible in SQL) will cause the provider to eliminate the duplicates in memory which may hurt performance for large collections so be aware of that.

    http://www.objectdb.com/java/jpa/que...ER_JOIN_FETCH_
    http://stackoverflow.com/questions/5...ing-join-fetch

    Option 3-
    If you move away from JPA and look at the vendor specific implementations you can usually do somethingn like batch fetching.

    http://docs.jboss.org/hibernate/orm/...fetching-batch

  7. #7
    Join Date
    May 2011
    Location
    Madrid (Spain)
    Posts
    101

    Default

    Thanks wgorder, I know how to resolve this with JPA (your option 1), for example:

    Code:
       public List fetchReservationsWithRelationships()
       {
          List list = manager.createQuery("FROM Reservation res").getResultList();
          for (Object obj : list)
          {
             Reservation res = (Reservation)obj;
             res.getCabins().size();
             res.getCustomers().size();
          }
          return list;
       }
    
       // instead of
    
       public List fetchReservations()
       {
          return manager.createQuery("FROM Reservation res").getResultList();
       }
    but I'd like to use Spring Data JPA, therefore, I think the solution is to have 3 entities.

  8. #8

    Default

    venosov,

    There is nothing wrong with the 3 entity approach, so I am not trying to deter you. But I am curious why you cant do that with spring data?

    Do you mean you don't want to extend your repository and provide an impl for that find, but rather just want to annotate with @query and be done with it?

    Thanks,

  9. #9
    Join Date
    May 2011
    Location
    Madrid (Spain)
    Posts
    101

    Default

    Thanks wgorder, I'm curious too, this is the objective of this thread :-). I extended my repository with the same result:

    Code:
        @Override
        @Transactional
        public void addElement(C c, A a) {
            c.getList().getElements().add(a);
            em.persist(c);
        }
    in other words, I haven't yet seen an example of Spring Data JPA that add an element in a ManyToMany relationship.

    Please, if you want more details, ask me, I think this is a great exercise.

    Thanks again.
    Last edited by venosov; Mar 12th, 2012 at 10:43 AM.

  10. #10
    Join Date
    Apr 2006
    Location
    Dresden, Germany
    Posts
    483

    Default

    The phenomenon you see here has got nothing to do with Spring Data at all but is a 101 of working with an OR mapper: accessing lazily loaded references you need to have the entity associated with a Session/EntityManager which has to be open. With Spring a Session/EntityManager is bound to the transaction scope by default. Spring Data repositories implicitly trigger transactions at their method boundaries if no transaction is in place already. Thus this is opening an EntityManager which get's closed on finishing the method call. If OpenEntityManagerInView is not an option the only reasonable way is to span the transaction more coarse grained:

    Code:
    class RepoClient {
    
      private final MyRepository repo;
    
      public RepoClient(MyRepository repo) {
        Assert.notNull(repo);
        this.repo = repo;
      }
    
      @Transactional(readOnly = true)
      public void myMethod(…) {
         Entity entity = repo.findBySomething(…);
         // access all properties of the entity here
      }
    }
    Triggering the start of the transaction will cause the repository to transparently take part in it and keep the underlying EntityManager open so that you can access all lazy properties of entity until you return from myMethod(…).

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •