【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 查询便可.但是只支持左连接,不支持右连接,虽说左右连接反过来就能实 ...
随机推荐
- cookie cookie的获取
什么是 cookie cookie 是 浏览器 的 一种功能 是 浏览器 用来 存储 前端数据的一种 存储机制 ...
- Python 潮流周刊#54:ChatTTS 强大的文本生成语音模型
本周刊由 Python猫 出品,精心筛选国内外的 250+ 信息源,为你挑选最值得分享的文章.教程.开源项目.软件工具.播客和视频.热门话题等内容.愿景:帮助所有读者精进 Python 技术,并增长职 ...
- runliuv MSDN I TELL YOU
runliuv MSDN I TELL YOU 老站点:WIN SEVER ,VISUAL STUDIO 早期版本 老站点:https://msdn.itellyou.cn/ 新站点:最近的WIN10 ...
- redis缓存详解(从入门到精通)
引言 Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库.缓存和消息中间件. 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(l ...
- url编码和解码分析URLEncoder.encode和URLDecoder.decode
url编码和解码分析 1.Get请求会将参数做默认的url解码操作,接口接收到的值是Get解码后的值.2.可以将Get操作修改成Post操作,这样不会url解码.可以在接口中做url解码.3.在多次传 ...
- es6.6.1 索引的增加,查询,修改,删除
1.新增 test2/user2/1/_create PUT操作{"name":"qiqi","age":17} 2.查询 test2/us ...
- C#/.NET/.NET Core拾遗补漏合集(24年6月更新)
前言 在这个快速发展的技术世界中,时常会有一些重要的知识点.信息或细节被忽略或遗漏.<C#/.NET/.NET Core拾遗补漏>专栏我们将探讨一些可能被忽略或遗漏的重要知识点.信息或细节 ...
- 汽车生产车间PMC组态画面应该怎么设计
通常我们所说的汽车制造四大工艺指的是:冲压.焊装.涂装.总装.一般来说,在汽车制造工厂,这四大工艺分别对应四大车间.本文结合一些实际应用案例,向大家展示一下 TopStack 在汽车制造业各工艺车间中 ...
- “科来杯”第九届山东省大学生网络安全技能大赛决赛部分wp
1.损坏的流量包 wireshark打不开,丢进winhex里,找关键字flag 哎,没找到. 那就仔细看看,在最后发现一串类似base64的密文 base64解密 得到flag 2.签到题 一个 ...
- yb课堂 核心数据库表字段设计和测试数据准备 《一》
设计对应的表字段(统一使用Innodb引擎,mysql5.7) video_banner video chapter episode video_order user 数据库脚本 CREATE TAB ...