简体   繁体   English

LazyInitializationException 即使使用了 @Transactional

[英]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:因此,总结一下解决问题的选项:


Fetch Eager获取渴望

Commented by Aman and crizzis , this is the easier but not the best from the point of view of the performance. Amancrizzis评论,从性能的角度来看,这是更容易但不是最好的。

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将被包含在内。


Custom Query自定义查询

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 .在这种情况下,您可以在存储库中创建一个新方法来获取特定LinkSubLink集合。

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 关联单向一对多提示中的更多信息


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 :因此,而不是使用创建新的方法@Query ,更容易选择将与配置@EntityGraph

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

More information here ( Example 77 )更多信息在这里Example 77


Work inside Transactional method在事务方法中工作

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.

相关问题 LazyInitializationException 即使使用@Transactional - LazyInitializationException even with @Transactional @Transactional 上的 Spring Hibernate LazyInitializationException - Spring Hibernate LazyInitializationException on @Transactional 如何使用@Transactional 修复 LazyInitializationException? - How to fix LazyInitializationException with @Transactional? @Transactional方法之前创建的休眠对象不能在之后使用(获取LazyInitializationException) - Hibernate object created before @Transactional method can't be used after (gets LazyInitializationException) 我试图理解 LazyInitializationException 和 @Transactional - I'm trying to understand LazyInitializationException and @Transactional @Transactional注释Spring Boot 2.0和休眠LazyInitializationException - @Transactional annotation Spring boot 2.0 and hibernate LazyInitializationException Java方法即使不使用也需要参数 - Java method requires argument even though it is not used 即使没有使用服务,也会出现泄漏窗口异常 - Leaked window exception even though no service is used 即使使用了Set,也会在Array中重复 - Duplicates in Array even though a Set was used 为什么eclipse说即使使用局部变量也不使用? - Why does eclipse say Local variable is not used even though it is used?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM