我制作了一个非常简单的项目,其中有一个Title表(Mr,Mrs等)和一个Student表,它与Title有一个ManyToOne关系。
当我找到所有学生时,Hibernate会为学生列表发出一个查询,然后为每个标题单独查询。我希望它能在一个带连接的查询中完成所有这些操作。
我希望春天会处理这个问题,而不必为这么简单的案例编写我自己的查询。
这是我的设置:
@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');
答案 0 :(得分:2)
您可以在存储库中添加一个新方法,说findAllStudentsAlongWithTitle
,并在其顶部使用@Query
注释指定HQL连接查询。您可以找到使用@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
获取学生和标题,除非我们使用fetch=LAZY
明确标记为延迟加载。这很可能是因为findOne
方法不使用JPQL
而是使用em.find(...)
,它尊重实体映射中配置的fetch or @Fetch
,以及{{1}它使用EAGER
作为默认策略。直接使用JOIN
时,您会注意到相同的行为。