我编写了一个非常简单的项目,其中包含一个 Title 表(Mr、Mrs 等)和一个 Student 表,它与 Title 具有多对一关系。
当我找到所有学生时,Hibernate 会针对学生列表发出一个查询,然后针对每个标题发出一个单独的查询。我期待它在一个带有连接的查询中完成所有这些。
我希望 spring 能够处理这个问题,而不必为这样一个简单的案例编写我自己的查询。
这是我的设置:
@Entity(name = "title")
@Data
public class Title {
@Id
@GeneratedValue
@Column(name = "TITLE_ID", nullable = false)
private Long titleId;
@Column(name = "SHORT_NAME", nullable = false)
private String shortName;
}
@Entity(name = "student")
@Data
public class Student {
@Id
@GeneratedValue
@Column(name = "STUDENT_ID", nullable = false)
private Long studentId;
@ManyToOne
@JoinColumn(name = "TITLE_ID", nullable = false)
private Title title;
@Column(name = "NAME", nullable = false)
private String name;
}
public interface StudentRepository extends CrudRepository<Student, Long> {
}
@RestController
public class JoinController {
@Autowired
private StudentRepository repository;
@RequestMapping(value = "/student")
public String allStudents() {
Iterable<Student> students = repository.findAll();
return "students: " + students;
}
}
这是输出,显示所有 (4) 查询:
org.hibernate.SQL :logStatement[109]
select
student0_.student_id as student_1_0_,
student0_.name as name2_0_,
student0_.title_id as title_id3_0_
from
student student0_
org.hibernate.SQL :logStatement[109]
select
title0_.title_id as title_id1_1_0_,
title0_.short_name as short_na2_1_0_
from
title title0_
where
title0_.title_id=?
o.h.t.d.sql.BasicBinder :bind[81] binding parameter [1] as [BIGINT] - [1]
org.hibernate.SQL :logStatement[109]
select
title0_.title_id as title_id1_1_0_,
title0_.short_name as short_na2_1_0_
from
title title0_
where
title0_.title_id=?
o.h.t.d.sql.BasicBinder :bind[81] binding parameter [1] as [BIGINT] - [2]
org.hibernate.SQL :logStatement[109]
select
title0_.title_id as title_id1_1_0_,
title0_.short_name as short_na2_1_0_
from
title title0_
where
title0_.title_id=?
o.h.t.d.sql.BasicBinder :bind[81] binding parameter [1] as [BIGINT] - [3]
另外,Postgres 的表设置:
CREATE TABLE TITLE (
TITLE_ID BIGSERIAL PRIMARY KEY,
SHORT_NAME TEXT NOT NULL
);
CREATE TABLE STUDENT (
STUDENT_ID BIGSERIAL PRIMARY KEY,
TITLE_ID BIGSERIAL,
NAME TEXT NOT NULL
);
ALTER TABLE STUDENT ADD FOREIGN KEY (TITLE_ID) REFERENCES TITLE;
INSERT INTO TITLE (TITLE_ID, SHORT_NAME) VALUES (1, 'Mr');
INSERT INTO TITLE (TITLE_ID, SHORT_NAME) VALUES (2, 'Mrs');
INSERT INTO TITLE (TITLE_ID, SHORT_NAME) VALUES (3, 'Dr');
INSERT INTO STUDENT (STUDENT_ID, TITLE_ID, NAME) VALUES (1, 1, 'Smith');
INSERT INTO STUDENT (STUDENT_ID, TITLE_ID, NAME) VALUES (2, 1, 'Anderson');
INSERT INTO STUDENT (STUDENT_ID, TITLE_ID, NAME) VALUES (3, 2, 'Jones');
INSERT INTO STUDENT (STUDENT_ID, TITLE_ID, NAME) VALUES (4, 3, 'Livingston');
最佳答案
您可以在您的存储库中添加一个新方法,例如 findAllStudentsAlongWithTitle
,并在其顶部使用 @Query
注释指定 HQL Join 查询。您可以找到使用 @Query
here 的示例 JOIN
更新:
我尝试了代码,我看到了相同的行为,即,无论 fetch
属性或 @Fetch(FetchMode.JOIN)
作为获取策略如何,都会触发多个查询。
原因是 findAll
在内部创建 JPQL
查询,例如,(from Student s)
使用它获取数据。并且默认 JPQL
不考虑实体映射中配置的 fetch strategy
并且默认策略是 SELECT
。因此,这导致首先获取父项(即学生),然后在后续查询(即 1+3 查询)中加载子项(标题)。使用 JPQL
直接使用 em.createQuery(...).getResultList()
查询时,您会注意到相同的行为。
另一方面,我使用 findOne
方法测试了相同的内容,该方法将 id
作为 StudentRepository
的参数。它实际上导致使用单个 JOIN
获取 student 和 title ,除非我们使用 fetch=LAZY
将其明确标记为延迟加载。这很可能是因为 findOne
方法不使用 JPQL
而是使用 em.find(...)
,这尊重实体映射中配置的 fetch or @Fetch
,并且对于 EAGER
关联,它使用 JOIN
作为默认策略。您可以在直接使用 em.find(...)
时注意到相同的行为。
关于spring - 如何使用 Spring Boot 和 Hibernate 在多对一关系中强制执行 Join 查询?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35425881/