【SpringBoot Data JPA】多表关联查询

前言

本文章仅对JPA中的一对多的多表关联查询做了介绍,其他类型的可以参考之前的博客:
Springboot整合JPA(二)
Springboot整合JPA(三)

一、数据库架构设计

1.1 数据表结构

  • 传感器基础信息表 sensor_info
  • 传感器种类表sensor_type
  • 传感器(所属面)位置信息表sensor_aspect
    架构如下:
    在这里插入图片描述

2.2 建立数据库表:

CREATE DATABASE /*!32312 IF NOT EXISTS*/`dstructure` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci */ /*!80016 DEFAULT ENCRYPTION='N' */;

USE `dstructure`;

/*Table structure for table `rocketmq_conf` */

DROP TABLE IF EXISTS `rocketmq_conf`;

CREATE TABLE `rocketmq_conf` (
  `id` int NOT NULL AUTO_INCREMENT,
  `nameserver_addr` varchar(20) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `UNIQUE_ADDR` (`nameserver_addr`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

/*Data for the table `rocketmq_conf` */

insert  into `rocketmq_conf`(`id`,`nameserver_addr`) values (1,'47.106.243.139:9876');

/*Table structure for table `sensor_aspect` */

DROP TABLE IF EXISTS `sensor_aspect`;

CREATE TABLE `sensor_aspect` (
  `id` int NOT NULL AUTO_INCREMENT,
  `aspect_type` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
  `aspect_desc` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `UNIQUE` (`aspect_type`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

/*Data for the table `sensor_aspect` */

insert  into `sensor_aspect`(`id`,`aspect_type`,`aspect_desc`) values (1,'top','上'),(2,'bottom','下'),(3,'left','左'),(4,'right','右'),(5,'front','前'),(6,'behind','后'),(7,'deck','第一层甲板'),(8,'airtight_doors','气密门');

/*Table structure for table `sensor_info` */

DROP TABLE IF EXISTS `sensor_info`;

CREATE TABLE `sensor_info` (
  `id` int NOT NULL AUTO_INCREMENT,
  `sensor_id` varchar(10) NOT NULL,
  `sensor_site_id` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
  `sensor_type_id` int NOT NULL,
  `max` decimal(4,0) NOT NULL,
  `min` decimal(4,0) NOT NULL,
  `sensor_aspect_id` int NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `UNIQUE_sensor_id` (`sensor_id`),
  KEY `FR_ASPECT_ID` (`sensor_aspect_id`),
  KEY `fr_site_id` (`sensor_site_id`),
  KEY `FK3qvkpeexq73liv8vdwl9mc39g` (`sensor_type_id`),
  CONSTRAINT `FK3qvkpeexq73liv8vdwl9mc39g` FOREIGN KEY (`sensor_type_id`) REFERENCES `sensor_type` (`id`),
  CONSTRAINT `FR_ASPECT_ID` FOREIGN KEY (`sensor_aspect_id`) REFERENCES `sensor_aspect` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

/*Data for the table `sensor_info` */

insert  into `sensor_info`(`id`,`sensor_id`,`sensor_site_id`,`sensor_type_id`,`max`,`min`,`sensor_aspect_id`) values (1,'WD001','NO.1',1,'22','11',1),(2,'WD002','2',1,'23','434',2),(3,'WD003','3',1,'23','23',2),(4,'YB001','3',2,'23','23',4),(5,'YB002','2',2,'34','45',5),(6,'YB003','2',2,'45','45',4);

/*Table structure for table `sensor_point` */

DROP TABLE IF EXISTS `sensor_point`;

CREATE TABLE `sensor_point` (
  `id` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
  `x` float NOT NULL,
  `y` float NOT NULL,
  `z` float NOT NULL,
  `point_sensor_id` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

/*Data for the table `sensor_point` */

insert  into `sensor_point`(`id`,`x`,`y`,`z`,`point_sensor_id`) values ('1',1,1,1,'WD001');

/*Table structure for table `sensor_type` */

DROP TABLE IF EXISTS `sensor_type`;

CREATE TABLE `sensor_type` (
  `id` int NOT NULL AUTO_INCREMENT,
  `sensor_type` varchar(20) NOT NULL,
  `desc` varchar(40) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `UNIQUE_TYPE` (`sensor_type`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

/*Data for the table `sensor_type` */

insert  into `sensor_type`(`id`,`sensor_type`,`desc`) values (1,'WD_DATA','温度传感器'),(2,'YB_DATA','应变传感器'),(3,'YL_DATA','压力传感器'),(4,'CJ_DATA','冲击传感器'),(5,'WY_DATA','位移传感器'),(6,'ZD_DATA','震动传感器');

/*Table structure for table `topic` */

DROP TABLE IF EXISTS `topic`;

CREATE TABLE `topic` (
  `topic_id` int NOT NULL AUTO_INCREMENT,
  `topic_type` varchar(10) NOT NULL,
  `desc` varchar(30) DEFAULT NULL,
  PRIMARY KEY (`topic_id`),
  UNIQUE KEY `UNIQUE_TOPIC_TYPE` (`topic_type`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

/*Data for the table `topic` */

insert  into `topic`(`topic_id`,`topic_type`,`desc`) values (1,'WD_DATA','温度数据'),(2,'YB_DATA','应变数据'),(3,'YL_DATA','压力数据'),(4,'WY_DATA','位移数据'),(5,'CJ_DATA','冲击数据'),(6,'ZD_DATA','震动数据'),(7,'Status','传感器状态信息'),(8,'Dispose','传感器配置信息');


二、SpringBoot整合JPA多表查询

2.1 环境配置

  1. pom依赖
<!--Springboot data jpa-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<!--数据库连接驱动-->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>8.0.21</version>
		</dependency>

2.application.yml配置
如果是 application.properties,修改成application.yml,然后添加一下配置。

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/dstructure?useUnicode=true&characterEncoding=UTF-8&userSSL=false&serverTimezone=GMT%2B8
    username: root
    password: 123456
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true

2.2 建立数据库映射模型

  1. 常用的注解
 @Entity
 	作用:指定当前类是实体类。
 @Table
 	作用:指定实体类和表之间的对应关系。
 	属性:
 		name:指定数据库表的名称
 @Id
 	作用:指定当前字段是主键。
 @GeneratedValue
 	作用:指定主键的生成方式。。
 	属性:
 		strategy :指定主键生成策略。
 @Column
 	作用:指定实体类属性和数据库表之间的对应关系
 	属性:
 		name:指定数据库表的列名称。
 		unique:是否唯一  
 		nullable:是否可以为空  
 		inserttable:是否可以插入  
 		updateable:是否可以更新  
 		columnDefinition: 定义建表时创建此列的DDL  
 		secondaryTable: 从表名。如果此列不建在主表上(默认建在主表),该属性定义该列所在从表的名字搭建开发环境[重点]

在建立实体类时,第一步只需要对数据库基本属性映射就行,不需要考虑外键映射
切记,toString方法里不要打印了外键,只打印基本属性就行。否则可能会导致后续的实体关联中,由于相互嵌套造成程序堆栈溢出

  1. 建立 InfoModel
@Setter
@Getter
@Entity
@Table(name = "sensor_info")
public class InfoModel {

    @Id//声明当前私有属性为主键
    @GeneratedValue(strategy=GenerationType.IDENTITY) //配置主键的生成策略
    private int id;
    @Column(name = "max")
    private float max;
    @Column(name = "min")
    private float min;
    

    @Override
    public String toString() {
        return "InfoModel{" +
                "id=" + id +
                ", max=" + max +
                ", min=" + min +
                '}';
    }
}
  1. 建立TypeModel
@Setter
@Getter
@Entity
@Table(name = "sensor_type")
public class TypeModel {
    @Id//声明当前私有属性为主键
    @GeneratedValue(strategy=GenerationType.IDENTITY) //配置主键的生成策略
    private int id;
    @Column(name = "sensor_type")
    private String sensorType;
    @Column(name = "desc")
    private String desc;

    @Override
    public String toString() {
        return "TypeModel{" +
                "id=" + id +
                ", sensorType='" + sensorType + '\'' +
                ", desc='" + desc + '\'' +
                '}';
    }
}
  1. 建立AspectModel
@Setter
@Getter
@Entity
@Table(name = "sensor_aspect")
public class AspectModel {
    @Id//声明当前私有属性为主键
    @GeneratedValue(strategy=GenerationType.IDENTITY) //配置主键的生成策略
    private int id;
    @Column(name = "aspect_type")
    private String aspectType;
    @Column(name = "desc")
    private String desc;

    @Override
    public String toString() {
        return "AspectModel{" +
                "id=" + id +
                ", aspectType='" + aspectType + '\'' +
                ", desc='" + desc + '\'' +
                '}';
    }
}

2.2 编写DAO层接口

  1. InfoDao
/*
*  第一个参数:接口对应的 实体对象
*  第二个参数:实体的主键数据包装类
* */
public interface InfoDao extends JpaRepository<InfoModel,Integer> {
}
  1. TypeDao
public interface TypeDao extends JpaRepository<TypeModel,Integer> {
}
  1. AspectDao
public interface AspectDao extends JpaRepository<AspectModel,Integer> {
}

2.3 在测试类中,测试数据库映射是否正确。

@SpringBootTest
class DatadumpApplicationTests {

	@Autowired
	private InfoDao infoDao;
	@Test
	void baseTest() {
		List<InfoModel> infos = infoDao.findAll();
		for (InfoModel info : infos) {
			System.out.println(info);
		}
	}
}

发现控制台打印正常,说明实体类与数据库映射成功:

InfoModel{id=1, max=22.0, min=11.0}
InfoModel{id=2, max=23.0, min=434.0}
InfoModel{id=3, max=23.0, min=23.0}
InfoModel{id=4, max=23.0, min=23.0}
InfoModel{id=5, max=34.0, min=45.0}
InfoModel{id=6, max=45.0, min=45.0}

2.3 在JPA框架中表关系的分析步骤

  • 第一步:首先确定两张表之间的关系。

    如果关系确定错了,后面做的所有操作就都不可能正确。

  • 第二步:在数据库中实现两张表的关系

  • 第三步:在实体类中描述出两个实体的关系

  • 第四步:配置出实体类和数据库表的关系映射(重点)

在进行多表关联查询时候,(本文章是一对多)第一步,要分清谁是“多”的一方,谁是“一”的一方。
比如 学生与班级,一个学生只属于一个班级,一个班级有很多学生。因此学生是,班级是
而在本文的数据表中,一个传感器只有一个类型type,因此sensor_infosensor_type是一对多的关系,sensor_info,而sensor_type
同理sensor_infosensor_aspect是一对多的关系,sensor_info,而sensor_aspect

2.4 映射的注解说明

@OneToMany:
   	作用:建立一对多的关系映射
    属性:
    	targetEntityClass:指定多的多方的类的字节码
    	mappedBy:指定从表实体类中引用主表对象的名称。
    	cascade:指定要使用的级联操作
    	fetch:指定是否采用延迟加载
    	orphanRemoval:是否使用孤儿删除

@ManyToOne
    作用:建立多对一的关系
    属性:
    	targetEntityClass:指定一的一方实体类字节码
    	cascade:指定要使用的级联操作
    	fetch:指定是否采用延迟加载
    	optional:关联是否可选。如果设置为false,则必须始终存在非空关系。

@JoinColumn
     作用:用于定义主键字段和外键字段的对应关系。
     属性:
    	name:指定外键字段的名称
    	referencedColumnName:指定引用主表的主键字段名称
    	unique:是否唯一。默认值不唯一
    	nullable:是否允许为空。默认值允许。
    	insertable:是否允许插入。默认值允许。
    	updatable:是否允许更新。默认值允许。
    	columnDefinition:列的定义信息。

2.5 多表关联查询

  1. 的一方的实体类添加外键,即在InfoModel类中添加以下代码:
    /*
     *   sensor_info 和 sensor_type 表关联
     *   sensor_info 是多方
     *
     *  name是指的是多方(从表)的外键(建立在从表)
     *  referencedColumnName 指的是引用一方(主表)的主键字段名称
     *
     *
     * */
    @ManyToOne(targetEntity = TypeModel.class)
    @JoinColumn(name = "sensor_type_id",referencedColumnName = "id")
    private TypeModel type;

    @ManyToOne(targetEntity = AspectModel.class)
    @JoinColumn(name = "sensor_aspect_id",referencedColumnName = "id")
    private AspectModel aspect;
    
  1. 一方 添加外键关联

TypeModel

 /*
     *  mappedBy 是参照 多方的映射关系。只需要 填上对方的相应的外键的私有属性
     *  一对多时候需要加上,fetch = FetchType.EAGER, 设定其在映射的时候立刻加载数据库,避免懒加载造成的 会话关闭 而无法加载数据
     *  这里的type 就是 InfoModel中的私有属性type
     * */
    @OneToMany(mappedBy = "type",targetEntity = InfoModel.class,fetch = FetchType.EAGER)
    private Set<InfoModel> infos=new HashSet<InfoModel>();

AspectModel

/*
     *  mappedBy 是参照 多方的映射关系。只需要 填上对方的相应的外键的私有属性
     *  一对多时候需要加上,fetch = FetchType.EAGER, 设定其在映射的时候立刻加载数据库,避免懒加载造成的 会话关闭 而无法加载数据
     *  这里的type 就是 InfoModel中的私有属性 aspect
     * */
    @OneToMany(mappedBy = "aspect",targetEntity = InfoModel.class,fetch = FetchType.EAGER)
    private Set<InfoModel> infos=new HashSet<InfoModel>();

2.6 测试多表查询

@SpringBootTest
class DatadumpApplicationTests {

	@Autowired
	private InfoDao infoDao;
	@Autowired
	private TypeDao typeDao;
	@Autowired
	private AspectDao aspectDao;


	@Test
	void multiTableQueryTest() {
		List<InfoModel> infos = infoDao.findAll();
		for (InfoModel info : infos) {
			System.out.println(info+"----传感器种类为:"+info.getType()+"----传感器的面信息为:"+info.getAspect());
		}
		System.out.println("==================");
		List<TypeModel> types = typeDao.findAll();
		for (TypeModel type : types) {
			System.out.println(type+"---该类型包含以下传感器:"+type.getInfos());
		}
	}
}

发现控制台进行多表查询:

InfoModel{id=1, max=22.0, min=11.0}----传感器种类为:TypeModel{id=1, sensorType='WD_DATA', desc='温度传感器'}----传感器的面信息为:AspectModel{id=1, aspectType='top', desc='上'}
InfoModel{id=2, max=23.0, min=434.0}----传感器种类为:TypeModel{id=1, sensorType='WD_DATA', desc='温度传感器'}----传感器的面信息为:AspectModel{id=2, aspectType='bottom', desc='下'}
InfoModel{id=3, max=23.0, min=23.0}----传感器种类为:TypeModel{id=1, sensorType='WD_DATA', desc='温度传感器'}----传感器的面信息为:AspectModel{id=2, aspectType='bottom', desc='下'}
InfoModel{id=4, max=23.0, min=23.0}----传感器种类为:TypeModel{id=2, sensorType='YB_DATA', desc='应变传感器'}----传感器的面信息为:AspectModel{id=4, aspectType='right', desc='右'}
InfoModel{id=5, max=34.0, min=45.0}----传感器种类为:TypeModel{id=2, sensorType='YB_DATA', desc='应变传感器'}----传感器的面信息为:AspectModel{id=5, aspectType='front', desc='前'}
InfoModel{id=6, max=45.0, min=45.0}----传感器种类为:TypeModel{id=2, sensorType='YB_DATA', desc='应变传感器'}----传感器的面信息为:AspectModel{id=4, aspectType='right', desc='右'}
==================
TypeModel{id=1, sensorType='WD_DATA', desc='温度传感器'}---该类型包含以下传感器:[InfoModel{id=1, max=22.0, min=11.0}, InfoModel{id=2, max=23.0, min=434.0}, InfoModel{id=3, max=23.0, min=23.0}]
TypeModel{id=2, sensorType='YB_DATA', desc='应变传感器'}---该类型包含以下传感器:[InfoModel{id=5, max=34.0, min=45.0}, InfoModel{id=4, max=23.0, min=23.0}, InfoModel{id=6, max=45.0, min=45.0}]
TypeModel{id=3, sensorType='YL_DATA', desc='压力传感器'}---该类型包含以下传感器:[]
TypeModel{id=4, sensorType='CJ_DATA', desc='冲击传感器'}---该类型包含以下传感器:[]
TypeModel{id=5, sensorType='WY_DATA', desc='位移传感器'}---该类型包含以下传感器:[]
TypeModel{id=6, sensorType='ZD_DATA', desc='震动传感器'}---该类型包含以下传感器:[]
  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JPA 中,如果你想要关联表的部分字段,可以使用 `@JoinColumn` 注解和 `@ManyToOne` 或 `@OneToOne` 注解来定义关联关系。然后,你可以使用 `@JoinColumn` 注解的 `referencedColumnName` 属性来指定关联表中的字段。 下面是一个示例: ```java @Entity @Table(name = "employee") public class Employee { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; @ManyToOne @JoinColumn(name = "department_id", referencedColumnName = "id") private Department department; // Getter and Setter methods } @Entity @Table(name = "department") public class Department { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; // Getter and Setter methods } ``` 在上面的示例中,`Employee` 实体类与 `Department` 实体类通过 `department_id` 字段进行关联。如果你只想要获取关联表 `Department` 的部分字段,可以在 `Department` 实体类中定义一个新的类似于 DTO(数据传输对象)的类,该类仅包含需要的字段。 例如,假设你只想要获取部门的名称(字段 `name`),你可以创建一个名为 `DepartmentDto` 的类: ```java public class DepartmentDto { private String name; public DepartmentDto(String name) { this.name = name; } // Getter and Setter methods } ``` 然后,在 `Employee` 实体类中,你可以添加一个返回 `DepartmentDto` 类型的方法,用于获取关联表 `Department` 的部分字段: ```java @Entity @Table(name = "employee") public class Employee { // ... public DepartmentDto getDepartmentName() { return new DepartmentDto(department.getName()); } // ... } ``` 这样,当你查询 `Employee` 实体时,调用 `getDepartmentName()` 方法就可以获取到部门的名称。 请注意,上述示例中的代码仅为演示目的,并未完整展示 JPA 的所有配置和方法。实际使用时,你可能需要根据自己的业务需求进行适当的调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值