表关系概述:

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. cookie cookie的获取

       什么是 cookie                  cookie 是 浏览器 的 一种功能                 是 浏览器 用来 存储 前端数据的一种 存储机制          ...

  2. Python 潮流周刊#54:ChatTTS 强大的文本生成语音模型

    本周刊由 Python猫 出品,精心筛选国内外的 250+ 信息源,为你挑选最值得分享的文章.教程.开源项目.软件工具.播客和视频.热门话题等内容.愿景:帮助所有读者精进 Python 技术,并增长职 ...

  3. runliuv MSDN I TELL YOU

    runliuv MSDN I TELL YOU 老站点:WIN SEVER ,VISUAL STUDIO 早期版本 老站点:https://msdn.itellyou.cn/ 新站点:最近的WIN10 ...

  4. redis缓存详解(从入门到精通)

    引言 Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库.缓存和消息中间件. 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(l ...

  5. url编码和解码分析URLEncoder.encode和URLDecoder.decode

    url编码和解码分析 1.Get请求会将参数做默认的url解码操作,接口接收到的值是Get解码后的值.2.可以将Get操作修改成Post操作,这样不会url解码.可以在接口中做url解码.3.在多次传 ...

  6. es6.6.1 索引的增加,查询,修改,删除

    1.新增 test2/user2/1/_create PUT操作{"name":"qiqi","age":17} 2.查询 test2/us ...

  7. C#/.NET/.NET Core拾遗补漏合集(24年6月更新)

    前言 在这个快速发展的技术世界中,时常会有一些重要的知识点.信息或细节被忽略或遗漏.<C#/.NET/.NET Core拾遗补漏>专栏我们将探讨一些可能被忽略或遗漏的重要知识点.信息或细节 ...

  8. 汽车生产车间PMC组态画面应该怎么设计

    通常我们所说的汽车制造四大工艺指的是:冲压.焊装.涂装.总装.一般来说,在汽车制造工厂,这四大工艺分别对应四大车间.本文结合一些实际应用案例,向大家展示一下 TopStack 在汽车制造业各工艺车间中 ...

  9. “科来杯”第九届山东省大学生网络安全技能大赛决赛部分wp

      1.损坏的流量包 wireshark打不开,丢进winhex里,找关键字flag 哎,没找到. 那就仔细看看,在最后发现一串类似base64的密文 base64解密 得到flag 2.签到题 一个 ...

  10. yb课堂 核心数据库表字段设计和测试数据准备 《一》

    设计对应的表字段(统一使用Innodb引擎,mysql5.7) video_banner video chapter episode video_order user 数据库脚本 CREATE TAB ...