Spring 数据 JPA 和 NamedEntityGraphs
Spring Data JPA And NamedEntityGraphs
目前我正在努力解决只能获取我需要的数据的问题。 findAll() 方法需要根据调用位置来获取数据。
我不想最终为每个实体图编写不同的方法。
此外,我会避免调用 entitymanagers 并自己形成(重复)查询。
基本上我想使用内置的 findAll 方法,但使用我喜欢的实体图。有机会吗?
@Entity
@Table(name="complaints")
@NamedEntityGraphs({
@NamedEntityGraph(name="allJoinsButMessages", attributeNodes = {
@NamedAttributeNode("customer"),
@NamedAttributeNode("handling_employee"),
@NamedAttributeNode("genre")
}),
@NamedEntityGraph(name="allJoins", attributeNodes = {
@NamedAttributeNode("customer"),
@NamedAttributeNode("handling_employee"),
@NamedAttributeNode("genre"),
@NamedAttributeNode("complaintMessages")
}),
@NamedEntityGraph(name="noJoins", attributeNodes = {
})
})
public class Complaint implements Serializable{
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue
private long id;
private Timestamp date;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "customer")
private User customer;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "handling_employee")
private User handling_employee;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="genre")
private Genre genre;
private boolean closed;
@OneToMany(mappedBy = "complaint", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<ComplaintMessage> complaintMessages = new ArrayList<ComplaintMessage>();
//getters and setters
}
还有我的 JPARepository
@Repository
public interface ComplaintRepository extends JpaRepository<Complaint, Long>{
List<Complaint> findByClosed(boolean closed);
@EntityGraph(value = "allJoinsButMessages" , type=EntityGraphType.FETCH)
@Override
List<Complaint> findAll(Sort sort);
}
我们 运行 遇到了类似的问题并设计了几个有前途的解决方案,但似乎没有一个优雅的解决方案来解决这个看似普遍的问题。
1) 前缀。 Data jpa 为方法名称提供了几个前缀(find、get、...)。一种可能性是对不同的命名图使用不同的前缀。这是最少的工作,但向开发人员隐藏了该方法的含义,并且很有可能导致一些不明显的错误实体加载问题。
@Repository
@Transactional
public interface UserRepository extends CrudRepository<User, Integer>, UserRepositoryCustom {
@EntityGraph(value = "User.membershipYearsAndPreferences", type = EntityGraphType.LOAD)
User findByUserID(int id);
@EntityGraph(value = "User.membershipYears", type = EntityGraphType.LOAD)
User readByUserId(int id);
}
2) 自定义存储库。另一种可能的解决方案是创建自定义查询方法并注入 EntityManager。该解决方案为您的存储库提供了最干净的接口,因为您可以为您的方法命名一些有意义的东西,但是添加到代码中以提供解决方案会非常复杂,并且您正在手动获取实体管理器而不是使用 Spring魔法.
interface UserRepositoryCustom {
public User findUserWithMembershipYearsById(int id);
}
class UserRepositoryImpl implements UserRepositoryCustom {
@PersistenceContext
private EntityManager em;
@Override
public User findUserWithMembershipYearsById(int id) {
User result = null;
List<User> users = em.createQuery("SELECT u FROM users AS u WHERE u.id = :id", User.class)
.setParameter("id", id)
.setHint("javax.persistence.fetchgraph", em.getEntityGraph("User.membershipYears"))
.getResultList();
if(users.size() >= 0) {
result = users.get(0);
}
return result;
}
}
@Repository
@Transactional
public interface UserRepository extends CrudRepository<User, Integer>, UserRepositoryCustom {
@EntityGraph(value = "User.membershipYearsAndPreferences", type = EntityGraphType.LOAD)
User findByUserID(int id);
}
3) JPQL。本质上,这只是放弃命名实体图并使用 JPQL 为您处理连接。我认为不理想。
@Repository
@Transactional
public interface UserRepository extends CrudRepository<User, Integer>, UserRepositoryCustom {
@EntityGraph(value = "User.membershipYearsAndPreferences", type = EntityGraphType.LOAD)
User findByUserID(int id);
@Query("SELECT u FROM users WHERE u.id=:id JOIN??????????????????????????")
User findUserWithTags(@Param("id") final int id);
}
我们选择了选项 1,因为它的实现最简单,但这确实意味着当我们使用我们的存储库时,我们必须查看获取方法以确保我们使用的是具有正确实体图的方法。祝你好运。
来源:
我没有足够的声誉 post 我所有的消息来源。对不起:(
将@EntityGraph
与@Query
一起使用
@Repository
public interface ComplaintRepository extends JpaRepository<Complaint, Long>{
@EntityGraph(value = "allJoinsButMessages" , type=EntityGraphType.FETCH)
@Query("SELECT c FROM Complaint ORDER BY ..")
@Override
List<Complaint> findAllJoinsButMessages();
@EntityGraph(value = "allJoins" , type=EntityGraphType.FETCH)
@Query("SELECT c FROM Complaint ORDER BY ..")
@Override
List<Complaint> findAllJoin();
...
}
我们遇到了同样的问题并构建了一个 Spring Data JPA 扩展来解决它:
https://github.com/Cosium/spring-data-jpa-entity-graph
此扩展允许将命名或动态构建的 EntityGraph 作为任何存储库方法的参数传递。
有了这个扩展,你可以立即使用这个方法:
List<Complaint> findAll(Sort sort, EntityGraph entityGraph);
并且能够使用在运行时选择的 EntityGraph 来调用它。
您能否尝试使用您将请求的子项创建 EntiyGraph 名称,并为查找所有方法提供相同的名称。
例如:
@EntityGraph(value = "fetch.Profile.Address.record", type = EntityGraphType.LOAD)
Employee getProfileAddressRecordById(long id);
针对您的情况:
@NamedEntityGraph(name="all.Customer.handling_employee.genre", attributeNodes = {
@NamedAttributeNode("customer"),
@NamedAttributeNode("handling_employee"),
@NamedAttributeNode("genre")
})
存储库中的方法名称
@EntityGraph(value = "all.Customer.handling_employee.genre" , type=EntityGraphType.FETCH)
findAllCustomerHandlingEmployeeGenre
这样您就可以跟踪不同的 findAll 方法。
在派生查询上使用 @EntityGraph
注释是可能的,正如我从 This article 中发现的那样。文章有例子:
@Repository
public interface ArticleRepository extends JpaRepository<Article,Long> {
@EntityGraph(attributePaths = "topics")
Article findOneWithTopicsById(Long id);
}
但我不认为 "with" 有什么特别之处,实际上您可以在 find
和 By
之间选择任何值。我尝试了这些并且它们有效(这段代码是 Kotlin,但想法是一样的):
interface UserRepository : PagingAndSortingRepository<UserModel, Long> {
@EntityGraph(attributePaths = arrayOf("address"))
fun findAnythingGoesHereById(id: Long): Optional<UserModel>
@EntityGraph(attributePaths = arrayOf("address"))
fun findAllAnythingGoesHereBy(pageable: Pageable): Page<UserModel>
}
文章中提到不能创建类似于 findAll
的方法,该方法将在没有 By
条件的情况下查询所有记录,并以 findAllWithTopicsByIdNotNull()
为例.我发现仅在名称末尾包含 By
本身就足够了:findAllWithTopicsBy()
。更简洁一点,但读起来可能更令人困惑。使用仅以 By
结尾且没有任何条件的方法名称可能会在 Spring 的未来版本中出现中断的危险,因为它看起来不像是派生查询名称的预期用途。
看起来 Spring 中解析派生查询名称的代码是 here on github。如果您对派生查询存储库方法名称的可能性感到好奇,可以查看那里。
These 是派生查询的 spring 文档。
这是用 spring-data-commons-2.2 测试的。3.RELEASE
编辑:这实际上不起作用。最终不得不选择 https://github.com/Cosium/spring-data-jpa-entity-graph。默认方法看起来是正确的,但没有成功覆盖注释。
使用 JPA,我发现使用默认方法和不同的 EntityGraph 注释是可行的:
@Repository
public interface ComplaintRepository extends JpaRepository<Complaint, Long>{
List<Complaint> findByClosed(boolean closed);
@EntityGraph(attributePaths = {"customer", "genre", "handling_employee" }, type=EntityGraphType.FETCH)
@Override
List<Complaint> findAll(Sort sort);
@EntityGraph(attributePaths = {"customer", "genre", "handling_employee", "messages" }, type=EntityGraphType.FETCH)
default List<Complaint> queryAll(Sort sort){
return findAll(sort);
}
}
您无需进行任何重新实现,可以使用现有界面自定义实体图。
目前我正在努力解决只能获取我需要的数据的问题。 findAll() 方法需要根据调用位置来获取数据。 我不想最终为每个实体图编写不同的方法。 此外,我会避免调用 entitymanagers 并自己形成(重复)查询。 基本上我想使用内置的 findAll 方法,但使用我喜欢的实体图。有机会吗?
@Entity
@Table(name="complaints")
@NamedEntityGraphs({
@NamedEntityGraph(name="allJoinsButMessages", attributeNodes = {
@NamedAttributeNode("customer"),
@NamedAttributeNode("handling_employee"),
@NamedAttributeNode("genre")
}),
@NamedEntityGraph(name="allJoins", attributeNodes = {
@NamedAttributeNode("customer"),
@NamedAttributeNode("handling_employee"),
@NamedAttributeNode("genre"),
@NamedAttributeNode("complaintMessages")
}),
@NamedEntityGraph(name="noJoins", attributeNodes = {
})
})
public class Complaint implements Serializable{
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue
private long id;
private Timestamp date;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "customer")
private User customer;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "handling_employee")
private User handling_employee;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="genre")
private Genre genre;
private boolean closed;
@OneToMany(mappedBy = "complaint", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<ComplaintMessage> complaintMessages = new ArrayList<ComplaintMessage>();
//getters and setters
}
还有我的 JPARepository
@Repository
public interface ComplaintRepository extends JpaRepository<Complaint, Long>{
List<Complaint> findByClosed(boolean closed);
@EntityGraph(value = "allJoinsButMessages" , type=EntityGraphType.FETCH)
@Override
List<Complaint> findAll(Sort sort);
}
我们 运行 遇到了类似的问题并设计了几个有前途的解决方案,但似乎没有一个优雅的解决方案来解决这个看似普遍的问题。
1) 前缀。 Data jpa 为方法名称提供了几个前缀(find、get、...)。一种可能性是对不同的命名图使用不同的前缀。这是最少的工作,但向开发人员隐藏了该方法的含义,并且很有可能导致一些不明显的错误实体加载问题。
@Repository
@Transactional
public interface UserRepository extends CrudRepository<User, Integer>, UserRepositoryCustom {
@EntityGraph(value = "User.membershipYearsAndPreferences", type = EntityGraphType.LOAD)
User findByUserID(int id);
@EntityGraph(value = "User.membershipYears", type = EntityGraphType.LOAD)
User readByUserId(int id);
}
2) 自定义存储库。另一种可能的解决方案是创建自定义查询方法并注入 EntityManager。该解决方案为您的存储库提供了最干净的接口,因为您可以为您的方法命名一些有意义的东西,但是添加到代码中以提供解决方案会非常复杂,并且您正在手动获取实体管理器而不是使用 Spring魔法.
interface UserRepositoryCustom {
public User findUserWithMembershipYearsById(int id);
}
class UserRepositoryImpl implements UserRepositoryCustom {
@PersistenceContext
private EntityManager em;
@Override
public User findUserWithMembershipYearsById(int id) {
User result = null;
List<User> users = em.createQuery("SELECT u FROM users AS u WHERE u.id = :id", User.class)
.setParameter("id", id)
.setHint("javax.persistence.fetchgraph", em.getEntityGraph("User.membershipYears"))
.getResultList();
if(users.size() >= 0) {
result = users.get(0);
}
return result;
}
}
@Repository
@Transactional
public interface UserRepository extends CrudRepository<User, Integer>, UserRepositoryCustom {
@EntityGraph(value = "User.membershipYearsAndPreferences", type = EntityGraphType.LOAD)
User findByUserID(int id);
}
3) JPQL。本质上,这只是放弃命名实体图并使用 JPQL 为您处理连接。我认为不理想。
@Repository
@Transactional
public interface UserRepository extends CrudRepository<User, Integer>, UserRepositoryCustom {
@EntityGraph(value = "User.membershipYearsAndPreferences", type = EntityGraphType.LOAD)
User findByUserID(int id);
@Query("SELECT u FROM users WHERE u.id=:id JOIN??????????????????????????")
User findUserWithTags(@Param("id") final int id);
}
我们选择了选项 1,因为它的实现最简单,但这确实意味着当我们使用我们的存储库时,我们必须查看获取方法以确保我们使用的是具有正确实体图的方法。祝你好运。
来源:
我没有足够的声誉 post 我所有的消息来源。对不起:(
将@EntityGraph
与@Query
@Repository
public interface ComplaintRepository extends JpaRepository<Complaint, Long>{
@EntityGraph(value = "allJoinsButMessages" , type=EntityGraphType.FETCH)
@Query("SELECT c FROM Complaint ORDER BY ..")
@Override
List<Complaint> findAllJoinsButMessages();
@EntityGraph(value = "allJoins" , type=EntityGraphType.FETCH)
@Query("SELECT c FROM Complaint ORDER BY ..")
@Override
List<Complaint> findAllJoin();
...
}
我们遇到了同样的问题并构建了一个 Spring Data JPA 扩展来解决它:
https://github.com/Cosium/spring-data-jpa-entity-graph
此扩展允许将命名或动态构建的 EntityGraph 作为任何存储库方法的参数传递。
有了这个扩展,你可以立即使用这个方法:
List<Complaint> findAll(Sort sort, EntityGraph entityGraph);
并且能够使用在运行时选择的 EntityGraph 来调用它。
您能否尝试使用您将请求的子项创建 EntiyGraph 名称,并为查找所有方法提供相同的名称。 例如:
@EntityGraph(value = "fetch.Profile.Address.record", type = EntityGraphType.LOAD)
Employee getProfileAddressRecordById(long id);
针对您的情况:
@NamedEntityGraph(name="all.Customer.handling_employee.genre", attributeNodes = {
@NamedAttributeNode("customer"),
@NamedAttributeNode("handling_employee"),
@NamedAttributeNode("genre")
})
存储库中的方法名称
@EntityGraph(value = "all.Customer.handling_employee.genre" , type=EntityGraphType.FETCH)
findAllCustomerHandlingEmployeeGenre
这样您就可以跟踪不同的 findAll 方法。
在派生查询上使用 @EntityGraph
注释是可能的,正如我从 This article 中发现的那样。文章有例子:
@Repository
public interface ArticleRepository extends JpaRepository<Article,Long> {
@EntityGraph(attributePaths = "topics")
Article findOneWithTopicsById(Long id);
}
但我不认为 "with" 有什么特别之处,实际上您可以在 find
和 By
之间选择任何值。我尝试了这些并且它们有效(这段代码是 Kotlin,但想法是一样的):
interface UserRepository : PagingAndSortingRepository<UserModel, Long> {
@EntityGraph(attributePaths = arrayOf("address"))
fun findAnythingGoesHereById(id: Long): Optional<UserModel>
@EntityGraph(attributePaths = arrayOf("address"))
fun findAllAnythingGoesHereBy(pageable: Pageable): Page<UserModel>
}
文章中提到不能创建类似于 findAll
的方法,该方法将在没有 By
条件的情况下查询所有记录,并以 findAllWithTopicsByIdNotNull()
为例.我发现仅在名称末尾包含 By
本身就足够了:findAllWithTopicsBy()
。更简洁一点,但读起来可能更令人困惑。使用仅以 By
结尾且没有任何条件的方法名称可能会在 Spring 的未来版本中出现中断的危险,因为它看起来不像是派生查询名称的预期用途。
看起来 Spring 中解析派生查询名称的代码是 here on github。如果您对派生查询存储库方法名称的可能性感到好奇,可以查看那里。
These 是派生查询的 spring 文档。
这是用 spring-data-commons-2.2 测试的。3.RELEASE
编辑:这实际上不起作用。最终不得不选择 https://github.com/Cosium/spring-data-jpa-entity-graph。默认方法看起来是正确的,但没有成功覆盖注释。
使用 JPA,我发现使用默认方法和不同的 EntityGraph 注释是可行的:
@Repository
public interface ComplaintRepository extends JpaRepository<Complaint, Long>{
List<Complaint> findByClosed(boolean closed);
@EntityGraph(attributePaths = {"customer", "genre", "handling_employee" }, type=EntityGraphType.FETCH)
@Override
List<Complaint> findAll(Sort sort);
@EntityGraph(attributePaths = {"customer", "genre", "handling_employee", "messages" }, type=EntityGraphType.FETCH)
default List<Complaint> queryAll(Sort sort){
return findAll(sort);
}
}
您无需进行任何重新实现,可以使用现有界面自定义实体图。