簡體   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