Spring Data JPA和NamedEntityGraphs

den*_*iii 47 jpa fetch spring-data-jpa entitygraph

目前我正在努力只能获取我需要的数据.findAll()方法需要根据其被调用的位置获取数据.我不想最终为每个实体图编写不同的方法.此外,我会避免调用实体管理员并自己形成(重复)查询.基本上我想在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
}
Run Code Online (Sandbox Code Playgroud)

还有我的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);
}
Run Code Online (Sandbox Code Playgroud)

小智 44

我们遇到了类似的问题,设计了几种潜在的解决方案,但似乎并没有成为一个优雅的解决方案似乎是一个常见的问题.

1)前缀.数据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);
}
Run Code Online (Sandbox Code Playgroud)

2)CustomRepository.另一种可能的解决方案是创建自定义查询方法并注入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);
}
Run Code Online (Sandbox Code Playgroud)

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);
}
Run Code Online (Sandbox Code Playgroud)

我们使用选项1,因为它是最简单的实现,但这确实意味着当我们使用我们的存储库时,我们必须查看fetch方法以确保我们使用具有正确实体图的那个.祝好运.

资料来源:

我没有足够的声誉来发布我的所有来源.对不起:(

  • 除了改变方法的动词之外,还可以在动词和"by"之间添加任意字符串,如`findEagerById`或`findLessEagerById` (4认同)
  • @JordanMackie这会产生一些奇怪的名字,但你可以在`By`之后结束这个名字,这使得它成为`findAll`的变种:`findAllBy`,`findEverythingBy` ... (2认同)

Réd*_*oui 15

我们遇到了同样的问题并构建了一个Spring Data JPA扩展来解决它:

https://github.com/Cosium/spring-data-jpa-entity-graph

此扩展允许将命名或动态构建的EntityGraph作为任何存储库方法的参数传递.

使用此扩展程序,您可以立即使用此方法:

List<Complaint> findAll(Sort sort, EntityGraph entityGraph);
Run Code Online (Sandbox Code Playgroud)

并且能够使用在运行时选择的EntityGraph来调用它.


GKi*_*lin 11

使用@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();

   ...
Run Code Online (Sandbox Code Playgroud)

}

  • @JordanMackie我在项目中使用了它:https://github.com/gkislin/topjava/blob/master/src/main/java/ru/javawebinar/topjava/repository/datajpa/CrudUserRepository.java (2认同)

mow*_*ker 5

正如我从本文@EntityGraph中发现的,在派生查询上使用注释是可能的。文章中有这样的例子:

@Repository
public interface ArticleRepository extends JpaRepository<Article,Long> {
   @EntityGraph(attributePaths = "topics")
   Article findOneWithTopicsById(Long id);
}
Run Code Online (Sandbox Code Playgroud)

但我不认为“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>
}

Run Code Online (Sandbox Code Playgroud)

该文章提到了一个警告,即您无法创建类似于findAll在没有By条件的情况下查询所有记录的方法并用作findAllWithTopicsByIdNotNull()示例。我发现仅By在名称末尾包含其本身就足够了:findAllWithTopicsBy()。更简洁,但读起来可能更混乱。使用以不带任何条件结尾的方法名称By可能会在 Spring 的未来版本中面临中断的危险,因为它似乎不是派生查询名称的预期用途。

看起来在 Spring 中解析派生查询名称的代码位于github 上。如果您对派生查询存储库方法名称的可能性感到好奇,可以查看那里。

这些是派生查询的 spring 文档。

这是用 spring-data-commons-2.2.3.RELEASE 测试的