使用内连接

时间:2017-04-27 17:48:17

标签: hibernate jpa spring-boot spring-data-jpa spring-data-rest

我正在使用:

  • Spring Boot
  • Spring Data JPA
  • Spring Data Rest
  • 休眠
  • 嵌入式H2数据库

我正在尝试定义2个类FirstSecond,以便从FirstSecond进行一对一的映射。由于我试图通过Spring Data Rest来托管查询,它将数据转换为JSON,我相信做EAGER提取是最有意义的。

所以,我有以下内容:

@Entity
public class First {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "FIRST_ID")
    private long id;

    @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinColumn(name = "SECOND_ID")
    private Second second;

    // more/getters/settings
}

@Entity
public class Second {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "SECOND_ID")
    private long id;

    // more/getters/settings
}

当我搜索First类型的数据时,我得到First类型的SELECT查询,然后查询每个Second的SELECT查询,其中SECOND_ID与引用的外键匹配First

对这些数据进行INNER JOIN以在单个查询中执行所有操作是最有意义的。如:

SELECT * FROM FIRST
INNER JOIN SECOND ON FIRST.SECOND_ID

我可以直接在数据库上运行此查询,并获取一个连接两个表的表。

如何让JPA / Hibernate执行此操作?这似乎是一种常见的操作,我觉得我错过了一些明显的东西。

编辑:请注意,我正在使用Spring Data Rest自动生成的REST端点来运行查询。我已经定义了一个Spring Data存储库:

@RepositoryRestResource(path = "first")
public interface FirstRepository extends CrudRepository<First, Long> {
}

然后,通过访问http://host/first,我使Spring运行findAll操作,该操作有效,但同样会触发大量SELECT查询。

2 个答案:

答案 0 :(得分:2)

尝试此变体:

@RepositoryRestResource(path = "first")
public interface FirstRepository extends CrudRepository<First, Long> {

    @Override
    @EntityGraph(attributePaths = {"second"})
    Iterable<First> findAll();
}

答案 1 :(得分:1)

首先,仅在使用EntityManager#find()加载实体时才会遵守关联的提取提示。

如果您要获得单个第一个实例(http://host/first/1),您应该看到使用LEFT OUTER JOIN(将关系标记为optional = false)在1个查询中检索实例的关联将导致INNER JOIN)因为Spring Data将委托给EntityManager#find()方法。

Spring Data提供了一种方便的findAll()方法,当您选择获取所有实例并在您点击http://host/first时调用时,它只会生成一个查询"Select f from First f"。所以这显然不会做JOIN FETCH。

如果您要创建查询:

@Query("select f from First f join fetch f.second")
public List<First> findAllEager();

并点击http://host/first/search/findAllEager然后这应该在一个选择中加载所有内容。

因为这显然不是很方便,你可以实际上只是重新定义存储库接口中的findAll()方法,如下所示:

@Query("select f from First f join fetch f.second")
public Iterable<First> findAll();

其他信息:使用EntityManager#find()加载实体时,除非标记为非可选和懒惰,否则{HIDnate中的@OneToOne关联始终是热切的。