【问题标题】:Hibernate failed to lazily initialize a collectionHibernate 无法延迟初始化集合
【发布时间】:2020-05-23 18:49:06
【问题描述】:

我在 Spring Boot 中设置了两个数据源。在控制器中,我想在两者上都保留几个父子实体。它们几乎是相同的双向关系,但第一个有效,而第二个发出提交消息但实际上并没有持久化子实体。 来自工作数据源的实体

@Entity
public class Brand {

private Integer id;

private Set<Line> lines = new HashSet<Line>(); 

@Id
@GeneratedValue(strategy=GenerationType.AUTO)
@JsonIgnore
public Integer getId() {
    return id;
}

@JsonIgnore
@OneToMany(mappedBy = "brand", cascade = CascadeType.ALL, orphanRemoval = true)
public Set<Line> getLines() {
    return lines;
}

@Entity
public class Line {

private Integer id;

private Brand brand;

@Id
@GeneratedValue(strategy=GenerationType.AUTO)
public Integer getId() {
    return id;
}

@ManyToOne
@JoinColumn(name = "brand")
public Brand getBrand() {
    return brand;
}

虽然这些来自第二个数据源,但它保留了父级而不是子级

@Entity
public class User {

private String id;

private List<UserCommit> userCommitList = new ArrayList<UserCommit>();

@Id
@GeneratedValue(strategy=GenerationType.AUTO)
public Integer getId() {
    return id;
}
}

@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
public List<UserCommit> getUserCommitList() {
    return userCommitList;
}

@Entity
public class UserCommit {

private Integer id;

private User user;

@Id
@GeneratedValue(strategy=GenerationType.AUTO)
public Integer getId() {
    return id;
}

@ManyToOne
@JoinColumn(name = "user")
public User getUser() {
    return user;
}

这是控制器的有趣部分

    Brand brand = brandService.createFrom(fsDto.getBrand());
    Line line = lineService.createFrom(fsDto.getLine(), brand);
    AccessToken accToken = token.getAccount().getKeycloakSecurityContext().getToken();

    try {

        prodService.updateFrom(id, line, fsDto);

        User user = userService.createFrom(accToken);
        ucService.createFrom(user, id, "EDIT", "FIRST");

这些是服务方法

public Brand createFrom(String name) {

    Brand foundBrand = findByName(name);
    Brand brand = new Brand();
    if (foundBrand == null) {
        brand = brandRepo.save(brand);
        brand.setName(name);
    } else
        brand = foundBrand;

    return brand;

public Line createFrom(String name, Brand brand) {

    Line foundLine = findByNameAndBrand(name, brand);
    Line line = new Line();
    if (foundLine == null) {
        line.setName(name);
        line.setBrand(brand);
        line.getBrand().addLine(line);

    } else
        line = foundLine;

    return line;
}

public User createFrom(AccessToken accToken) {

    User user = findByKcId(accToken.getSubject());

    if(user == null) {
        user = new User();
        user.setKcId(accToken.getSubject());
        user.setName(accToken.getPreferredUsername());
        userRepo.save(user);
    }

    return user;

}

public UserCommit createFrom(User user, Integer prodId, String ucType, String stage) {

    UserCommit uc = new UserCommit();
    uc.setUser(user);
    uc.getUser().addUserCommit(uc);
    uc.setProdId(prodId);
    uc.setUCType(UserCommit.UCType.valueOf(ucType));
    uc.setStage(UserCommit.Stage.valueOf(stage));
    uc.setTime(LocalDateTime.now());

    return uc;
}
  • 品牌持续存在。
  • 线路保持不变。
  • 用户被持久化。
  • UserCommit 不要被持久化。
  • 如果我明确表示一切正常 调用 UserCommitService.save(),但我认为不需要。

编辑 我更新了我的代码以使两对事务更加相似,现在对于第二个事务,我在延迟初始化时遇到了这个经典错误。

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: petmenu.entities.users.User.userCommitList, could not initialize proxy - no Session

真正让我抓狂的是 Brand→Line 有效,而 User→UserCommit 无效。即使跟踪 JPA,我也无法理解为什么 User 实体未包含在 UserCommit 提交时的会话中。

2020-05-26 16:51:54.258 DEBUG 78269 --- [http-nio-192.168.1.10-8080-exec-2] o.s.orm.jpa.JpaTransactionManager        : Found thread-bound EntityManager [SessionImpl(1767213597<open>)] for JPA transaction
2020-05-26 16:51:54.258 DEBUG 78269 --- [http-nio-192.168.1.10-8080-exec-2] o.s.orm.jpa.JpaTransactionManager        : Creating new transaction with name [petmenu.services.users.UserService.createFrom]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2020-05-26 16:51:54.259 DEBUG 78269 --- [http-nio-192.168.1.10-8080-exec-2] o.s.orm.jpa.JpaTransactionManager        : Exposing JPA transaction as JDBC [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@41ca5759]
2020-05-26 16:52:01.687 DEBUG 78269 --- [http-nio-192.168.1.10-8080-exec-2] o.s.orm.jpa.EntityManagerFactoryUtils    : Opening JPA EntityManager
2020-05-26 16:52:01.708 DEBUG 78269 --- [http-nio-192.168.1.10-8080-exec-2] org.hibernate.SQL                        : select user0_.id as id1_0_, user0_.kc_id as kc_id2_0_, user0_.name as name3_0_ from user user0_ where user0_.kc_id=?
2020-05-26 16:52:01.708 TRACE 78269 --- [http-nio-192.168.1.10-8080-exec-2] o.h.type.descriptor.sql.BasicBinder      : binding parameter [1] as [VARCHAR] - [80a3b4b1-00d1-4062-a7e5-1927b938c203]
2020-05-26 16:52:01.709 TRACE 78269 --- [http-nio-192.168.1.10-8080-exec-2] o.h.type.descriptor.sql.BasicExtractor   : extracted value ([id1_0_] : [INTEGER]) - [2005]
2020-05-26 16:52:01.709 TRACE 78269 --- [http-nio-192.168.1.10-8080-exec-2] o.h.type.descriptor.sql.BasicExtractor   : extracted value ([kc_id2_0_] : [VARCHAR]) - [80a3b4b1-00d1-4062-a7e5-1927b938c203]
2020-05-26 16:52:01.709 TRACE 78269 --- [http-nio-192.168.1.10-8080-exec-2] o.h.type.descriptor.sql.BasicExtractor   : extracted value ([name3_0_] : [VARCHAR]) - [user1]
2020-05-26 16:52:01.709 TRACE 78269 --- [http-nio-192.168.1.10-8080-exec-2] o.s.orm.jpa.JpaTransactionManager        : Triggering beforeCommit synchronization
2020-05-26 16:52:01.709 TRACE 78269 --- [http-nio-192.168.1.10-8080-exec-2] o.s.orm.jpa.JpaTransactionManager        : Triggering beforeCompletion synchronization
2020-05-26 16:52:01.709 DEBUG 78269 --- [http-nio-192.168.1.10-8080-exec-2] o.s.orm.jpa.JpaTransactionManager        : Initiating transaction commit
2020-05-26 16:52:01.709 DEBUG 78269 --- [http-nio-192.168.1.10-8080-exec-2] o.s.orm.jpa.JpaTransactionManager        : Committing JPA transaction on EntityManager [SessionImpl(1767213597<open>)]
2020-05-26 16:53:06.630 TRACE 78269 --- [http-nio-192.168.1.10-8080-exec-2] o.s.orm.jpa.JpaTransactionManager        : Triggering afterCommit synchronization
2020-05-26 16:53:06.671 TRACE 78269 --- [http-nio-192.168.1.10-8080-exec-2] o.s.orm.jpa.JpaTransactionManager        : Triggering afterCompletion synchronization
2020-05-26 16:53:06.671 DEBUG 78269 --- [http-nio-192.168.1.10-8080-exec-2] o.s.orm.jpa.JpaTransactionManager        : Not closing pre-bound JPA EntityManager after transaction
2020-05-26 16:53:06.671 DEBUG 78269 --- [http-nio-192.168.1.10-8080-exec-2] o.s.orm.jpa.JpaTransactionManager        : Found thread-bound EntityManager [SessionImpl(1767213597<open>)] for JPA transaction
2020-05-26 16:53:06.671 DEBUG 78269 --- [http-nio-192.168.1.10-8080-exec-2] o.s.orm.jpa.JpaTransactionManager        : Creating new transaction with name [petmenu.services.users.UserCommitService.createFrom]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2020-05-26 16:53:06.671 DEBUG 78269 --- [http-nio-192.168.1.10-8080-exec-2] o.s.orm.jpa.JpaTransactionManager        : Exposing JPA transaction as JDBC [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@323b85aa]
2020-05-26 16:53:17.438 TRACE 78269 --- [http-nio-192.168.1.10-8080-exec-2] o.s.orm.jpa.JpaTransactionManager        : Triggering beforeCompletion synchronization
2020-05-26 16:53:17.472 DEBUG 78269 --- [http-nio-192.168.1.10-8080-exec-2] o.s.orm.jpa.JpaTransactionManager        : Initiating transaction rollback
2020-05-26 16:53:17.472 DEBUG 78269 --- [http-nio-192.168.1.10-8080-exec-2] o.s.orm.jpa.JpaTransactionManager        : Rolling back JPA transaction on EntityManager [SessionImpl(1767213597<open>)]
2020-05-26 16:53:17.472 TRACE 78269 --- [http-nio-192.168.1.10-8080-exec-2] o.s.orm.jpa.JpaTransactionManager        : Triggering afterCompletion synchronization
2020-05-26 16:53:17.472 DEBUG 78269 --- [http-nio-192.168.1.10-8080-exec-2] o.s.orm.jpa.JpaTransactionManager        : Not closing pre-bound JPA EntityManager after transaction
2020-05-26 16:53:17.473 DEBUG 78269 --- [http-nio-192.168.1.10-8080-exec-2] o.j.s.OpenEntityManagerInViewInterceptor : Closing JPA EntityManager in OpenEntityManagerInViewInterceptor
2020-05-26 16:53:17.496 ERROR 78269 --- [http-nio-192.168.1.10-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: petmenu.entities.users.User.userCommitList, could not initialize proxy - no Session] with root cause 

【问题讨论】:

  • 从未与实体合作过,但看起来您保存用户而不是添加提交。用户 createFrom(AccessToken accToken) --> 保存用户 UserCommit createFrom --> 创建用户提交 您是否尝试添加提交然后再次保存用户以查看是否是这种情况?
  • 是的。我对 Brand 和 Line 做同样的事情,而且效果很好。

标签: hibernate spring-boot jpa persistence mariadb-10.3


【解决方案1】:

我研究了很多关于事务管理等的东西,我(希望)找到了罪魁祸首。 但谢天谢地,每个更正都被接受了

LSS

似乎 Spring Boot 注册 OpenEntityManagerInViewInterceptor 只是用于主要数据源(我在网上找到了一些关于此的信息,但在官方文档中没有找到)。

TL:DR

将会话绑定到整个视图并加载热切管理实体的集合,当运行LineService.createFrom Brand 的集合时已经持久化,然后新添加的 Line 在事务结束时被刷新。 在没有视图会话的对应对象上,我在 UserCommitService.createFrom 事务中没有托管集合,然后在没有显式调用 UserCommitRepo.save 的情况下,UserCommit 将保持暂时状态,并且将在事务提交时从刷新中静默丢弃。

我不知道我是否正确理解了所有内容,如果是这样,我希望有人解释CascadeType.PERSIST(包含在CascadeType.ALL 中)在双向的@OneToMany 一侧的意义何在 关系。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-12-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-30
    • 2020-05-10
    • 2021-03-13
    • 1970-01-01
    相关资源
    最近更新 更多