我正在使用:
我正在尝试定义2个类First
和Second
,以便从First
到Second
进行一对一的映射。由于我试图通过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查询。
答案 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
关联始终是热切的。