![](/img/trans.png)
[英]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.