Abhilash L L
Abhilash L L

Reputation: 366

Understanding transaction session with lazy loading in Spring JPA Hibernate

I would like to get some clarification regarding lazy loading and session boundaries etc.

My code structure is as follows

@Entity

class A {

....

  @OneToOne(fetch=LAZY)
  private B b;

  ..
}

@Entity
class B {

 private id;

 private name;    

}

@Transactional(SUPPORTS)
ADao {
  A findById(int id);  
}

@Transactional(SUPPORTS)
LayerDB {

    A getAForId(int i) {
      return adao.findById(i);
    }

}

//Note that there is no transactional attribute here
LayerB {

   public boolean doSomethingWithAandB(int aId) {
    A a = LayerDB.getAForId(aId);
    if(a.getB().getName().equals("HIGH"))
     return true;
    return false;
   }

}

//start transaction here
@Transaction(REQUIRED)
LayerC {

    LayerB layerb;

    private handleRequest(int id) {

       layerb.doSomethingWithAandB(id);

    }
}

Now when we try to access B in entity A within the method

doSomethingWithAandB

Am getting a lazy initialization exception when trying to access B.

Even though the method is within the transaction created in LayerC, still i get the following exception

Exception : org.hibernate.LazyInitializationException: could not initialize proxy - no Session

But on changing the following two methods as :

@Transactional(SUPPORTS)
LayerDB {

   A getAForId(int i) {
      A a = adao.findById(i);
      a.getB().getName();
      return a;
    }

}

//Note that there is no transactional attribute here
LayerB {

   public boolean doSomethingWithAandB(int aId) {
     A a = LayerDB.getAForId(aId);
     if(a.getB().getName().equals("HIGH"))
     return true;
    return false;
  }

}

Why is it not using the transaction / session created in LayerC ?

Even though we have SUPPORTS on the DBLayer, is it creating a separate 'session'.

Any pointers for proper understand would help me a great deal.

Thank you.

Upvotes: 10

Views: 30291

Answers (2)

wallenborn
wallenborn

Reputation: 4273

With lazy loading, when you request an object a of type A, you get an object a of type A. a.getB() however, will not be of type B, instead a.getB() is a proxy for B that can be resolved later on (that's the lazy loading part), but only in the persistence context in which a lives in.

Your second implementation does just that: it resolves B by calling a.getB().getName() while you are still in the @Transaction. Hibernate can now make a second request to the database to fetch B, and now a.getB() is really of type B and stays that way, so you can use it outside the persistence context.

Your first implementation skips that. A is fetched from the database, the @Transactional block ends, then you call a.getB().getName(), but now the persistence context is gone, a.getB() can not be fetched from the database, and an exception is thrown.

Upvotes: 25

vadimv82
vadimv82

Reputation: 93

What will happen if you add @Transactional(SUPPORTS) to LayerB ? Propagation SUPPORT means that method joins the caller’s transaction. By idea it will join the same transation LayerC created. And since LayerDB's getAForId method runs within same transaction that means they have same persitance context there should not be problem with fetching B name. I am just guessing.

Upvotes: 1

Related Questions