jpa多表关联查询_JPA【关联查询篇】

jpa多表关联查询_JPA【关联查询篇】_第1张图片

摘要:本文主要介绍JPA的多表关联查询(一对一、一对多、双向关联、多对一、多对多)以及N+1查询的优化。

1. JPA多表关联查询

多表关联查询就是实现使用 一个实体类对象操作或者查询多个表的数据。

配置多表联系查询必须有两个步骤:

[1] 建立实体类,使用注解配置 单表关联。
[2] 修改实体类,使用注解配置 多表关联。

本文使用的数据库表SQL脚本:

/*
 Navicat Premium Data Transfer

 Source Server         : root
 Source Server Type    : MySQL
 Source Server Version : 50562
 Source Host           : localhost:3306
 Source Schema         : hibernate

 Target Server Type    : MySQL
 Target Server Version : 50562
 File Encoding         : 65001

 Date: 28/01/2020 17:10:48
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for tb_result
-- ----------------------------
DROP TABLE IF EXISTS `tb_result`;
CREATE TABLE `tb_result`  (
  `result_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '成绩编号',
  `result_subject` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '科目',
  `result_score` float NULL DEFAULT NULL COMMENT '分数',
  `student_id` int(11) NOT NULL COMMENT '学生编号',
  PRIMARY KEY (`result_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '成绩表' ROW_FORMAT = Compact;

-- ----------------------------
-- Table structure for tb_student
-- ----------------------------
DROP TABLE IF EXISTS `tb_student`;
CREATE TABLE `tb_student`  (
  `student_id` int(11) NOT NULL AUTO_INCREMENT,
  `student_name` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `student_pwd` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `student_status` int(2) NULL DEFAULT NULL,
  `create_date` date NULL DEFAULT NULL,
  PRIMARY KEY (`student_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

-- ----------------------------
-- Table structure for tb_student_identifer
-- ----------------------------
DROP TABLE IF EXISTS `tb_student_identifer`;
CREATE TABLE `tb_student_identifer`  (
  `student_id` int(11) NOT NULL COMMENT '学生编号',
  `student_idcard` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '身份证号码',
  `student_number` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '学号',
  PRIMARY KEY (`student_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '学生身份信息表' ROW_FORMAT = Compact;

-- ----------------------------
-- Table structure for tb_student_teacher
-- ----------------------------
DROP TABLE IF EXISTS `tb_student_teacher`;
CREATE TABLE `tb_student_teacher`  (
  `student_id` int(11) NOT NULL COMMENT '学生编号',
  `teacher_id` int(11) NOT NULL COMMENT '教师编号'
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '学生教师关系表' ROW_FORMAT = Compact;

-- ----------------------------
-- Table structure for tb_teacher
-- ----------------------------
DROP TABLE IF EXISTS `tb_teacher`;
CREATE TABLE `tb_teacher`  (
  `teacher_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '教师编号',
  `teacher_name` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '教师名字',
  `teacher_pwd` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '登录密码',
  PRIMARY KEY (`teacher_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '教师表' ROW_FORMAT = Compact;

SET FOREIGN_KEY_CHECKS = 1;

1.1. 一对多的实现

1.1.1. 需求

通过ID查询一条学生表的记录,同时查询该学生的对应的成绩的信息

1.1.2. 实现代码

第一步:配置单表关联

--Student

@Entity

--Result

@Entity
第二步:配置多表关联

分析:由于一个学生可以多门课程成绩,学生和成绩的关系是一对多,既然一个学生多门成绩,所以一个学生实体类也就有多门成绩,所以使用集合来存储成绩信息,因此修改学生类,以配置多表tb_student和tb_result的关系。

在Student实体类中加入如下代码:

// 声明关联关系

注意事项:

1.配置关联的两个实体类,必须有一个外键字段建立和数据库的关联的。
2.@JoinColumn用于设置,关联的外键约束的字段(外键配置)。
3.使用JPA注解配置的集合建议使用List。 第三步:测试

--StudentDAOTest

@Test

1.2. 多对一的实现

1.2.1. 需求

需求:通过ID查询一条成绩表的记录,同时查询该成绩的对应的学生的信息

1.2.2. 实现代码

第一步,配置单表关联

--Student

@Entity

--Result

@Entity

第二步:配置多表关联

分析:由于一门成绩总有一个学生与之对应,而学生对应多门成绩,因此,成绩和学生的关系是多对一,因此学生实体类作为一个对象属性存在于成绩实体类中,因此只需修改成绩类即可。

在Result实体类中加入如下代码:

// 声明关联关系

注意:当外键字段不是主键时,需要删掉,因此Result实体类中的studentId字段需要注释掉。

/*  @Column(name = "student_id")
第三步:测试

--StudentDAOTest

@Test

1.3. 双向关联

1.3.1. 说明

所谓的单向关联,就是配置一对多、多对一、一对一、多对多关联表的双方,一方配置出现改动不会影响另一方的正常使用。
所谓的双向关联,就是一对多的配置,依赖多对一的配置,以上的代码,一对一有一个 @JoinColumn(name = "student_id"),多对一有一个 @JoinColumn(name = "student_id"),在JPA注解配置中支持,由一的一方引用多的一方配置,即一的一方无需配置@JoinColumn(name = "student_id"),而是直接引用多的一方。

引用配置如下:

多对一的配置不变:
// 声明关联关系
修改一对多的配置:
// 配置双向关联

--问题:为什么叫双向关联呢?

就是因为 一对多多对一的关系都是由 多对一的配置维护。如果多对一的关联关系被破坏,一对多方的配置也会出问题,由于耦合度太高了,不好维护和扩展,因此不推荐使用!!!

注意事项:一定是一对多引用多对一,不能是多对一引用一对多。

测试

--StudentDAOTest

@Test

1.4. 一对一的实现

1.4.1. 需求

[1] 通过ID查询学生的信息,同时查询该学生对应的学生身份信息。
[2] 通过ID查询一条学生身份表的记录,同时查询该学生身份的对应的学生的信息。

1.4.2. 示例代码

第一步:配置单表关联

--Student

@Entity

--studentIdentifer

@Entity

第二步:配置多表关联

分析:一个学生只有一个学生身份,因此学生和学生身份之间的关系是一对一,针对需求[1],将学生身份作为学生的一个对象属性存在,针对需求[2]则相反。

--需求[1]:

在学生实体类中,添加如下代码:
@OneToOne

--需求[2]:

在学生身份实体类中,添加如下代码:
@OneToOne
第三步:测试
@Test

注意事项:注解配置的一对一,与XML配置不同,必须要配置外键。

1.5. 多对多的实现

1.5.1. 需求

[1] 通过ID查询学生的信息,通过该学生信息也获得对应的教师信息。
[2] 通过ID查询教师的信息,通过教师信息也获得该对应的学生信息。

如图所示:一个学生可以有多个教师,一个教师也可以有多个学生,所以学生和教师的关系是多对多的关系。

jpa多表关联查询_JPA【关联查询篇】_第2张图片

如上图所示:

如果要从学生表的信息获得教师表的信息。必须需要三个条件:

1. 必须需要有一个中间表。
2. 必须需要中间表对应本表的外键。
3. 必须需要中间表对应关联表的外键。

1.5.2. 示例代码

第一步:配置单表关联

--Student

@Entity

--Teacher

@Entity

第二步,配置多表关联

需求配置内容:

1. 必须需要有一个中间表。
2. 必须需要中间表对应本表的外键。
3. 必须需要中间表对应关联表的外键。

--在Student中加入如下代码:

@ManyToMany

--在Teacher中加入如下代码:

@ManyToMany
第三步:测试
@Test

2. 关于N+1的问题

在一对多获得多对多的时候。如果通过一的一方取获得多的一方的数据。除了第一次查询表的数据以外。每获得一条多的一方的数据就查询一次。

如:通过学生表的记录查询成绩表的记录。一个学生就查询一次,50个学生就查询50次。如果需要查询50个学生的成绩,需要查询数据库的次数为 第一次查询学生的记录+50次查询成绩的记录。这个情况我们成为 N+1。

@Test

对于一些对性能要不高的内部系统,其实N+1问题是可以接受。但对于要求性能高的系统是不可以使用的。

解决N+1问题的方法就是通过连接表查询,在JPQL里面通过fetch 设置生成SQL语句的策略。

@Test

你可能感兴趣的:(jpa多表关联查询)