gpt4 book ai didi

java - 如何在以正确方式获取的事务之外初始化延迟加载的集合?

转载 作者:行者123 更新时间:2023-12-05 05:56:33 26 4
gpt4 key购买 nike

我有一个 spring boot 应用程序,它使用 Hibernate 作为 ORM 和 DGS framework作为 graphql 引擎。我一直在努力寻找初始化延迟加载集合的正确方法。我有以下情况:

application.properties
# The below has been set to false to get rid of the anti-pattern stuff it introduces
spring.jpa.open-in-view=false
...
@Entity
public class User {
@Id
@GeneratedValue
private UUID id;

@OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
private List<Article> articles;

...

}
@Entity
public class Article {
@Id
@GeneratedValue
private UUID id;

@ManyToOne(optional = false, fetch = FetchType.LAZY)
private User user;

...
}

我的 User 数据 getter 看起来像这样:

@DgsComponent
public class UserDataFetcher {
@Autowired
private UserService userService;

@DgsQuery
public User getUserById(@InputArgument UUID id) {
return userService.findById(id);
}
...
}

我的 UserService 看起来像这样:

@Service
public class UserServiceImpl implements UserService {

@Autowired
private UserRepository userRepository;

@Override
public User findById(UUID id) {
return userRepository.findById(id).orElseThrow(DgsEntityNotFoundException::new);
}
...
}

现在,我只想在用户在 graphql 查询中请求时从数据库初始化/加载我的文章集合。为此,我为我的文章创建了一个子解析器,它仅在用户在查询中请求文章时执行。我的 UserDataFetcher 开始看起来像这样:

@DgsComponent
public class UserDataFetcher {
@Autowired
private UserService userService;

@DgsQuery
public User getUserById(@InputArgument UUID id) {
return userService.findById(id);
}

@DgsData(parentType = "User", field = "articles")
public List<Article> getArticle(DgsDataFetchingEnvironment dfe) {
User user = dfe.getSource();
Hibernate.initialize(user.getArticles());
return user.getArticles();
}
...
}

但是,上面开始抛出异常,告诉我 Hibernate 无法为上述请求找到打开的 session 。这是有道理的,因为没有任何东西,所以我在我的子解析器上放了一个 @Transactional,它开始看起来像这样:

@DgsComponent
public class UserDataFetcher {
@Autowired
private UserService userService;

@DgsQuery
public User getUserById(@InputArgument UUID id) {
return userService.findById(id);
}

@DgsData(parentType = "User", field = "articles")
@Transactional
public List<Article> getArticle(DgsDataFetchingEnvironment dfe) {
User user = dfe.getSource();
Hibernate.initialize(user.getArticles());
return user.getArticles();
}
...
}

但是,上面的方法也没有用。我也尝试将此 @Transactional 移动到我的服务层,但即使那样它也不起作用,并且抛出了相同的异常。经过深思熟虑,我发现(也许)Hibernate.initialize(...) 仅在我在初始事务中调用它时才有效,该事务首先为我获取了我的用户。意思是,它对我没有用,因为我的用例是非常用户驱动的。我只想在我的用户要求时得到它,并且它总是在父事务之外的我的应用程序的其他部分。

我正在寻找除以下之外的解决方案:

  1. 将子解析器更改为如下内容:
    @DgsData(parentType = "User", field = "articles")
@Transactional
public List<Article> getArticle(DgsDataFetchingEnvironment dfe) {
User user = dfe.getSource();
List<Article> articles = articlesRepository.getArticlesByUserId(user.getUserId);
return articles;
}

我不赞成上述解决方案,因为我觉得这是通过尝试自己解决关系而不是让 hibernate 自己来解决而未充分利用 ORM 本身。 (如果我这样想错了,请纠正我)

  1. 更改我的 User 实体以使用 FetchMode.JOIN
@Entity
public class User {
@Id
@GeneratedValue
private UUID id;

@OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
@Fetch(FetchMode.JOIN)
private List<Article> articles;
...

}

这与告诉 hibernate 无论如何都急切加载下面的集合是一样的。我也不想要这个。

  1. spring.jpa.open-in-view=false 设置为 spring.jpa.open-in-view=true。也不赞成这样做,因为这只是 LazyInitializationExceptions 的创可贴。

  2. 通过在请求的整个生命周期中保持 session 打开来让您忘记 LazyInitializationException 的任何其他解决方案。

最佳答案

请注意,此答案假定可以使用 Spring Data JPA。

有帮助的可以full dynamic usage of EntityGraphs

Entity Graphs give us a possibility to define fetch plans and declare whichrelations (attributes) have to be queried from the database.

根据documentation

你可以做类似的事情

productRepository.findById(1L, EntityGraphUtils.fromAttributePaths(“article, “comments”));

并根据用户选择将所有必要的参数(关系)传递给 EntityGraphUtils.fromAttributePaths 方法。这使我们有可能只获取必要的数据。

其他资源:

  1. Sample project
  2. Spring Blog mentioned this extension
  3. JPA EntityGraph
  4. EntityGraph

关于java - 如何在以正确方式获取的事务之外初始化延迟加载的集合?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69143733/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com