如何使用Spring Boot和Hibernate在ManyToOne关系中强制加入查询?

时间:2016-02-16 07:03:21

标签: spring hibernate

我制作了一个非常简单的项目,其中有一个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');

1 个答案:

答案 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时,您会注意到相同的行为。