【问题标题】:Hibernate LAZY loading and spring's UserDetailsHibernate LAZY 加载和 spring 的 UserDetails
【发布时间】:2021-01-16 17:49:15
【问题描述】:

我有一个无状态的 REST 后端。所以没有 HTML 视图。只是 JSON 和 REST 端点。 使用 Json Web 令牌完成身份验证。客户端在每个请求中发送一个 JWT。 我的后端从这个 JWT 中的主题声明中获取用户的电子邮件。然后它从class LiquidoUserDetailsService implements UserDetailsService { ...}的数据库中加载UserModel

每个用户都是团队的一部分。但是团队是一个很大的实体,里面有很多信息。所以团队只会在必要时延迟加载:

UserModel.java

@Entity
@Table(name = "users")
public class UserModel extends BaseModel {
  @NotNull
  @NonNull
  @Column(unique = true)
  public String email;
  @ManyToOne(fetch = FetchType.LAZY)  // only load team info (with all info) if required
  public TeamModel team;

  [...]
}

现在我有一个服务应该返回当前用户的团队:

TeamService.java

@PreAuthorize(HAS_ROLE_USER)
@RequestMapping("/getOwnTeam")
@Transactional                          // [1] 
public TeamModel getOwnTeam() {
  // Get currently logged in user (that was loaded with info from JWT)
  Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
  LiquidoAuthUser authUser = (LiquidoAuthUser)authentication.getPrincipal();
  // LiquidoAuthUser is the Adapter betwen spring's User and my USerModel
  UserModel currentUser = authUser.getLiquidoUserModel()    

  TeamModel team = currentUser.getTeam()     // <=== [2] throws LazyInitializationException

  return team
}

现在我想我知道问题出在哪里了。但我还没有一个干净的解决方案。

我的 UserModel 已加载到 class LiquidoUserDetailsService implements UserDetailsService 中,但这很早就发生在过滤器中,当 HTTP 请求被处理时。因为它接缝了我的TeamService 课程中的@Transaction,那时还没有开始。

然后当代码进入getOwnTeam() 方法时,一个new 事务被启动[1]。但是在那里我不能再懒加载用户的团队了。 [2]

我如何为我的用户和团队建模,以便

  1. 团队数据仅在必要时加载
  2. 可以在必要时手动加载数据

【问题讨论】:

  • 您能确认 TransactionSynchronizationManager.isActualTransactionActive() 在 @Transactional 块中返回 true 吗?
  • 另外,您是否有理由在该方法中使用@Transactional 注释?我认为没有必要
  • REST 控制器方法 getOwnTeam() 上的 @Transactional 注释在那里,因为我认为,然后 UserModel 延迟加载的 TeamModel 将成为该事务的一部分.显然这个假设是错误的。

标签: spring hibernate spring-data-jpa


【解决方案1】:

如果您需要不同的负载启动,您可以使用:

  1. 查询时的原生sql
  2. jpql 与 join fetch 之类的构造
  3. 实体图 (https://www.baeldung.com/jpa-entity-graph) 使用这种方式加载的主要好处是对数据库的单个请求。 你可以阅读更多https://thorben-janssen.com/lazyinitializationexception/

您的对象处于分离状态 - 这是 LazyInitializationException 的原因(您将其移动到其他状态以加载您的对象) 例如

entityManager.merge(deatachedEntity);

【讨论】:

  • 我尝试了合并方法。但我收到一个错误:entityManager.merge(currentUser); 抛出“无法初始化代理 [org.doogie.liquido.model.TeamModel#22] - 没有会话” - 我确实在方法上保留了 @Transactional 注释。你能帮忙吗?
  • 啊啊啊啊。我找到了解决方案。 entityManager.merge(userModel) 调用返回然后合并/附加的实体。一旦我使用 returned 值,它就会起作用。 currentUser = entityManager.merge(currentUser);
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-01-19
  • 2016-07-16
  • 1970-01-01
  • 2012-03-18
  • 2016-12-22
  • 2011-05-11
  • 2012-01-24
相关资源
最近更新 更多