表关系概述:

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 一对多关系操作的更多相关文章

  1. Spring Data JPA 实现多表关联查询

    本文地址:https://liuyanzhao.com/6978.html 最近抽出时间来做博客,数据库操作使用的是 JPA,相对比 Mybatis 而言,JPA 单表操作非常方便,增删改查都已经写好 ...

  2. spring data jpa关联查询(一对一、一对多、多对多)

    在实际过往的项目中,常用的查询操作有:1.单表查询,2.一对一查询(主表和详情表)3.一对多查询(一张主表,多张子表)4.多对多查询(如权限控制,用户.角色多对多).做个总结,所以废话不多说. 使用i ...

  3. spring data jpa之Auditing 表的创建时间,更新时间自动生成策略

    java实际编程中,几乎每一张表都会有createTime和updateTime字段,spring的优秀之处在于只要用几个注解,就帮我们解决该类问题,具体实现: 1,实体类添加注解: @EntityL ...

  4. Spring Boot:整合Spring Data JPA

    综合概述 JPA是Java Persistence API的简称,是一套Sun官方提出的Java持久化规范.其设计目标主要是为了简化现有的持久化开发工作和整合ORM技术,它为Java开发人员提供了一种 ...

  5. 集成Spring Data JPA

    1.Spring Data JPA简介 Spring Data是一个用于简化数据访问,并支持云服务的开源框 使用完成Spring Data JPA对user表的CRUD操作. 2.步骤 1.创建工程勾 ...

  6. JDBC、ORM、JPA、Spring Data JPA,傻傻分不清楚?一文带你厘清个中曲直,给你个选择SpringDataJPA的理由!

    序言 Spring Data JPA作为Spring Data中对于关系型数据库支持的一种框架技术,属于ORM的一种,通过得当的使用,可以大大简化开发过程中对于数据操作的复杂度. 本文档隶属于< ...

  7. Spring Boot 入门系列(二十七)使用Spring Data JPA 自定义查询如此简单,完全不需要写SQL!

    前面讲了Spring Boot 整合Spring Boot JPA,实现JPA 的增.删.改.查的功能.JPA使用非常简单,只需继承JpaRepository ,无需任何数据访问层和sql语句即可实现 ...

  8. 最近项目中使用Spring data jpa 踩过的坑

    最近在做一个有关OA项目中使用spring data JPA 操作数据库,结果遇到了补个不可思议的麻烦.困惑了好久. 首先看一下问题吧,这就是当时测试“设置角色时,需要首先删除该用户已经拥有的角色时” ...

  9. spring data jpa hibernate jpa 三者之间的关系

    JPA规范与ORM框架之间的关系是怎样的呢? JPA规范本质上就是一种ORM规范,注意不是ORM框架——因为JPA并未提供ORM实现,它只是制订了一些规范,提供了一些编程的API接口,但具体实现则由服 ...

  10. Spring Data JPA 多个实体类表联合视图查询

    Spring Data JPA 查询数据库时,如果两个表有关联,那么就设个外键,在查询的时候用Specification创建Join 查询便可.但是只支持左连接,不支持右连接,虽说左右连接反过来就能实 ...

随机推荐

  1. 鼠标悬浮显示鼠标停留数据的内容 elementui + vue

    先看效果图 直接上代码 <el-tabs v-model="activeName" @tab-click="handleClick"> <el ...

  2. 剑指Offer-54.字符流中第一个不重复的字符(C++/Java)

    题目: 请实现一个函数用来找出字符流中第一个只出现一次的字符.例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g".当从该字符流中读出前 ...

  3. C#.NET 读取PFX私钥证书并导出PEM格式私钥

    项目nuget引用 BouncyCastle. 读取证书 X509Certificate2 x509 = new X509Certificate2(lblPfxPath.Text, txtPfxPwd ...

  4. 在Linux中使用crontab

    背景 虽然不是专业运维,但是在嵌入式开发中还是需要懂一点的.部门内部搞服务器最厉害的就是我了,汗. 参考: https://blog.csdn.net/longgeaisisi/article/det ...

  5. 移植 uCos-III 3.03 到 STM32F429 上

    背景 通过STM32 的学习,我们可以往更深层次的地方走,尝试系统上的一些开发. STM32: F429(StdPeriph) uCos-III : v3.04 + 3.03 有关说明: 在移植 3. ...

  6. 【Python】基于动态规划和K聚类的彩色图片压缩算法

    引言 当想要压缩一张彩色图像时,彩色图像通常由数百万个颜色值组成,每个颜色值都由红.绿.蓝三个分量组成.因此,如果我们直接对图像的每个像素进行编码,会导致非常大的数据量.为了减少数据量,我们可以尝试减 ...

  7. 【Python】用Python把从mysql统计的结果数据转成表格形式的图片并推送到钉钉群

    ** python把数据转为图片 / python推送图片到钉钉群 ** 需求:通过python访问mysql数据库,统计业务相关数据.把统计的结果数据生成表格形式的图片并发送到钉钉群里. 一:Cen ...

  8. React Context 的使用

    使用React开发应用程序时,如果多个组件需要共享数据,可以把数据放到父组件中,通过属性向下传递给子组件.但当组件嵌套较深时,两个最底层的组件要想共享数据,那就霜要把数据放到最顶层的组件中,然后再一层 ...

  9. debian12 笔记

    前言 最近在win10通过wsl安装了debian linux子系统(wsl2安装报错了..所以改成了wsl),没想到安装的还是最新的debian12 (Bookworm).的确和ubuntu有些不一 ...

  10. linux 查看端口状态

    查看端口 netstat -tlun 查看端口被那个服务占用 netstat -tunlp |grep 8080