5

i am a new bee to spring data JPA, so i was trying to make something out of it, so that i can know about fetching Modes, but it's throwing a exception, Please make a look at the code.

public class State {
  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  private Integer id;

  String name;

  @OneToMany(mappedBy = "state", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
  Set<Constituency> constituencies ;

  public void fetchLazyCollection() {
    getConstituencies().size();
  }
}

public class Constituency {
  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  private Integer id;

  String name;

  @ManyToOne
  @JoinColumn(name = "state_id")
  State state;
}

So, as you can see there are two classes State and Constituency and from State to Constituency one-to-many mapping exists and vice versa there is many-to-one. Please let me know if i have made any stupid mistake here.

@Test
public void delhiShouldHaveTwoConstituency(){
  State delhi = new State();
  delhi.setName("New Delhi");

  Constituency northWest = new Constituency();
  northWest.setName("North West");
  northWest.setState(delhi);

  Constituency southDelhi = new Constituency();
  southDelhi.setName("South Delhi");
  southDelhi.setState(delhi);

  Set<Constituency> constituencies = new HashSet<Constituency>();
  constituencies.add(northWest);
  constituencies.add(southDelhi);

  delhi.setConstituencies(constituencies);

  stateRepository.save(delhi);

  List<State> states= stateRepository.findByName("New Delhi");
  states.get(0).fetchLazyCollection();
  assertThat(delhi.getConstituencies().size(), is(2));
}

Now, i have a test which is saving a state with two of its constituencies and then i am trying to retrieve them again using :

List<State> states= stateRepository.findByName("New Delhi");

According to my understanding, i am assuming as this statement will execute, constituencies in State won't get initialize until next statement executes which is to invoke fetchLazyCollection but as fetchLazyCollection invokes it's throwing exception of

could not initialize Proxy - No Session

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.prateekj.model.State.constituencies, could not initialize proxy - no Session
at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:567)
at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:187)
at org.hibernate.collection.internal.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:138)
at org.hibernate.collection.internal.PersistentSet.size(PersistentSet.java:156)
at com.prateekj.model.State.fetchLazyCollection(State.java:35)
at com.prateekj.repositories.StateRepositoryTest.shouldDoIt(StateRepositoryTest.java:53)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:83)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:88)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174)
at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:65)

as i am assuming, each unit tests in java maintain its own session of interaction with database, may be i am wrong, can anyone please tell me what wrong i have done here ? Any help will be appreciated.

4 Answers 4

13

You get the LazyInitializationException because after you get out of the stateRepository.findByName("New Delhi"), the Hibernate Session gets closed, and you are not able to fetch additional associations outside of a running Hibernate Session.

Adding @Transactional to your test will fix this issue and you won't have to call:

states.get(0).fetchLazyCollection();

It's best to fetch the lazy associations you need in the original entity query:

select s 
from State s 
left join fetch s.constituencies 
where s.name = :stateName
Sign up to request clarification or add additional context in comments.

7 Comments

thanks bro, it worked, a little doubt about it, as you said findByName was the method responsible to open/close session, so on the behalf of your statement, i made a StateService object having @Transactional at Class level, it has a method to fetchWithCollection inside whose i am retrieving State and its collection which is in single transaction now,and i am calling this method of service from my test, but it didn't work and same exception occur this time too, Isn't it a single Transaction now ?
Yes it should. Add your new code to your questions to be verified.
` @Service @Transactional public class StateService { @Autowired private StateRepository stateRepository; public List<State> getStateWithCollection(String name){ List<State> states = stateRepository.findByName(name); for (State state : states) { state.fetchLazyCollection(); } return states; } } ` this is the service that i was talking about.
` stateRepository.save(delhi); List<State> states= stateService.getStateWithCollection("New Delhi"); assertThat(states.get(0).getConstituencies().size(), is(2)); ` and the part of the test that is interacting with DB is here. sorry, couldn't make the code to be formatted in the comment section.
Check that Spring is aware of your transaction annotations: tx:annotation-driven.
|
1

Lazy loading works in persistence context. When your transaction has finished, you can't perform lazy fetch. You can extend your transaction scope, so you can fetch collection within transaction, or create query which will return collection for you. Another way is to manually fetch collection in one of your stareRepository methods, for example List<State> states = stateRepository.findByNameAndFetchCollection("New Delhi");, where transaction should be still active.

Comments

1

set lazy flag true to your collection mapping as following

@OneToMany(mappedBy = "objectId", cascade = CascadeType.ALL, orphanRemoval = true , fetch = FetchType.EAGER)

Comments

0

This is because you fetch lazy collection outside of transaction (session). So you have got two ways:

  • Fetch your collection in same session/transaction that loads entity
  • Set hibernate property (for SessionFactory) which enables non transactional loading of lazies:

    <prop key="hibernate.enable_lazy_load_no_trans">true</prop>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.