繁体   English   中英

LazyInitializationException 即使使用了 @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没有按您的预期工作以及如何使用它的信息。

因此,总结一下解决问题的选项:


获取渴望

Amancrizzis评论,从性能的角度来看,这是更容易但不是最好的。

为此,请在subLinks属性定义中包含fetch = FetchType.EAGER

@OneToMany(mappedBy = "link", fetch = FetchType.EAGER)
private Collection<SubLink> subLinks;

但是,每次您从Link => 的数据库实例中获取时,其相关SubLink集合SubLink将被包含在内。


自定义查询

Aman也发表了评论(尽管on (link.id = sublink.link.id)没有必要)。 在这种情况下,您可以在存储库中创建一个新方法来获取特定LinkSubLink集合。

通过这种方式,您将能够处理您想要接收此类“额外信息”( 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属性。

Hibernate 关联单向一对多提示中的更多信息


实体图

这是上一个的变体。 因此,而不是使用创建新的方法@Query ,更容易选择将与配置@EntityGraph

@EntityGraph(attributePaths = "subLinks")
Link findWithSubLinkById(Long id);

更多信息在这里Example 77


在事务方法中工作

正如crizzis评论的那样,在使用@Transactional配置的方法中,您不会收到此类异常。 因此,另一种选择是将使用Sublink的代码从您的控制器移动到您的服务中。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM