即使使用@ Transactional,LazyInitializationException [英] LazyInitializationException even though @Transactional is used

查看:50
本文介绍了即使使用@ Transactional,LazyInitializationException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当前我遇到以下异常:

org.hibernate.LazyInitializationException:无法延迟初始化角色集合:com.example.model.Link.subLinks,不能初始化代理-没有会话

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.example.model.Link.subLinks, could not initialize proxy - no Session

我用谷歌搜索,并且找到了针对此异常的另一种解决方案,但我想知道为什么@Transactional在我的案例中无法正常工作.我确定我做错了什么.我在做什么错了?

I googled and I found another solution for this exception but I would like to know why @Transactional in my case is not working. I am sure that I am doing something wrong. What am I doing wrong?

奇怪的是,我已经在该项目的其他地方使用了@Transactional,并且可以正常使用.

提前感谢您的提示.

我的链接类别:实体链接包含子链接的集合.

My Link class: The entity Link contains a collection of 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;
}

我的SubLink类:

@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";
    }

推荐答案

Aman 很好地解释了您的案例中 LazyInitializationException 的原因和贪婪>添加了有关为什么 @Transactional 不能按预期工作以及应如何使用的更多信息.

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.

因此,总结一下解决问题所必须采取的措施:

So, to summarize the options you have to solve your problem:

Aman crizzis 的评论下,从性能的角度来看,这很容易,但不是最好的.

Commented by Aman and crizzis, this is the easier but not the best from the point of view of the performance.

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

To do it, include fetch = FetchType.EAGER in subLinks property definition:

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

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

However, every time you get from database instances of Link => its collection of related SubLink will be included.

也由 Aman 评论(尽管在(link.id = sublink.link.id)上不是必需的).在这种情况下,您可以在存储库中创建一个新方法,以获取特定 Link SubLink 集合.

Commented by Aman too (although is not necessary 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.

通过这种方式,您将能够处理想要接收此类额外信息"的情况.( findWithSubLinkById )或不提供( findById ).

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).

使用双向 OneToMany ,就可以满足以下要求:

With a bidirectional OneToMany, it is enough with something like:

@Query("select l from Link l left join fetch l.subLinks where l.id = :id")
Link findWithSubLinkById(Long id);

使用单向 OneToMany ,除了上述查询外,您还必须包括如何"加入两个表:

With a unidirectional OneToMany, besides the above query you have to include how "join" both tables:

@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "link_id")
private Collection<SubLink> subLinks;

SubLink 类中删除 link 属性.

休眠关联中的更多信息和单向一对多提示

这是前一个版本的变体.因此,不是使用 @Query 创建新方法,而是使用 @EntityGraph 配置一个更简单的选择:

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:

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

更多信息此处(示例77 )

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

As crizzis commented, inside the method configured with @Transactional you won't receive such exception. So, another option is move the code that uses Sublink from your controller to your service.

这篇关于即使使用@ Transactional,LazyInitializationException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆