[英]LazyInitializationException even though @Transactional is used
Currently I am getting the following exception:目前我收到以下异常:
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.example.model.Link.subLinks, could not initialize proxy - no Session
org.hibernate.LazyInitializationException:无法延迟初始化角色集合:com.example.model.Link.subLinks,无法初始化代理 - 没有会话
I googled and I found another solution for this exception but I would like to know why @Transactional in my case is not working.我用谷歌搜索并找到了针对此异常的另一个解决方案,但我想知道为什么 @Transactional 在我的情况下不起作用。 I am sure that I am doing something wrong.
我确定我做错了什么。 What am I doing wrong?
我究竟做错了什么?
The weird part is that I used @Transactional already somewhere else in this project and there it works.奇怪的是,我已经在这个项目的其他地方使用了 @Transactional,并且它在那里工作。
Thank you in advance for any tips.预先感谢您提供任何提示。
My Link class: The entity Link contains a collection of SubLink. My Link 类:实体Link 包含SubLink 的集合。
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Entity
public class Link {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@NotEmpty
private String title;
@NotEmpty
private String description;
@OneToMany(mappedBy = "link")
private Collection<SubLink> subLinks;
}
My SubLink class:我的子链接类:
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Entity
public class SubLink {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@NotEmpty
private String type;
@NotEmpty
@URL
private String url;
@ManyToOne
@JoinColumn(name = "link_id", referencedColumnName = "id")
private Link link;
}
My LinkServiceImpl class:我的 LinkServiceImpl 类:
@Service
public class LinkServiceImpl implements LinkService {
@Autowired
public LinkRepository linkRepository;
@Override
@Transactional
public Link findById(long id) {
return linkRepository.findById(id);
}
}
In my controller class there is the method showLink():在我的控制器类中有方法 showLink():
@GetMapping("/link/{linkId}")
public String showLink(@PathVariable(value = "linkId") long linkId, Model model) {
Link link = linkService.findById(linkId);
Collection<SubLink> subLinkCollection = link.getSubLinks(); //Error
model.addAttribute("subLinkCollection", subLinkCollection);
return "link";
}
LazyInitializationException
indicates access to unfetched data outside of a session context. LazyInitializationException
表示访问会话上下文之外未提取的数据。 To fix this, you have to fetch all required associations within your service layer.要解决此问题,您必须在服务层中获取所有必需的关联。
I think in your case you are fetching Link
without its SubLink
, but trying to access sublinks.我认为在您的情况下,您是在没有
SubLink
情况下获取Link
,而是尝试访问子Link
。 So, using JOIN FETCH would be the best strategy.因此,使用JOIN FETCH将是最好的策略。
Note: You can use FetchType.EAGER
too, but it might affect performance.注意:您也可以使用
FetchType.EAGER
,但它可能会影响性能。 Because, it will always fetch fetch the association, even if you don't use them.因为,即使您不使用它们,它也会始终获取关联。
= UPDATE = = 更新 =
Thanks to crizzi for mentioning an interesting feature called Open Session In View(OSIV) (enabled by default in spring-boot applications).感谢crizzi提到一个有趣的特性,叫做Open Session In View(OSIV) (在 spring-boot 应用程序中默认启用)。 Though it depends on who you may ask to label it as a pattern or an antipattern .
尽管这取决于您可能要求谁将其标记为模式或反模式。
The main purpose is to to facilitate working with lazy associations(avoids LazyInitializationException
).主要目的是促进使用惰性关联(避免
LazyInitializationException
)。 Very detailed explanation can be found on非常详细的解释可以在
Aman explained well the cause of the LazyInitializationException
in your case and crizzis added more information about why @Transactional
is not worked as you expected and how it should be used. Aman在您的案例中很好地解释了
LazyInitializationException
的原因,并且crizzis添加了更多关于为什么@Transactional
没有按您的预期工作以及如何使用它的信息。
So, to summarize the options you have to solve your problem:因此,总结一下解决问题的选项:
Commented by Aman and crizzis , this is the easier but not the best from the point of view of the performance. Aman和crizzis评论说,从性能的角度来看,这是更容易但不是最好的。
To do it, include fetch = FetchType.EAGER
in subLinks
property definition:为此,请在
subLinks
属性定义中包含fetch = FetchType.EAGER
:
@OneToMany(mappedBy = "link", fetch = FetchType.EAGER)
private Collection<SubLink> subLinks;
However, every time you get from database instances of Link
=> its collection of related SubLink
will be included.但是,每次您从
Link
=> 的数据库实例中获取时,其相关SubLink
集合SubLink
将被包含在内。
Commented by Aman too (although is not necessary on (link.id = sublink.link.id)
). Aman也发表了评论(尽管
on (link.id = sublink.link.id)
没有必要)。 In this case, you can create a new method in your repository to get the SubLink
collection of an specific Link
.在这种情况下,您可以在存储库中创建一个新方法来获取特定
Link
的SubLink
集合。
In this way, you will be able to deal with situations on which you want to receive such "extra information" ( findWithSubLinkById
) or not (provided findById
).通过这种方式,您将能够处理您想要接收此类“额外信息”(
findWithSubLinkById
)或不接收(提供findById
)的情况。
With a bidirectional OneToMany
, it is enough with something like:使用双向
OneToMany
,使用以下内容就足够了:
@Query("select l from Link l left join fetch l.subLinks where l.id = :id")
Link findWithSubLinkById(Long id);
With a unidirectional OneToMany
, besides the above query you have to include how "join" both tables:使用单向
OneToMany
,除了上述查询之外,您还必须包括如何“连接”两个表:
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "link_id")
private Collection<SubLink> subLinks;
Removing link
property from SubLink
class.从
SubLink
类中删除link
属性。
More information in Hibernate associations and unidirectional one-to-many tips Hibernate 关联和单向一对多提示中的更多信息
This is a variation of the previous one.这是上一个的变体。 So, instead of create the new method using
@Query
, an easier choice will be configured with @EntityGraph
:因此,而不是使用创建新的方法
@Query
,更容易选择将与配置@EntityGraph
:
@EntityGraph(attributePaths = "subLinks")
Link findWithSubLinkById(Long id);
More information here ( Example 77
)更多信息在这里(
Example 77
)
As crizzis commented, inside the method configured with @Transactional
you won't receive such exception.正如crizzis评论的那样,在使用
@Transactional
配置的方法中,您不会收到此类异常。 So, another option is move the code that uses Sublink
from your controller to your service.因此,另一种选择是将使用
Sublink
的代码从您的控制器移动到您的服务中。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.