Java LazyInitializationException,即使使用了@Transactional

Java LazyInitializationException,即使使用了@Transactional,java,spring,spring-boot,hibernate,Java,Spring,Spring Boot,Hibernate,目前我遇到以下异常: org.hibernate.LazyInitializationException:延迟初始化失败 未能找到角色集合:com.example.model.Link.subLinks 初始化代理-无会话 我在谷歌上搜索了一下,找到了另一个解决这个异常的方法,但我想知道为什么在我的案例中@Transactional不起作用。我确信我做错了什么。我做错了什么 奇怪的是,我已经在这个项目的其他地方使用了@Transactional,并且它在那里工作。 提前感谢您提供的任何提示 我的

目前我遇到以下异常:

org.hibernate.LazyInitializationException:延迟初始化失败 未能找到角色集合:com.example.model.Link.subLinks 初始化代理-无会话

我在谷歌上搜索了一下,找到了另一个解决这个异常的方法,但我想知道为什么在我的案例中@Transactional不起作用。我确信我做错了什么。我做错了什么

奇怪的是,我已经在这个项目的其他地方使用了@Transactional,并且它在那里工作。

提前感谢您提供的任何提示

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

@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;
}
我的LinkServiceImpl类:

@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;
}
    @Service
    public class LinkServiceImpl implements LinkService {
        @Autowired
        public LinkRepository linkRepository;
        
        @Override
        @Transactional
        public Link findById(long id) {
            return linkRepository.findById(id);
        }
    }
    @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";
    }
在我的控制器类中有showLink()方法:

@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;
}
    @Service
    public class LinkServiceImpl implements LinkService {
        @Autowired
        public LinkRepository linkRepository;
        
        @Override
        @Transactional
        public Link findById(long id) {
            return linkRepository.findById(id);
        }
    }
    @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";
    }
@GetMapping(“/link/{linkId}”)
公共字符串showLink(@PathVariable(value=“linkId”)长linkId,模型){
Link=linkService.findById(linkId);
Collection subLinkCollection=link.getSubLinks();//错误
model.addAttribute(“子链接集合”,子链接集合);
返回“链接”;
}

LazyInitializationException
表示在会话上下文之外访问未蚀刻的数据。要解决这个问题,您必须获取服务层中所有必需的关联

我认为在您的例子中,您正在获取
链接
,而没有其
子链接
,但尝试访问子链接。因此,使用JOIN-FETCH将是最好的策略

注意:您也可以使用
FetchType.EAGER
,但这可能会影响性能。因为,它将始终获取关联,即使您不使用它们

=更新=

感谢crizzi提到了一个有趣的特性,名为opensessioninview(OSIV)(在spring引导应用程序中默认启用)。尽管这取决于您可能要求谁将其标记为模式或反模式

主要目的是为了方便使用惰性关联(避免
lazyiinitializationexception
)。非常详细的解释可以在


Aman很好地解释了您案例中出现的
懒散初始化异常的原因,并且crizzis添加了更多关于
@Transactional
未按您预期工作的原因以及应如何使用的信息

因此,总结一下解决问题的方法:


迫不及待 由Amancrizzis评论,从性能角度来看,这是比较容易的,但不是最好的

要执行此操作,请在
子链接
属性定义中包括
fetch=FetchType.EAGER

@OneToMany(mappedBy = "link", fetch = FetchType.EAGER)
private Collection<SubLink> subLinks;
对于单向
OneToMany
,除了上述查询外,还必须包括如何“连接”两个表:

@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "link_id")
private Collection<SubLink> subLinks;
更多信息(
示例77


在事务方法内部工作
正如crizzis所评论的,在配置了
@Transactional
的方法中,您不会收到此类异常。因此,另一个选项是将使用
子链接的代码从控制器移动到服务中。

您好,谢谢您的帮助。事实上,为了解决这个问题,我正在使用JOIN,但我想知道为什么“@Transactional”不起作用@“事务性”应该做类似的事情,还是我错了?事务是一系列被视为单个工作单元的操作。例如,在事务中,您可以获取并更新实体。如果其中一个操作失败,则认为这两个操作都失败。@tqnone您说您正在使用JOIN。它是JOIN还是JOIN FETCH?@tqnone您将
@Transactional
放在
findById
之上,这意味着为执行
findById
打开一个持久性上下文,并为执行
findById
创建一个事务。现在,如果从
findById
内部调用
link.getSublinks()
,则不会出现错误。但是,您的代码正在
findById
外部调用
link.getSublinks()
,其中持久性上下文已经关闭。我希望事情会好转谢谢你们。我明白了!:-)@[crizzis,Aman,doctore]请提供链接实体结构好吗?这将有助于理解。嗨@SebinThomas,我添加了Link类。谢谢默认情况下,如果您没有提到LAZY,fetchType已经是EAGER。您的子链接类是否包含任何惰性属性?我也添加了子链接类。但要回答您的问题:我想您在存储库中没有使用过查询或查询方法吗?如果有疑问,您是否尝试过@Aman建议?如果按Id查找,那么使用查询方法的原因是什么?您可以使用JpaRepository ie提供的默认方法findById。我只是想问问你。非常感谢你抽出时间。我得到了它!