spring - 如何使用 Spring Boot 和 Hibernate 在多对一关系中强制执行 Join 查询?

标签 spring hibernate

我编写了一个非常简单的项目,其中包含一个 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/

相关文章:

java - Spring @Transactional 提交失败; Deby + Eclipse链接

java - Spring MVC 应用程序可以是多线程的,即使它的 servlet 不是吗?

java - Spring Boot 无法为服务测试类 Autowiring 存储库 bean

java - 无法使用 SpringMVC 通过 Hibernate 将 ItemDetail 列表 ie.List<ItemDetail> (OBJECT) 插入 MySQL 数据库字段

hibernate - JPA。如何仅在合并操作时忽略某些字段

java - Hibernate Criteria 集合的最新日期字段

mysql - Spring中调试 "org.springframework.beans.factory.BeanCreationExcpetion"错误

java - 没有为 java hibernate 中的实体指定标识符

java - 测试期间自动创建 Spring 实体 "authorities"

spring - 现实世界中的隔离级别用例