【Spring Data JPA】08 多表关系 Part1 一对多关系操作
表关系概述:
1、一 对应 一
一对夫妻,一个男人只能有一个老婆,一个女人只能有一个老公,即这种对应关系
2、一 对应 多 【多对一】
一个年级具有多个班级,一个班级具有对应的所属年级,即这种上下层级关系或者其他类似的
3、多 对应 多
授课老师和所授课程 一个授课老师可以教授多门课程,一个课程也可以被多个老师教授。即多重关系

数据库表设计的实现:
一对一只需要一个关联键即可
一对多,需要建立主表(一)从表(多)关系,从表设置外键来检索主表信息
多对多,对应的关系无法用外键实现,所以需要建立关联表,用来存储关联关系映射
实体类设计的实现:
一对一,对象附属,作为属性存在【包含】
一对多,对象仅表示一个记录,所以更改为一个对象的集合容器作为属性存在,或者使用继承实现
多对多
测试环境搭建:
customer.sql
/*创建客户表*/
CREATE TABLE cst_customer (
cust_id bigint(32) NOT NULL AUTO_INCREMENT COMMENT '客户编号(主键)',
cust_name varchar(32) NOT NULL COMMENT '客户名称(公司名称)',
cust_source varchar(32) DEFAULT NULL COMMENT '客户信息来源',
cust_industry varchar(32) DEFAULT NULL COMMENT '客户所属行业',
cust_level varchar(32) DEFAULT NULL COMMENT '客户级别',
cust_address varchar(128) DEFAULT NULL COMMENT '客户联系地址',
cust_phone varchar(64) DEFAULT NULL COMMENT '客户联系电话',
PRIMARY KEY (`cust_id`)
) ENGINE=InnoDB AUTO_INCREMENT=94 DEFAULT CHARSET=utf8; /*创建联系人表*/
CREATE TABLE cst_linkman (
lkm_id bigint(32) NOT NULL AUTO_INCREMENT COMMENT '联系人编号(主键)',
lkm_name varchar(16) DEFAULT NULL COMMENT '联系人姓名',
lkm_gender char(1) DEFAULT NULL COMMENT '联系人性别',
lkm_phone varchar(16) DEFAULT NULL COMMENT '联系人办公电话',
lkm_mobile varchar(16) DEFAULT NULL COMMENT '联系人手机',
lkm_email varchar(64) DEFAULT NULL COMMENT '联系人邮箱',
lkm_position varchar(16) DEFAULT NULL COMMENT '联系人职位',
lkm_memo varchar(512) DEFAULT NULL COMMENT '联系人备注',
lkm_cust_id bigint(32) NOT NULL COMMENT '客户id(外键)',
PRIMARY KEY (`lkm_id`),
KEY `FK_cst_linkman_lkm_cust_id` (`lkm_cust_id`),
CONSTRAINT `FK_cst_linkman_lkm_cust_id` FOREIGN KEY (`lkm_cust_id`) REFERENCES `cst_customer` (`cust_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
客户:
package cn.echo42.domain; import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor; import javax.persistence.*;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set; /**
* @author DaiZhiZhou
* @file Spring-Data-JPA
* @create 2020-08-01 9:48
*/ @Data
@AllArgsConstructor
@NoArgsConstructor
@Entity//表示当前类是一个实体类
@Table(name="cst_customer")//建立当前实体类和表之间的对应关系
public class Customer implements Serializable { @Id//表明当前私有属性是主键
@GeneratedValue(strategy = GenerationType.IDENTITY)//指定主键的生成策略
@Column(name = "cust_id")//指定和数据库表中的cust_id列对应
private Long custId;
@Column(name = "cust_name")//指定和数据库表中的cust_name列对应
private String custName;
@Column(name = "cust_source")//指定和数据库表中的cust_source列对应
private String custSource;
@Column(name = "cust_industry")//指定和数据库表中的cust_industry列对应
private String custIndustry;
@Column(name = "cust_level")//指定和数据库表中的cust_level列对应
private String custLevel;
@Column(name = "cust_address")//指定和数据库表中的cust_address列对应
private String custAddress;
@Column(name = "cust_phone")//指定和数据库表中的cust_phone列对应
private String custPhone; //配置客户和联系人的一对多关系
@OneToMany(targetEntity = LinkMan.class)
@JoinColumn(name = "lkm_cust_id", referencedColumnName = "cust_id")
private Set<LinkMan> linkmans = new HashSet<LinkMan>(0);
}
联系人:
package cn.echo42.domain; import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor; import javax.persistence.*;
import java.io.Serializable; /**
* @author DaiZhiZhou
* @file Spring-Data-JPA
* @create 2020-08-01 9:49
*/ @Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name="cst_linkman")
public class LinkMan implements Serializable {
@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
@Column(name="lkm_id")
private Long lkmId;
@Column(name="lkm_name")
private String lkmName;
@Column(name="lkm_gender")
private String lkmGender;
@Column(name="lkm_phone")
private String lkmPhone;
@Column(name="lkm_mobile")
private String lkmMobile;
@Column(name="lkm_email")
private String lkmEmail;
@Column(name="lkm_position")
private String lkmPosition;
@Column(name="lkm_memo")
private String lkmMemo; //多对一关系映射:多个联系人对应客户
@ManyToOne(targetEntity=Customer.class)
@JoinColumn(name="lkm_cust_id",referencedColumnName="cust_id")
private Customer customer;//用它的主键,对应联系人表中的外键
}
编写对应的Dao接口:
package cn.echo42.repository; import cn.echo42.domain.Customer;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor; /**
* @author DaiZhiZhou
* @file Spring-Data-JPA
* @create 2020-08-01 9:54
*/
public interface CustomerRepository extends JpaRepository<Customer, Integer> , JpaSpecificationExecutor<Customer> {
}
---------------------------------------------------------------------------------------------------------------------------
package cn.echo42.repository; import cn.echo42.domain.LinkMan;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor; /**
* @author DaiZhiZhou
* @file Spring-Data-JPA
* @create 2020-08-01 9:54
*/
public interface LinkManRepository extends JpaRepository<LinkMan, Integer>, JpaSpecificationExecutor<LinkMan> {
}
注解配置Jpa的配置信息
自动创建数据库表设置:create每次执行创建数据库表,update没有表才会创建

多表关系插入:
创建测试类:
import cn.echo42.config.ApplicationConfiguration;
import cn.echo42.domain.Customer;
import cn.echo42.domain.LinkMan;
import cn.echo42.repository.CustomerRepository;
import cn.echo42.repository.LinkManRepository;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional; /**
* @author DaiZhiZhou
* @file Spring-Data-JPA
* @create 2020-08-01 10:06
*/ @RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = ApplicationConfiguration.class)
public class MTRQ_Test { @Autowired
CustomerRepository customerRepository;
@Autowired
LinkManRepository linkManRepository; @Test
@Transactional
@Rollback(false) // 设置不自动回滚
public void addExecute(){
// 创建一个客户 & 联系人
Customer customer = new Customer();
customer.setCustName("百度"); LinkMan linkMan = new LinkMan();
linkMan.setLkmName("小李"); customerRepository.save(customer);
linkManRepository.save(linkMan);
} }
显示结果:
[org.hibernate.SQL]-alter table cst_linkman drop foreign key FKh9yp1nql5227xxcopuxqx2e7q
[org.hibernate.SQL]-drop table if exists cst_customer
[org.hibernate.SQL]-drop table if exists cst_linkman
[org.hibernate.SQL]-drop table if exists sys_user
[org.hibernate.SQL]-create table cst_customer (cust_id bigint not null auto_increment, cust_address varchar(255), cust_industry varchar(255), cust_level varchar(255), cust_name varchar(255), cust_phone varchar(255), cust_source varchar(255), primary key (cust_id))
[org.hibernate.SQL]-create table cst_linkman (lkm_id bigint not null auto_increment, lkm_email varchar(255), lkm_gender varchar(255), lkm_memo varchar(255), lkm_mobile varchar(255), lkm_name varchar(255), lkm_phone varchar(255), lkm_position varchar(255), lkm_cust_id bigint, primary key (lkm_id))
[org.hibernate.SQL]-create table sys_user (user_id integer not null auto_increment, user_is_del integer, user_name varchar(255), user_password varchar(255), user_status integer, primary key (user_id))
[org.hibernate.SQL]-alter table cst_linkman add constraint FKh9yp1nql5227xxcopuxqx2e7q foreign key (lkm_cust_id) references cst_customer (cust_id)
数据库查看:


可以看到联系人的外键这里没有百度,两个记录不具备任何联系,是相互独立的存在
要建立关系,我们则需要在联系人的集合中增加对象,表现出我们的关联关系:
@Test
@Transactional
@Rollback(false) // 设置不自动回滚
public void addExecute(){
// 创建一个客户 & 联系人
Customer customer = new Customer();
customer.setCustName("百度"); LinkMan linkMan = new LinkMan();
linkMan.setLkmName("小李"); // 配置多表关系
customer.getLinkmans().add(linkMan); customerRepository.save(customer);
linkManRepository.save(linkMan);
}
再次执行的结果:

可以看到已经成功绑定到了
从客户表的角度上观察:
发送了两条插入语句,
一条插入记录
一条更新外键
我们配置了客户到联系人的关系,客户可以对外键进行维护
我们也可以换过来,从联系人的角度上关联客户:
@Test
@Transactional
@Rollback(false) // 设置不自动回滚
public void addExecute2(){
// 创建一个客户 & 联系人
Customer customer = new Customer();
customer.setCustName("百度"); LinkMan linkMan = new LinkMan();
linkMan.setLkmName("小李"); // 在联系人的属性中设置客户,以关联
linkMan.setCustomer(customer); customerRepository.save(customer);
linkManRepository.save(linkMan);
}
结果一样
但是如果同时绑定设置就会出错了:
@Test
@Transactional
@Rollback(false) // 设置不自动回滚
public void addExecute3(){
// 创建一个客户 & 联系人
Customer customer = new Customer();
customer.setCustName("百度"); LinkMan linkMan = new LinkMan();
linkMan.setLkmName("小李"); linkMan.setCustomer(customer);
customer.getLinkmans().add(linkMan); customerRepository.save(customer);
linkManRepository.save(linkMan);
}
双方彼此的对象在不断相互引用:

解决方案是任意的一方放弃主键的维护即可:
原先联系人类的维护关系:

如果放弃,就需要这样设置:

But it not work .... 不晓得为什么,难道又有新的策略了?
【发现是使用了Lombok原因,这里也只要记住一点,放弃主键维护注意下Lombok辅助的问题】
级联操作:
ALL 所有操作 MERGE 更新
PERSIST 持久化 -> 保存
REMOVE 移除

取消之前设置的创建数据库表:

测试级联删除:
@Test
@Transactional
@Rollback(false) // 设置不自动回滚
public void addExecute4(){
Specification<Customer> customerSpecification = (Specification<Customer>) (root, criteriaQuery, criteriaBuilder) -> {
// 获取比较的属性
Path<Object> cust_id = root.get("custId");
// 模糊要求指定参数类型
return criteriaBuilder.equal(cust_id, 1);
};
Optional<Customer> customerOptional = customerRepository.findOne(customerSpecification); Customer customer = customerOptional.get(); customerRepository.delete(customer);
}
【Spring Data JPA】08 多表关系 Part1 一对多关系操作的更多相关文章
- Spring Data JPA 实现多表关联查询
本文地址:https://liuyanzhao.com/6978.html 最近抽出时间来做博客,数据库操作使用的是 JPA,相对比 Mybatis 而言,JPA 单表操作非常方便,增删改查都已经写好 ...
- spring data jpa关联查询(一对一、一对多、多对多)
在实际过往的项目中,常用的查询操作有:1.单表查询,2.一对一查询(主表和详情表)3.一对多查询(一张主表,多张子表)4.多对多查询(如权限控制,用户.角色多对多).做个总结,所以废话不多说. 使用i ...
- spring data jpa之Auditing 表的创建时间,更新时间自动生成策略
java实际编程中,几乎每一张表都会有createTime和updateTime字段,spring的优秀之处在于只要用几个注解,就帮我们解决该类问题,具体实现: 1,实体类添加注解: @EntityL ...
- Spring Boot:整合Spring Data JPA
综合概述 JPA是Java Persistence API的简称,是一套Sun官方提出的Java持久化规范.其设计目标主要是为了简化现有的持久化开发工作和整合ORM技术,它为Java开发人员提供了一种 ...
- 集成Spring Data JPA
1.Spring Data JPA简介 Spring Data是一个用于简化数据访问,并支持云服务的开源框 使用完成Spring Data JPA对user表的CRUD操作. 2.步骤 1.创建工程勾 ...
- JDBC、ORM、JPA、Spring Data JPA,傻傻分不清楚?一文带你厘清个中曲直,给你个选择SpringDataJPA的理由!
序言 Spring Data JPA作为Spring Data中对于关系型数据库支持的一种框架技术,属于ORM的一种,通过得当的使用,可以大大简化开发过程中对于数据操作的复杂度. 本文档隶属于< ...
- Spring Boot 入门系列(二十七)使用Spring Data JPA 自定义查询如此简单,完全不需要写SQL!
前面讲了Spring Boot 整合Spring Boot JPA,实现JPA 的增.删.改.查的功能.JPA使用非常简单,只需继承JpaRepository ,无需任何数据访问层和sql语句即可实现 ...
- 最近项目中使用Spring data jpa 踩过的坑
最近在做一个有关OA项目中使用spring data JPA 操作数据库,结果遇到了补个不可思议的麻烦.困惑了好久. 首先看一下问题吧,这就是当时测试“设置角色时,需要首先删除该用户已经拥有的角色时” ...
- spring data jpa hibernate jpa 三者之间的关系
JPA规范与ORM框架之间的关系是怎样的呢? JPA规范本质上就是一种ORM规范,注意不是ORM框架——因为JPA并未提供ORM实现,它只是制订了一些规范,提供了一些编程的API接口,但具体实现则由服 ...
- Spring Data JPA 多个实体类表联合视图查询
Spring Data JPA 查询数据库时,如果两个表有关联,那么就设个外键,在查询的时候用Specification创建Join 查询便可.但是只支持左连接,不支持右连接,虽说左右连接反过来就能实 ...
随机推荐
- 鼠标悬浮显示鼠标停留数据的内容 elementui + vue
先看效果图 直接上代码 <el-tabs v-model="activeName" @tab-click="handleClick"> <el ...
- 剑指Offer-54.字符流中第一个不重复的字符(C++/Java)
题目: 请实现一个函数用来找出字符流中第一个只出现一次的字符.例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g".当从该字符流中读出前 ...
- C#.NET 读取PFX私钥证书并导出PEM格式私钥
项目nuget引用 BouncyCastle. 读取证书 X509Certificate2 x509 = new X509Certificate2(lblPfxPath.Text, txtPfxPwd ...
- 在Linux中使用crontab
背景 虽然不是专业运维,但是在嵌入式开发中还是需要懂一点的.部门内部搞服务器最厉害的就是我了,汗. 参考: https://blog.csdn.net/longgeaisisi/article/det ...
- 移植 uCos-III 3.03 到 STM32F429 上
背景 通过STM32 的学习,我们可以往更深层次的地方走,尝试系统上的一些开发. STM32: F429(StdPeriph) uCos-III : v3.04 + 3.03 有关说明: 在移植 3. ...
- 【Python】基于动态规划和K聚类的彩色图片压缩算法
引言 当想要压缩一张彩色图像时,彩色图像通常由数百万个颜色值组成,每个颜色值都由红.绿.蓝三个分量组成.因此,如果我们直接对图像的每个像素进行编码,会导致非常大的数据量.为了减少数据量,我们可以尝试减 ...
- 【Python】用Python把从mysql统计的结果数据转成表格形式的图片并推送到钉钉群
** python把数据转为图片 / python推送图片到钉钉群 ** 需求:通过python访问mysql数据库,统计业务相关数据.把统计的结果数据生成表格形式的图片并发送到钉钉群里. 一:Cen ...
- React Context 的使用
使用React开发应用程序时,如果多个组件需要共享数据,可以把数据放到父组件中,通过属性向下传递给子组件.但当组件嵌套较深时,两个最底层的组件要想共享数据,那就霜要把数据放到最顶层的组件中,然后再一层 ...
- debian12 笔记
前言 最近在win10通过wsl安装了debian linux子系统(wsl2安装报错了..所以改成了wsl),没想到安装的还是最新的debian12 (Bookworm).的确和ubuntu有些不一 ...
- linux 查看端口状态
查看端口 netstat -tlun 查看端口被那个服务占用 netstat -tunlp |grep 8080