Java JPA 延迟获取不起作用并抛出 LazyInitializationException

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/23923886/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 09:05:10  来源:igfitidea点击:

JPA lazy fetch not working and throwing LazyInitializationException

javaspringhibernatejpahibernate-mapping

提问by Prateek Jain

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.

我是 spring 数据 JPA 的新蜜蜂,所以我试图用它做一些事情,以便我可以了解获取模式,但它抛出了一个异常,请查看代码。

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 Stateand Constituencyand from Stateto Constituencyone-to-many mapping exists and vice versa there is many-to-one. Please let me know if i have made any stupid mistake here.

因此,正如您所看到的,有两个类StateConstituency,从StateConstituency存在一对多映射,反之亦然,存在多对一映射。如果我在这里犯了任何愚蠢的错误,请告诉我。

@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, constituenciesin Statewon't get initialize until next statement executes which is to invoke fetchLazyCollectionbut as fetchLazyCollectioninvokes it's throwing exception of

按照我的理解,我假设,因为这语句将执行,选区国家不会得到使用,直到下一个语句执行是调用fetchLazyCollectionfetchLazyCollection调用它抛出的异常

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.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.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access
states.get(0).fetchLazyCollection();
0(ParentRunner.java:53) at org.junit.runners.ParentRunner.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.

正如我假设的那样,Java 中的每个单元测试都维护自己的与数据库交互的会话,可能是我错了,有人能告诉我我在这里做错了什么吗?任何帮助将不胜感激。

采纳答案by Vlad Mihalcea

This is a very common question, so this answer is based on this articleI wrote on my blog.

这是一个很常见的问题,所以这个答案是基于我在博客上写的这篇文章

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

您会得到 ,LazyInitializationException因为在您退出 之后stateRepository.findByName("New Delhi"),HibernateSession将关闭,并且您无法在正在运行的 Hibernate 之外获取其他关联Session

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

添加@Transactional到您的测试将解决此问题,您将不必调用:

select s 
from State s 
left join fetch s.constituencies 
where s.name = :stateName

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

最好在原始实体查询中获取您需要的惰性关联:

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

回答by MGorgon

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.

延迟加载适用于持久性上下文。当您的交易完成后,您将无法执行延迟获取。您可以扩展您的事务范围,以便您可以在事务中获取集合,或者创建将为您返回集合的查询。另一种方法是在你的一个 stareRepository 方法中手动获取集合,例如List<State> states = stateRepository.findByNameAndFetchCollection("New Delhi");,事务应该仍然处于活动状态。

回答by Eugene

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>

  • 在加载实体的同一会话/事务中获取您的集合
  • 设置 hibernate 属性(用于 SessionFactory),它启用惰性加载的非事务性:

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

回答by Pravin

set lazy flag true to your collection mapping as following

将惰性标志设置为您的集合映射,如下所示

##代码##