繁体   English   中英

带有 JPA 实体的 Spring 中的 LazyInitializationException

[英]LazyInitializationException in Spring with JPA entity

我想在我的 JPA 实体上从 EAGER 切换到 LAZY 获取,但随后出现错误。 我已经尝试了很多解决方案,并且现在一直在寻找一个可行的解决方案 - 在 Google 上找不到任何适用的解决方案......而且我也不希望实体加入 fetch。

错误:org.hibernate.LazyInitializationException:无法延迟初始化角色集合:package.User.wallets,无法初始化代理 - 没有 Z71C7AE294B7ABD866B3FB295B3ZB

网络控制器文件:

@Controller
public class AppController {

    @GetMapping("app/")
    public ModelAndView app() {

        ModelAndView mv = new ModelAndView();
        mv.setViewName("app/index");

        User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();

        List<Wallet> wallets = user.getWallets(); // error on this line
        mv.addObject("wallets", wallets);

        return mv;
    }
}

用户文件:

@Entity
public class User {

    @Id
    @GeneratedValue
    @Type(type="uuid-char")
    private UUID id;

    @Column(nullable = false)
    private String email;

    @Column(nullable = false)
    private String firstName;

    @Column(nullable = false)
    private String lastName;

    @Column(nullable = false)
    private String password;

    // FetchType.EAGER solves the error, but I can't have EAGER due to performance issues.
    @OneToMany(mappedBy="user", fetch = FetchType.LAZY)
    @Fetch(value = FetchMode.SUBSELECT)
    private List<Wallet> wallets;

    // ** getters and setters **

}

@Repository
public interface UserRepo extends JpaRepository<User, UUID> {

    Optional<User> findByEmail(String email);

}

钱包文件:

@Entity
public class Wallet {

    @Id
    @GeneratedValue
    @Type(type="uuid-char")
    private UUID id;

    @ManyToOne
    @JoinColumn(name="user_id", nullable=false)
    private User user;

    @OneToOne
    @JoinColumn(name="currency_id", nullable=false)
    private Currency currency;

    @Column(nullable = false)
    private double balance;

    // ** getters and setters **

}

@Repository
public interface WalletRepo extends JpaRepository<Wallet, UUID> {

    Optional<Wallet> findByUserAndCurrency(User user, Currency currency);

}

感谢您阅读所有这些,我将非常感谢任何回复。

你的问题的根源:

By default hibernate lazily loads the collections (relationships) which means whenver you use the collection in your code the hibernate gets that from database, now the problem is that you are getting the collection in your controller (where the JPA session is closed).This是导致异常的代码行(您正在加载评论集合):

List<Wallet> wallets = user.getWallets();

解决方案

你在你的用户实体上有这个

 @OneToMany(mappedBy="user", fetch = FetchType.LAZY)
    @Fetch(value = FetchMode.SUBSELECT)
    private List<Wallet> wallets;

用这个

@OneToMany(fetch = FetchType.EAGER, mappedBy = "user", cascade = CascadeType.ALL)
private List<Wallet> wallets;

Collections 默认是延迟加载的

更新的解决方案

这种特殊情况将通过在User中添加 toString() 以重新定义 Lombok toString() 来解决

保留您的fetchType.LAZY而不是fetchType.EAGER并将 toString 方法添加到您的用户实体

@Override
public String toString() {
        return "User [id=" + id + ", email=" + email+ ", firstName=" + firstName+ ", lastName=" + lastName + ", password=" + password+ "]";
    }

并添加到您的钱包实体:

  @ManyToOne(fetch=FetchType.EAGER)
  @JoinColumn(name="user_id", nullable=false)
  private User user;

尝试将@Transactional添加到您的 controller

您必须在AppController class 上方添加org.springframework.transaction.annotation.Transactional ,然后延迟加载将起作用。

例子:

import org.springframework.transaction.annotation.Transactional;

@Controller
@Transactional
public class AppController {

    @GetMapping("app/")
    public ModelAndView app() {

        ModelAndView mv = new ModelAndView();
        mv.setViewName("app/index");

        User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();

        List<Wallet> wallets = user.getWallets(); // error on this line
        mv.addObject("wallets", wallets);

        return mv;
    }
}

如果它不起作用,您可能没有启用事务管理(这通常由 Spring Boot auto configuration 配置)。 尝试在主应用程序 class 上方添加@EnableTransactionManagement ,例如使用@SpringBootApplication注释。

你也可以试试下面的那一行:

Hibernate.initialize(user.getWallets());

这是因为 SecurityContextHolder.getContext().getAuthentication().getPrincipal() 中的用户 object 已经关闭了连接,即使在 Controller ZA2F2ED4F8EBC2CBB14C21A29DC40 中注释了@Transaction。

我不得不像这样更改 Controller class :

    @GetMapping("app/")
    public ModelAndView app() {

        ModelAndView mv = new ModelAndView();
        mv.setViewName("app/index");

        UserDetails userDetails = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        User user = userRepo.findByUsername(userDetails.getUsername()).orElse(null);

        List<Wallet> wallets = user.getWallets(); // no error anymore
        mv.addObject("wallets", wallets);

        return mv;
    }

希望这可以帮助其他人,因为我在任何地方都找不到这个错误。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM