[英]LazyInitializationException even with @Transactional
[英]LazyInitializationException even though @Transactional is used
目前我收到以下异常:
org.hibernate.LazyInitializationException:无法延迟初始化角色集合:com.example.model.Link.subLinks,无法初始化代理 - 没有会话
我用谷歌搜索并找到了针对此异常的另一个解决方案,但我想知道为什么 @Transactional 在我的情况下不起作用。 我确定我做错了什么。 我究竟做错了什么?
奇怪的是,我已经在这个项目的其他地方使用了 @Transactional,并且它在那里工作。
预先感谢您提供任何提示。
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;
}
我的子链接类:
@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;
}
我的 LinkServiceImpl 类:
@Service
public class LinkServiceImpl implements LinkService {
@Autowired
public LinkRepository linkRepository;
@Override
@Transactional
public Link findById(long id) {
return linkRepository.findById(id);
}
}
在我的控制器类中有方法 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
表示访问会话上下文之外未提取的数据。 要解决此问题,您必须在服务层中获取所有必需的关联。
我认为在您的情况下,您是在没有SubLink
情况下获取Link
,而是尝试访问子Link
。 因此,使用JOIN FETCH将是最好的策略。
注意:您也可以使用FetchType.EAGER
,但它可能会影响性能。 因为,即使您不使用它们,它也会始终获取关联。
= 更新 =
感谢crizzi提到一个有趣的特性,叫做Open Session In View(OSIV) (在 spring-boot 应用程序中默认启用)。 尽管这取决于您可能要求谁将其标记为模式或反模式。
主要目的是促进使用惰性关联(避免LazyInitializationException
)。 非常详细的解释可以在
Aman在您的案例中很好地解释了LazyInitializationException
的原因,并且crizzis添加了更多关于为什么@Transactional
没有按您的预期工作以及如何使用它的信息。
因此,总结一下解决问题的选项:
Aman和crizzis评论说,从性能的角度来看,这是更容易但不是最好的。
为此,请在subLinks
属性定义中包含fetch = FetchType.EAGER
:
@OneToMany(mappedBy = "link", fetch = FetchType.EAGER)
private Collection<SubLink> subLinks;
但是,每次您从Link
=> 的数据库实例中获取时,其相关SubLink
集合SubLink
将被包含在内。
Aman也发表了评论(尽管on (link.id = sublink.link.id)
没有必要)。 在这种情况下,您可以在存储库中创建一个新方法来获取特定Link
的SubLink
集合。
通过这种方式,您将能够处理您想要接收此类“额外信息”( findWithSubLinkById
)或不接收(提供findById
)的情况。
使用双向OneToMany
,使用以下内容就足够了:
@Query("select l from Link l left join fetch l.subLinks where l.id = :id")
Link findWithSubLinkById(Long id);
使用单向OneToMany
,除了上述查询之外,您还必须包括如何“连接”两个表:
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "link_id")
private Collection<SubLink> subLinks;
从SubLink
类中删除link
属性。
这是上一个的变体。 因此,而不是使用创建新的方法@Query
,更容易选择将与配置@EntityGraph
:
@EntityGraph(attributePaths = "subLinks")
Link findWithSubLinkById(Long id);
更多信息在这里( Example 77
)
正如crizzis评论的那样,在使用@Transactional
配置的方法中,您不会收到此类异常。 因此,另一种选择是将使用Sublink
的代码从您的控制器移动到您的服务中。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.