快速入门spring data jpa 2,多表操作,逻辑删除
spring data jpa
- 多表
- 一、多表
- 实体类
- 实体类接口
- 数据库
- 注意
- 表关系
- 使用
- 测试(一对多)
- 注意
- 一,(非级联,主表有维护外键的权利)
- 二,(级联)
- 测试(多对多)
- 开始,新建两各实体类
- 实体类接口
- 两实体类建立关系
- 使用
- 注意:双向维护(不可行)
- 放弃外键维护
- 多对多级联操作
- 对象导航查询
- no Session
- @Where
- 逻辑删除
多表
单表jpa
提示:以下是本篇文章正文内容,下面案例可供参考
一、多表
在之前单表的环境上操作,注意说的是环境
如坐标,spring配置文件等
具体查看在,
单表jpa
实体类
Customer类@Entity
@Table(name = "cst_customer")
public class Customer {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)@Column(name = "cust_id")private Long custId;@Column(name = "cust_name")private String custName;@Column(name = "cust_source")private String custSource;@Column(name = "cust_industry")private String custIndustry;@Column(name = "cust_level")private String custLevel;@Column(name = "cust_address")private String custAddress;@Column(name = "cust_phone")private String custPhone;setter ... getter....
}LinkMan 类@Entity
@Table(name = "cst_linkman")
public class LinkMan {@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;setter ... getter....
}
实体类接口
public interface CustomerDao
extends JpaRepository<Customer,Long>,JpaSpecificationExecutor<Customer>{}public interface LinkManDao
extends JpaRepository<LinkMan,Long> ,
JpaSpecificationExecutor<LinkMan> {}
数据库
注意
如果数据库的编码出现问题,导致无法插入中文导致报错可以修改数据库编码ALTER DATABASE 数据库名 DEFAULT CHARACTER SET utf8;修改数据库的编码ALTER TABLE 表名 CONVERT TO CHARACTER SET utf8;修改表的编码
表关系
客户表
CREATE TABLE `cst_customer` (`cust_id` bigint(20) NOT NULL AUTO_INCREMENT,`cust_address` varchar(255) DEFAULT NULL,`cust_industry` varchar(255) DEFAULT NULL,`cust_level` varchar(255) DEFAULT NULL,`cust_name` varchar(255) DEFAULT NULL,`cust_phone` varchar(255) DEFAULT NULL,`cust_source` varchar(255) DEFAULT NULL,PRIMARY KEY (`cust_id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;联系人表
CREATE TABLE `cst_linkman` (`lkm_id` bigint(20) NOT NULL AUTO_INCREMENT,`lkm_email` varchar(255) DEFAULT NULL,`lkm_gender` varchar(255) DEFAULT NULL,`lkm_memo` varchar(255) DEFAULT NULL,`lkm_mobile` varchar(255) DEFAULT NULL,`lkm_name` varchar(255) DEFAULT NULL,`lkm_phone` varchar(255) DEFAULT NULL,`lkm_position` varchar(255) DEFAULT NULL,`lkm_cust_id` bigint(20) DEFAULT NULL,PRIMARY KEY (`lkm_id`),KEY `FKh9yp1nql5227xxcopuxqx2e7q` (`lkm_cust_id`),CONSTRAINT `FKh9yp1nql5227xxcopuxqx2e7q` FOREIGN KEY (`lkm_cust_id`) REFERENCES `cst_customer` (`cust_id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; 现在表的关系很明确了
然后cst_linkman表维护了一个外键,在实体类里氏没有该字段的,注意那么现在看来,cst_linkman是多的一方,cst_customer是一的一方
使用
如何进行多表操作
首先确定了双方的关系后我们在Customer一的一方添加一个属性/*** 配置一对多关系(客户对联系人)* 使用注解的形式配置多表关系* 1.声明关系* targetEntity:对方对象的字节码对象* 2.配置外键(中间表)* @JoinColumn:配置外键* name:外键字段名称 ,注意,该外键名称是linkman表里的字段* referencedColumnName:参照的主表的主键字段名称** * 在客户实体类上添加了外键配置,对客户而言,也具备了维护外键的作用*/@OneToMany(targetEntity = LinkMan.class)@JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id")private Set<LinkMan> linkMans=new HashSet<LinkMan>();而LinkMan类这里/*** 配置多对一* 联系人对客户* @ManyToOne:配置多对一关系* targetEntity:对方的实体类字节码* @配置外键(中间表)*/@ManyToOne(targetEntity = Customer.class)@JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id")private Customer customer;
测试(一对多)
在customer,一的一方维护外键关系@Autowiredprivate LinkManDao linkManDao;@Autowiredprivate CustomerDao customerDao;@Test@Transactional@Rollback(value = false)public void testadd(){LinkMan linkMan=new LinkMan();linkMan.setLkmName("威威");Customer customer=new Customer();customer.setCustName("alibaba");/*** 这是一对多,执行了两条插入语句之后,还会有一条修改语句,就是给外键赋值的** 此时控制台会显示执行了两条insert语句,就是数据的插入* 还有一条update语句,是customer类对外键的维护*/customer.getLinkMans().add(linkMan); //配置外键关系 ,因为在customer实体类中维护里外键当然也可以linkMan.setCustomer(customer);linkManDao.save(linkMan);customerDao.save(customer);}在多的一方维护外键
@Test@Transactional@Rollback(false)public void testadd1(){LinkMan linkMan=new LinkMan();linkMan.setLkmName("小蝶");Customer customer=new Customer();customer.setCustName("alibaba");/*** 在多对一的这边保存外键关系* 只有两条insert语句* 只执行了插入保存语句,没有修改语句,* 由于外键在该书体类对应的数据库表上,而且也维护了外键关系,* 所以在保存数据的时候就一起把外键给保存了*/linkMan.setCustomer(customer);customerDao.save(customer);linkManDao.save(linkMan);}两种方式维护外键都可以,具体的区别也有说明
那么当写了两种外键的维护关系又会怎么样@Test@Transactional@Rollback(false)public void testadd2(){LinkMan linkMan=new LinkMan();linkMan.setLkmName("威威");/*** 双向外键维护是,会执行两条insert,一条update语句,就是一的那一方* 可以采取的措施是,放弃外键维护,在实体类中* 我们在一的一方放弃外键维护,这样就能少执行一条sql**/Customer customer=new Customer();customer.setCustName("alibaba");linkMan.setCustomer(customer);customer.getLinkMans().add(linkMan); //维护外键,会多发送一条修改语句 ,原因是,就算是单独使用这种方式,也会发送修改语句修改外键// 我们可以选择一方方放弃外键维护 可以选择放弃外键维护customerDao.save(customer);linkManDao.save(linkMan);}当有维护关系的主表进行删除数据时,会先将从表的外键置为null,当然要保证该外键可以为null,
然后再删除,原因是,该主表数据被其他外键引用了,所有要有该操作@Test@Transactional@Rollback(value = false)public void testdelete(){Customer one = customerDao.findOne(1l);customerDao.delete(one);}而从表,也就是外键所在的表数据,则无需考虑其他的因素,因为该表主键没有被其他表的外键引用,
所有可以直接删除那么如果要取消一方的维护外键的关系的话,一般是取消的是一的一方的外键维护/*** 放弃主表对从表外键的维护* mappedBy:对方配置关系的属性名称 ,从表配置多对一所使用的属性名称*/如: 在 Customer实体类的注解修改成如下@OneToMany(mappedBy = "customer",cascade = CascadeType.ALL,fetch = FetchType.LAZY)private Set<LinkMan> linkMans=new HashSet<LinkMan>();
注意
一,(非级联,主表有维护外键的权利)
对于数据的删除
如果是从表也就是有外键的表,数据是可以随意删除的
但是如果是主表的话:一般如果有从表,且有外键维护关系的权利的话,会先将从表的外键值改为null,而且外键没有限制不能为空的话,主表数据就可以删除成功了,否则,报错。如果没有维护外键的权利的话,那么就不能删除,这跟外键没有关系了,因为已经不维护了如果没有外键维护关系的话,还需要删除主表关系的话,只能通过级联删除引用了
二,(级联)
级联设置:需要在操作主体上添加属性 cascade = CascadeType.ALL@OneToMany(mappedBy = "customer",cascade = CascadeType.ALL)private Set<LinkMan> linkMans=new HashSet<LinkMan>();级联添加保存客户的同事保存联系人级联删除删除 级联添加
注意:虽然主表没有对外键维护的权利,但是,他们的主外表的关系还在
@Test
@Transactional
@Rollback(value = false)
public void jilian(){Customer customer=new Customer();customer.setCustName("rer");LinkMan linkMan = new LinkMan();linkMan.setLkmName("vvv");customer.getLinkMans().add(linkMan);linkMan.setCustomer(customer);customerDao.save(customer);linkManDao.save(linkMan);
}级联删除
@Test@Transactional@Rollback(value = false)public void testdelete(){Customer one = customerDao.findOne(1l); 注意,先要把数据查询出来在删除,否则报错customerDao.delete(one);}
gitee案例参考,一对多
测试(多对多)
开始,新建两各实体类
Role类,User类@Table(name = "sys_role")
@Entity
public class Role {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)@Column(name = "role_id")private Long roleId;@Column(name="role_name")private String roleName;@Column(name="role_memo")private String roleMemo;setter..... getter.....}@Table(name = "sys_user")
@Entity
public class User {@Id@Column(name = "user_id")@GeneratedValue(strategy=GenerationType.IDENTITY)private Long userid;@Column(name="user_code")private String userCode;@Column(name="user_name")private String userName;@Column(name="user_password")private String userPassword;@Column(name="user_state")private String userState;setter .... getter ....
}
实体类接口
public interface RoleDao
extends JpaRepository<Role,Long>
, JpaSpecificationExecutor<Role> {}public interface UserDao
extends JpaRepository<User,Long>
, JpaSpecificationExecutor<User> {}
两实体类建立关系
user类添加set类型的属性,并配置主键与中间表的关系
@ManyToMany(targetEntity = Role.class,cascade = CascadeType.ALL)@JoinTable(name = "sys_user_role", //创建中间表,name是表名//joinColumn是当前对象在中间表中的外键,name是表中的名称,referenceColumnName是对应的当前对象的属性joinColumns = {@JoinColumn(name = "sys_user_id",referencedColumnName = "user_id")},//inverseJoinColumn是对方对象在中间表的外键,name是在表中的名称, role_id是对方对象的属性idinverseJoinColumns = {@JoinColumn(name = "sys_role_id",referencedColumnName = "role_id")})private Set<Role> roles=new HashSet<Role>();Role类添加一个set类型的属性@ManyToMany(targetEntity = User.class)@JoinTable(name = "sys_user_role", //joinColumn是配置当前对象在中间表的信息,注意一下,并不是完全和User表里面的属性字段配置一样joinColumns = {@JoinColumn(name = "sys_role_id",referencedColumnName = "role_id")},inverseJoinColumns = {@JoinColumn(name = "sys_user_id",referencedColumnName = "user_id")})private Set<User> users=new HashSet<User>();配置好俩表之间的关系后,进行测试
使用
用户表对中间表外键进行维护
@Test@Transactional@Rollback(false)public void ManyToManysave(){User user=new User();user.setUserName("小蝶");Role role=new Role();role.setRoleName("老师");//配置用户到角色的关系.用户对中间表进行维护user.getRoles().add(role);userDao.save(user);roleDao.save(role);}
这时,中间表的外键已经有数据了,因为用户表对中间表的外键进行了维护
既然用户表可以,那么角色表也可以对中间表的外键进行维护角色表对中间表外键进行维护@Test@Transactional@Rollback(false)public void ManyToManysave(){User user=new User();user.setUserName("小蝶");Role role=new Role();role.setRoleName("老师");//配置角色到用户的关系,可以对中国表进行维护role.getUsers().add(user);userDao.save(user);roleDao.save(role);}删除数据
当有维护权利时,不管是哪一方都可以进行删除操作,因为是多对多,双方都有维护
@Test@Transactional@Rollback(false)public void delete(){User one = userDao.findOne(5l);userDao.delete(one);这个删除会删除中间表与之对应的数据和User表要删除的数据,不会删除role表的数据}对外建有维护权时,删除本表的数据,但是会先删除中间表的外键关系@Test@Transactional@Rollback(false)public void delete(){Role one = roleDao.findOne(17l);roleDao.delete(one);这个删除会删除中间表与之对应的数据和role表要删除的数据,不会删除User表的数据}删除是先删除中间表的数据,再删除要操作的表的数据,如果想删除相关联的另一个主表的数据,需要使用到级联
注意:双向维护(不可行)
上面说了,用户表和角色表都可以单独的对中间表进行外键维护,
那么他们同时使用会怎么样呢?@Test@Transactional@Rollback(false)public void ManyToManysave(){User user=new User();user.setUserName("小蝶");Role role=new Role();role.setRoleName("老师");//配置用户到角色的关系.可以对中间表进行维护user.getRoles().add(role);//配置角色到用户的关系,可以对中国表进行维护role.getUsers().add(user);userDao.save(user);roleDao.save(role);}此时就会出现异常: 主键冲突,原因是用户表维护了中间表外键,而角色表也去维护,就会出现异常com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry 'n-n' for key 'PRIMARY'
放弃外键维护
修改,由于是多对多,其实那边放弃维护都可以,但一般是选择在使用是被动的一方放弃外键维护权
如 我们将role实体类的users属性的注解属性做一些修改//放弃对中间表主键的维护权,加上级联的配置cascade=CascadeType.ALL@ManyToMany(mappedBy = "roles",cascade=CascadeType.ALL) //在对方属性中的对象名称private Set<User> users=new HashSet<User>();user实体类的roles属性修改,添加了cascade = CascadeType.ALL@ManyToMany(targetEntity = Role.class,cascade = CascadeType.ALL)@JoinTable(name = "sys_user_role", //创建中间表,name是表名//joinColumn是当前对象在中间表中的外键,name是表中的名称,referenceColumnName是对应的当前对象的属性joinColumns = {@JoinColumn(name = "sys_user_id",referencedColumnName = "user_id")},//inverseJoinColumn是对方对象在中间表的外键,name是在表中的名称, role_id是对方对象的属性idinverseJoinColumns = {@JoinColumn(name = "sys_role_id",referencedColumnName = "role_id")})private Set<Role> roles=new HashSet<Role>();
多对多级联操作
:级联添加
@Test@Transactional@Rollback(false)public void testadd(){User user=new User();Role role=new Role();user.setUserName("小蝶");role.setRoleName("学生");role.getUsers().add(user); 1roleDao.save(role); 2}
由于我们做了修改,role已经不维护外键了,所以通过上面的 1和2两个步骤,
只会生成两个表,一个给两个表插入数据,role,user两个表如果将1和2两个改为user.getRoles().add(role);userDao.save(user);则会给三张表都插入数据,原因是维护了外键的关系如果没有中间表的话,那么删除操作只会删除自己本表的数据
:级联删除
在上面配置了role放弃维护主键的情况下,添加要由维护了主键的user类进行添加数据,才会给中间表插入数据
那么级联删除有没有限制呢,答案是没有,Role one = roleDao.findOne(31l);roleDao.delete(one);User user=userDao.findOne(324l);userDao.delete(user);
注意: 这两种方式都可以进行级联删除,因为都配置级联的删除配置,但他们不能一起使用,
gitee案例,多对多
对象导航查询
对象导航查询就是通过配置实体类的包含关系,可以通过get去获取其他表的数据,
其中 findone(...)是立即加载getont(...) 延迟加载延迟加载是当只有使用到了该数据的时候才会去查询,立即查询是将关联的关系数据全部查询出来配置延迟加载举例 ,@OneToOne ,@OneToMany @ManyToMany 都是可以配置的@ManyToOne(fetch = FetchType.LAZY)立即加载 fetch = FetchType.EAGER
no Session
也就是说当使用到延迟加载去加载数据时,但事物已经关闭了,
因此延迟加载时找不到存在的会话来运行接下来的自动查询
could not initialize proxy - no Session at
如果是简单的单元测试,则需要在方法上加上
@Transactional注解就可以了如果是立即加载则无需加上该注解一对多,一一的一方为主体,区查询数据时,默认是延迟,
当以多的一方为主体查询是,默认是立即加载
多对多是默认延迟加载那么在非单元测试环境下使用到了延迟加载下该怎么处理
....为完善
@Where
在使用Hibernate或者JPA时,我们经常会使用@Where注解实现查询过滤,在实体类上、实体属性上、查询语句上都有应用。如就我们上面定义的实体类上加上注解
@Where(clause = "lkm_id != 9")
那么就会过滤数据库中的lkm_id 为9的记录,注意,是数据库的字段名
逻辑删除
@Where(clause = "lg = 0")
@SQLDelete(sql="update cst_linkman set lg = 1 where lkm_id = ?")配置在实体类上如 @Entity@Table(name = "cst_linkman")@Where(clause = "lg = 0") @SQLDelete(sql="update cst_linkman set lg = 1 where lkm_id = ?")//lg public class LinkMan {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)@Column(name = "lkm_id") //主键private Long lkmId;......... //逻辑删除字段@Type(type = "org.hibernate.type.NumericBooleanType")private Boolean lg=false; 在mysql数据库生成int类型的字段,false对应的,默认值为0
} 数据库表的字段也需要添加但这里我们就不进行手动修改,我们让他自动生成新的表当我们进行删除时,他会将逻辑删除字段改为1,然后我们还配置了一个@Where 只会显示逻辑字段为0的,这样就达到了逻辑删除字段注意,如果是级联的状态下,要注意一下,
gitee案例,逻辑删除
快速入门spring data jpa 2,多表操作,逻辑删除相关推荐
- 五分钟快速入门 Spring Data JPA
Spring Data JPA(类似于Java Web 中的 DAO) 操作声明持久层的接口(Repository) 三个核心接口: CrudRepository PagingAndSortingRe ...
- Spring Data JPA 实现多表关联查询
原文链接:https://blog.csdn.net/johnf_nash/article/details/80587204 多表查询在spring data jpa中有两种实现方式,第一种是利用hi ...
- Spring Data JPA实现多表的关联查询
1.Spring Data JPA关系映射 对象关系映射(Object relational mapping)是指通过将对象状态映射到数据库列,来开发和维护对象和关系数据库之间的关系.它能够轻松处理( ...
- 使用H2Database+Druid连接池+Spring Data JPA+Ehcache实现CRUD操作
前言 注:本篇为纯实践篇,主要用于技术整合,介绍如何搭建一个完整全面的Web项目.如果对于技术原理还不了解的童鞋可点击下方链接,学习后在来~ H2数据库教程:H2数据库入门 缓存使用教程:在Sprin ...
- Spring Data JPA Specification多表关联查询
需求:有一个流量计的设备,流量计有一个所属罐区id,想要通过所属罐区查到所关联的这个罐区的编码以及名称. 1.流量计实体类: 主要是给流量计实体添加了以下属性,通过tank_area_id关联: pa ...
- Spring Data JPA 多表关联查询的实现
Spring Data JPA 多表关联查询的实现 多表查询在spring data jpa中有两种实现方式,第一种是利用hibernate的级联查询来实现,第二种是创建一个结果集的接口来接收连表查询 ...
- Spring Security+Spring Data Jpa 强强联手,安全管理只有更简单!
Spring Security+Spring Data Jpa 强强联手,安全管理没有简单,只有更简单! 这周忙着更新 OAuth2,Spring Security 也抽空来一篇. Spring Se ...
- Spring Data JPA(官方文档翻译)
关于本书 介绍 关于这本指南 第一章 前言 第二章 新增及注意点 第三章 项目依赖 第四章 使用Spring Data Repositories 4.1 核心概念 4.2 查询方法 4.3 定义rep ...
- Spring Data Jpa出现Not supported for DML operations
问题描述:在使用Spring Data Jpa的注解形式去配置删除sql语句,出现了下述异常: org.springframework.dao.InvalidDataAccessApiUsageExc ...
- Spring Data JPA 五分钟快速入门和实践
Spring Data JPA(类似于Java Web 中的 DAO) 操作声明持久层的接口(Repository) 三个核心接口: CrudRepository PagingAndSortingRe ...
最新文章
- 三层交换机与路由器的比较
- iptables透明网桥无法使用透明代理错误
- 统一代码风格工具 editorConfig
- springboot学习笔记(四)
- C++调用python(C++)
- cap理论具体含义_架构设计之「 CAP 定理 」
- c语言正确理解以下名词及其含义,C程序作业答案.doc
- 【面向对象】面向对象程序设计测试题8-对象之间one-to-many关系测试题
- Linux环境下的JFreeChart中文乱码问题解决办法
- 2021年下半年网络工程师上午真题及答案解析
- ArrayList<object> list 转org.json.JSONArray
- GeoTools——JTS空间操作
- SPSS数据分析流程
- windows系统下进入jupyter本地服务器(localhost)的步骤
- 种草软文怎么写?分享一些超实用的种草软文写作技巧。
- 深入浅出 - Android系统移植与平台开发(六)- 为Android启动加速【转】
- 怎样把旧电脑数据迁移到新电脑
- [机器学习基础][台大林轩田]机器学习概念
- 服务器ftp日志文件在哪里,ftp服务器的日志在哪
- Ceph 学习——OSD读写流程与源码分析(一)
热门文章
- 哈德森喜迎万分夜 CBA“第一外援”就此易主?
- MySQL调优(二)——索引最佳实践
- C语言标准库中round函数
- Rational License Key Error的永久解决办法
- jzoj 3457. 【NOIP2013模拟联考3】沙耶的玩偶(doll) (Standard IO)
- 单细胞测序step1——搭建云服务器下网页版Rstudio
- 自适应simpson积分
- sin的傅里叶变换公式_正弦信号傅里叶变换
- Python面向对象案例之语音计算器
- C++控制台五子棋(带背景音乐)