SpringDataJPA

(一)简介

Spring Data JPA 是 Spring 基于 ORM 框架、JPA 规范的基础上封装的一套 JPA 应用框架,可使开发者用极简的代码就可以实现对数据库的访问和操作。它提供了包括增删改查等在内的常用功能,且易于扩展!学习并使用Spring Data JPA 可以极大提高开发效率!
它是把JPA又封装了一遍,我们的 dao层中只需要写接口,就自动具有了增删改查、分页查询等方法,持久层不用写了.
SpringDataJPA[老的有时间整理一下] - 图1

(二)基本使用

1.搭建开发环境

依赖配置文件


编写配置文件
SpringDataJPA[老的有时间整理一下] - 图2

简单单表增删改查操作

编写dao层
/
JpaRepository:此接口提供了CRUD基本的操作
接口只需要extends JpaRepository 即可, 参数1是你要操作当前实体类, 参数2是当前要操作实体类主键的类型
/
public interface CustomerDao extends JpaRepository{


编写service层代码

直接操作service层就行,dao不用管理

/
基本增删改查
/
@Service
public class CustomerServiceImpl implements CustomerService {

@Autowired
private CustomerDao customerDao;

/

保存
@param customer
/
public void save(Customer customer) {
//保存和修改都是save方法,如果对象里面有id能跟表里面的id对上就是保存
//如果没有id就是保存
customerDao.save(customer);
}

/**
根据id查询
@param l
@return
/
public Customer findById(long id) {
//返回值是Optional 是包装类,你直接用.get方法就可以取出来了,是通过dao层你 extends的JpaRepository里面的泛型决定的返回值类型
Optional opt = customerDao.findById(id);
//直接GET出来可以,没有参数
return opt.get();
}

/**
删除
@param c
/
public void delete(Customer c) {
customerDao.delete(c);
}

/
根据id删除
@param l
*/
public void delete(long id) {
customerDao.deleteById(id);
}

编写controller层代码

@RunWith(SpringJUnit4ClassRunner.
class)
@ContextConfiguration(locations=”classpath:applicationContext.xml”)
public class Test_SpringDataJPA {

@Autowired
private CustomerService cs;

@Test
public void testSave(){
Customer c =
new Customer();
c.setCustName(“佳佳洗脚城”);

cs.save(c);
}

@Test
public void testFindById(){
Customer c = cs.findById(1L);
System.
out.println(c.getCustName());
}

@Test
public void testUpdate(){
Customer c = cs.findById(1L);
c.setCustName(“佳佳洗浴中心”);
cs.save(c);
}

/

删除,通过传入对象删除.
Test_SpringDataJPA.class
/
@Test
public void testDelete() {
/

源码里面会执行一次查询,如果你再查询的话,就会出现控制台打印两次查询语句
原因是为了保证要删除的数据库里面的数据一致,它会自己再查询一遍
为什么先查一遍,因为有可能别人先给你数据删除了,没了你再删除就重复了
这样就会报错了,它为了保证不报错,就先查询一下,再对比一级缓存里面有没有数据
这样保证数据库里面有,一级缓存也有,这样就不会报错了.
Customer c = cs.findById(1L);
/
Customer c = new Customer();
/

即使你中途设置了值,只要是主键对上,
也能删除.
*/
c.setCustId(1L);
c.setCustName(“yyyy”);
cs.delete(c);
// cs.delete(1L);
}
}

JPQL复杂的查询

可以写其它条件,需要程序员自己编写条件查询

dao层

public interface CustomerDao extends JpaRepository {
/
使用JPQL查询
问号后面的值代表形参的参数的位置
就是一个形参也得把 后面的1写上
/
@Query(“from Customer where custName like ?1 and custId=?2”)
public List findAllCustomer(String name, Long id);

/

/SQL 语句查询(基本不用,因为如果你要是使用SQL还不如用Mybatis,性能更高)

nativeQuery = true (默认是false)
必须要加这个东西,否则框架无法识别,认为它是jpql语句
/
@Query(value = “select
from cstcustomer where cust_name like ?1 and cust_id = ?2”, nativeQuery = true)
public List findAllCustomerBySQL(String name, Long id);

/
方法命名规则查询,
按照 Spring Data JPA 定义的规则,查询方法以 findBy 开头,涉及条件查询时,条件的属性用条件关键
字连接,要注意的是:条件属性首字母需大写。框架在进行方法名解析时,会先把方法名多余的前缀截取掉,然后
对剩下部分进行解析。
/
public List findByCustNameLikeAndCustId(String name, Long id);

service层

/
JPQL查询
@param name
*
@param id
*
@return
*/
public List findAllCustomer(String name, Long id) {
return customerDao.findAllCustomer(name, id);
}

/

sql查询
@param name
@param id
@return
/
public List findAllCustomerBySQL(String name, Long id) {
return customerDao.findAllCustomerBySQL(name, id);
}

/**
方法命名规则查询
@param name
@param id
@return
/
public List findByCustNameLikeAndCustId(String name, Long id) {
return customerDao.findByCustNameLikeAndCustId(name, id);
}

controller层

@Test
//JPQL查询
public void test1() {
List list = cs.findAllCustomer(“%集团%”, 1L);
for (Customer c : list) {
System.**_out
.println(c.getCustName());
}
}

@Test
//sql查询
public void test2() {
List list = cs.findAllCustomerBySQL(“%集团%”, 1L);
for (Customer c : list) {
System.
out.println(c.getCustName());
}
}

@Test
//方法命名查询
public void test3() {
List list = cs.findByCustNameLikeAndCustId(“%集团%”, 1L);
for (Customer c : list) {
System.
out**.println(c.getCustName());
}
}

Specifications 动态查询(底层是JPA的QBC查询)

QBC查询就是动态加条件查询,通过使用Hibernate提供的Query By Criteria API来查询对象,这种API封装了SQL语句的动态拼装,动态就是条件查询时候不确定有哪些条件查询,dao层不需要编写代码.

controller层
@Test
// Specifications 动态查询(JPA的QBC查询)
public void test4() {
/
查询什么泛型就写什么,然后用匿名内部实现


/
Specification spec = new Specification() {
/

cb:该对象中定义着查询条件的方法 (封装查询条件的)
cq:用于生成sql语句()
root:获取实体类对象的封装对象,有此对象之后,所有实体类都可以看成此类型(你Root 的话root就是customer对象)
/

public Predicate toPredicate(Root root, CriteriaQuery<?> cq, CriteriaBuilder cb) {
/
root获取对象里面的值
as后面跟类型
/
Predicate p1 = cb.like(root.get(“custName”).as(String.class), “%集团%”); // custName like ?
Predicate p2 = cb.equal(root.get(“custId”), 1L); // custId = ?
//组合上面的两个条件对象
Predicate p3 = cb.and(p1, p2);
//返回组合好的条件对象
return p3;
}
};
List list = cs.findByQBC(spec);
for (Customer c : list) {
System.out.println(c.getCustName());
}
}
service层
/
QBC查询
@param spec
*
@return
*/
public List findByQBC(Specification spec) {
return customerDao.findAll(spec);
}
dao层
以后dao建议extends两个都写上,
JpaSpecificationExecutor:提供了复杂查询和分页查询,你需要extends这个才能使用qbc查询等等
/
public interface CustomerDao extends** JpaRepository, JpaSpecificationExecutor {

带条件的分页查询


@Test
/分页查询带条件的Specifications 动态查询(JPA的QBC查询)

/
public void test5() {
Specification spec = new Specification() {
//cb:该对象中定义着查询条件的方法
//cq:用于生成sql语句
//root:获取实体类对象的封装对象,有此对象之后,所有实体类都可以看成此类型
public Predicate toPredicate(Root root, CriteriaQuery<?> cq, CriteriaBuilder cb) {

Predicate p1 = cb.like(root.get(“custName”).as(String.class), “%集团%”); // custName like ?
Predicate p2 = cb.equal(root.get(“custId”), 1L); // custId = ?
Predicate p3 = cb.and(p1, p2);
return p3;
}
};

/
分页查询操作
导包用import org.springframework.data.domain.Pageable的
Pageable pageable = PageRequest.of(1, 2);
参数1:页码 从零开始
参数2:每页显示条数
参数3是 升序降序的
/
Pageable pageable = PageRequest.of(0, 2, Sort.by(Order.desc(“custId”))); //参数1:页码 参数2:每页显示条数
/

返回值是一个page对象 .泛型是customer了
参数1 是条件对象,如果没有分页条件对象就写null
参数2 是 每页显示的条数
/
Page page = cs.findAllPage(null, pageable);
//取值问题
List list = page.getContent();
for (Customer c : list) {
System.out.println(c);
}
}

多表查询

controller层
@Test
/多表查询
Specifications 动态查询(多表查询)(JPA的QBC查询)
/
public void test6() {
Specification spec = new Specification() {
/

cb:该对象中定义着查询条件的方法 (封装查询条件的)
cq:用于生成sql语句()
root:获取实体类对象的封装对象,有此对象之后,所有实体类都可以看成此类型(你Root 的话root就是customer对象)
/
public Predicate toPredicate(Root root, CriteriaQuery<?> cq, CriteriaBuilder cb) {
/
关联查询 root.join
参数一:主表 里面引用从表的实体类的属性名
主表里面的: private Set linkMans = new HashSet(0)
参数二:JoinType.INNER 是连接状态,这个是内连接(如果不写就是默认内连接) 如果是left 就是左连接 如果是right就是右连接

返回值类型写两个关联的实体类
/
Join join = root.join(“linkMans”, JoinType.INNER);
//条件 通过主键 1 的查询
return cb.equal(root.get(“custId”), 1L);
}
};

List list = cs.findByManyCondition(spec);

Customer c = list.get(0);
System.out.println(c);

Set linkMans = c.getLinkMans();
for (LinkMan lkm : linkMans) {
System.out.println(lkm);
}
}
service层
/
多表查询
@param spec
*
@return
*/
public List findByManyCondition(Specification spec) {
return** customerDao.findAll(spec);
}


HibernateJPA

(一)简介

1.ORM的简介


ORM(Object-Relational Mapping) 表示对象关系映射。

Object Relational Mapping
对象 关系 映射
为什么使用 ORM?
通过建立实体类和数据库表的对应关系,从而让开发者不再关心SQL语句怎么写。
直接实现最终效果。减化开发代码。

简单的说:ORM 就是建立实体类和数据库表之间的关系,从而达到操作实体类就相当于操作数据库表的目的。


常见的 orm 框架: Mybatis(ibatis)、Hibernate、Jpa

2.jpa概述

JPA 的全称是Java Persistence API, 即 Java 持久化 API,是SUN 公司推出的一套基于 ORM 的规范,内部是由一系列的接口和抽象类构成。
JPA 通过JDK 5.0注解描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。

3.JPA 与 hibernate 的关系

JPA规范本质上就是一种ORM规范,提供了一些编程的 API 接口,但具体实现则由服务厂商来提供实现。
JPA 和 Hibernate 的关系就是规范与实现的关系。
SpringDataJPA[老的有时间整理一下] - 图3

(二)入门配置使用

1.基本入门

坐标依赖

org.hibernate
hibernate-c3p0
5.0.7.Final


mysql
mysql-connector-java
5.1.6


org.hibernate
hibernate-entitymanager
5.0.7.Final

编写实体类并配置和表的映射关系
package com.itheima.pojo;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

//CREATE TABLE cst_customer (
// cust_id BIGINT(32) NOT NULL AUTOINCREMENT 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;

@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;

getset方法………..

编写配置文件
在 maven 工程的 resources 路径下创建一个名为
META‐INF 的文件夹,在此文件夹下创建一个名为persistence.xml的配置文件。注意:META‐INF文件夹名称不能修改。persistence.xml文件名称不能改

SpringDataJPA[老的有时间整理一下] - 图4

2.工具类抽取

SpringDataJPA[老的有时间整理一下] - 图5
代码:
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
/
抽取工具类,
重复代码抽取出来
*/
public class JpaUtils {
//声明工厂
private static EntityManagerFactory factory;

static {
// 注意:该方法参数必须和persistence.xml中persistence‐unit标签name属性取值一致
//只找META-INF目录下的persistence.xml配置完美
factory = Persistence.createEntityManagerFactory(“jpaUnit”);
}

/

获取连接池对象
JpaUtils.class
*@return
/
public static EntityManager getEntityManager() {
*return
factory.createEntityManager();
}

3.基本增删改查排序分页使用

基本的增删改查操作
SpringDataJPA[老的有时间整理一下] - 图6

条件查询分页查询排序查询操作,基本都是固定的操作,拿过来粘贴上就行了.
SpringDataJPA[老的有时间整理一下] - 图7

(三)主键使用策略

1.基本介绍

需要思考哪个列是否适合主键:

自然主键:把具有业务含义的字段作为主键,称之为自然主键。
也可以理解把一个事物具备的属性作为主键
比如人的身份证号(是有含义)



代理主键:把不具备业务含义的字段作为主键,称之为代理主键。 尽量多使用代理主键.
身份证号码作为主键,以前身份证是15位,后来升级18位的,但是主键需要修改,主键一修 改一系列问题来了,引用主键的其他表也需要修改,最后大量修改很麻烦,甚至项目废了
适合主键的列应该永远都不要改才好

代理主键:比如,id自增,有具体的业务含义,只是序号,只是作为每一列的唯一标识而已.

2.JPA的主键生成策略

整数型策略

TABLE,SEQUENCE,IDENTITY,AUTO 四个类型都适用于整数数据类型,想要字符串作为主键都不好使,如果是随机字符串就不行.,下面就得使用hibernate自己的策略
对于这四个数据库只需要记住两个就可以了.java程序开发企业基本只用 MySQL,要不就是oracle数据库,其它很少用

IDENTITY:它适用于数据库中表中的列支持自增长的情况。例如:mysql,db2(ibm公司的),sqlserver 所以这三种数据库直接用这个策略就可以了,底层采用各自数据库的自增

SEQUENCE:它适用于数据库支持序列的情况,通常情况下支持序列的数据不支持表中列的自增长,但是也有例外。例如: oracle支持序列,但是不支持字段自增长。db2也支持序列,也支持字段自增长。 使用oracle数据库请用这个

TABLE:它用了一张表来维护,维护主键的值,值是下一个ID的值。
这个策略是给框架用的,不是给数据库用的

AUTO:就是TABLE
意思就是你是什么数据库,我就产生什么策略,封装了上面三个策略,如果你是MySQL就自动用IDENTITY策略,如果是oracle就自动用SEQUENCE策略.如果是其他数据库的话就是TABLE
但是现在不管用,它只支持table,所以AUTO就是TABLE.

JPA使用hibernate的主键生成策略

总结:
如果是数值类型的就用native策略,如果是字符串就用uuid策略

其实不仅仅只有下面的六个
increment :增长,适用类型:short,int,long类型主键.在多进程和集群下不要使用.
用的不是数据库的自动增长,hibernate底层的增长策略,select max(custId) from customer; 然后+1作为下一条记录的ID.(不用,在多进程和集群下面不行,因为多线程访问必然会出现问题,假如写两个测试方法,一个新增,但是没有提交这时候上一个主键是2,另一个也新增,上一个主键也是2最后两个都提交主键会重复,都是3)

identity:自动增长,用的是数据库的自动增长。适用类型:short,int,long类型主键.支持自动增长数据库如Mysql sqlserver(不用说,跟整数型策略,上面介绍的是一样的,采用数据口自己的自增策略)

sequence :序列,适用类型:short,int,long类型主键.支持序列的数据库如:Oracle.(不用解释, 和上面的一样,是oracle的策略)

native :本地策略,根据数据库的底层采用使用identity还是sequence.(用数值的都用这个多,是根 据数据库底层而自动采用identity还是sequence,把上面的两个封装了,问题来了,框架怎么 知道你用的是什么数据库,因为你配置文件里面配置方言了,框架会自动根据方言来判断你 用的是什么数据库)

下面两个是整数型的主键使用的

uuid :随机的字符串,适用于字符串类型的主键.(是hibernate的策略,底层就是uuid策略)

assigned :需要用户手动输入OID的.(*需要程序员自己手动输入,一般不用,因为你让用户输入 主键他知道是什么东西)

主键策略的使用(在持久化类)

/ 步骤,先让hibernate的注解指定策略并声明一个值,再用jpa注解,把hibernate的生成好的值的赋值给主键

@GenericGenerator注解是 hibernate 提供的。导包是hibernate的包,不是jpa的
属性介绍:
name属性:
用于给使用的生成规则起个名称,名称随便写以供 JPA引用 ,
strategy属性:
用于指定 hibernate 中提供的主键生成策略
@GenericGenerator(name = “hibernate_uuid”, strategy = “uuid”)的含义是
代表我声明了一个变量为hibernate_uuid,变量的值是由strategy = “uuid”生成的然后赋给hibernate_uuid
上面的有值的实体,还需要用JPA注解赋值给主键,用下面的注解实现
generator 属性: 用于引用@GenericGenerator 注解name 属性的值
@GeneratedValue(generator = “hibernate_uuid”)
/

@Id
@GeneratedValue(generator = “hibernate_uuid”)
private** String sid;

(四)缓存机制和快照机制

1.一级缓存

缓存就是有一个集合,或者另一个对象持有了这个对象的引用,才叫缓存
什么是一级缓存:
被另一个对象所引用了,那么这个对象就保存到一级缓存了.一级缓存是EntityManager级别的缓存,可以提高查询效率。

/
演示JPA的一级缓存(一级缓存即是EntityManager对象)
TestHIbernateJpa.class
怎么区分三种状态,看临界点, 看stu对象与em之间的关系
主要是打断点看到:
持久态和托管态区别: 有没有id

我们关注的就是持久态,至少持久态才能涉及到一级缓存

一级缓存什么时候结束?close时候就关闭了一级缓存

一级缓存作用:
提高查询效率
自动更新修改数据能力(快照机制)

/
@Test
public void test2() {
//得到EntityManager
EntityManager em = JpaUtils.getEntityManager();
//获取事务
EntityTransaction tx = em.getTransaction();
tx.begin();

//瞬时态(刚new出来,和em没有发生关系,就叫瞬时态 ,还没有分配id)
Student stu =
new Student();
stu.setUserName(“jack”);
/持久态(被em管理了,或者和em发生关系了,就叫持久态)
不管你调用em的什么方法,只要你操作了stu那么stu就在一级缓存了
持久化特点 有id 并且通常和表里面的主键对应的
/
em.persist(stu); //一级缓存中
//还没提交,只是在内存里面有数据,一旦提交数据库里面就有数据了,
tx.commit();
//em还没有关闭 那么 stu都是持久态
em.close();
//一旦 em close了, 不管stu了,那么stu就是托管态了
//托管态(游离态,就是不要了)
System.
out**.println(stu);

}

2.快照机制

自动更新数据能力
/
演示JPA的一级缓存的快照机制(可以自动更新数据)
一级缓存里面有一系列集合,一级缓存集合里面有 数据区域和快照区域 两个区域
提交数据,检查一级缓存中的(数据区和快照区)的数据是否一致,如果不一致,则自动执行update语句。

所以即使不需要em.merge(stu); 也可以保存数据
面试:一级缓存怎么就能自动更新数据? 答 : 因为有快照机制
TestHIbernateJpa.class
/
@Test
public void test4() {
EntityManager em = JpaUtils.getEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();

Student stu = em.find(Student.
class, “8a7e874e65c79d9d0165c79d9f3a0000”);
stu.setUserName(“tom”);
//下面这行代码注释了照样可以修改(原因就是快照机制,可以自动更新数据)
//在一级缓存集合中,数据区域值已经发生改变了
//em.merge(stu);

//!!!提交数据,检查一级缓存中的(数据区和快照区)的数据是否一致,如果不一致,则自动执行update语句。
tx.commit();
em.close();
//这时候查出来 UserName 是 tom
System.
out**.println(stu);
}

(五)多表关系操作

1.基本概念和步骤分析

系统设计的三种实体关系分别为:多对多、一对多和一对一关系。
注意:一对多关系可以看为两种: 即一对多,多对一。所以说四种更精确。
明确:**
在JPA框架中表关系的分析步骤
第一步:首先确定两张表之间的关系。(主从关系,如果关系确定错了,后面做的所有操作就都不可能正确。)
第二步:在数据库中实现两张表的关系 (在数据库里面把两个表创建出来)
第三步:在实体类中描述出两个实体的关系
第四步:配置出实体类和数据库表的关系映射(描述完了框架不知道,需要配置注解才行)

2.JPA的一对多多对一配置

我们采用的示例为客户和联系人。
客户:指的是一家公司。
联系人:指的是公司中的员工。
在不考虑兼职的情况下,公司和员工的关系即为一对多。
SpringDataJPA[老的有时间整理一下] - 图8

在一对多关系中,我们习惯把一的一方称之为主表,把多的一方称之为从表。在数据库中建立一对多的关系,需要使用数据库的外键约束。

什么是外键?
指的是从表中有一列,取值参照主表的主键,这一列就是外键。

一对多多对一实体类的配置

/
**
配置一对多
用list集合和set集合,通常习惯用set集合,因为不重复,泛型里面是从表的实体类
到底要不要new? 如果不new的话,你getlinkMans.add 的时候会报出空指针异常
因为没有被实例化(没有new出来)
但是new出来会了浪费内存,所以就new出来是时候指定一下长度,把长度指定为0
下面写法,既避免了空指针,又节省了内存

private Set linkMans = new HashSet(0);

需要用注解告诉框架实体类之间的关系
!!!在主表里面配置类与类的关系,在从表里面配置表与表的关系
配置一对多@OneToMany
属性:targetEntity 是目标实体, 里面写多的一方的实体类的字节码文件
mappedBy:指定子表实体类中引用主表对象的名称.
/
@OneToMany(targetEntity = LinkMan.
class, mappedBy = “customer”)
private Set linkMans = new HashSet(0);

/多对一的配置
不能写外键字段,需要写一的一方的实体类 private Customer customer;
但是又有问题了,是对象名字和数据库的字段对不上,就得需要配置来搞定

!!!在主表里面配置类与类的关系,在从表里面配置表与表的关系
1.配置多对一注解:@ManyToOne
2.@JoinColumn 连接列
属性
name:指定外键字段的名称(表里面的字段,不是实体类的属性)
这个name值的含义等价于@Column(name = “xxxxx”)
referencedColumnName:指定引用主表的主键字段名称
写主表里面的主键(不是实体类里面的属性,是表字段)
*/
@ManyToOne
@JoinColumn(name = “lkm_cust_id”, referencedColumnName = “cust_id”)
private** Customer customer;

一对多多对一保存


/
保存操作
需求:
保存一个客户和一个联系人
要求:
创建一个客户对象和一个联系人对象
建立客户和联系人之间关联关系(双向一对多的关联关系)
先保存客户,再保存联系人

*/
@Test
public void test1() {
EntityManager em = JpaUtils.getEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();

//创建一个客户和两个联系人
Customer c =
new Customer();
c.setCustName(“冠希洗脚城”);

LinkMan lkm1 =
new LinkMan();
lkm1.setLkmName(“菜10”);

LinkMan lkm2 =
new** LinkMan();
lkm2.setLkmName(“张宪”);

//告知客户有哪些联系人!!!如果不写外键没有值
c.getLinkMans().add(lkm1);
c.getLinkMans().add(lkm2);

//告知联系人属于哪个客户!!!!!!如果不写外键没有值
lkm1.setCustomer(c);
lkm2.setCustomer(c);

//保存客户和联系人
em.persist(c);
em.persist(lkm1);
em.persist(lkm2);

tx.commit();
em.close();
}

一对多多对一的删除(慎用)

/
删除操作
删除从表数据:可以随时任意删除。
删除主表数据:
有从表数据引用
1、不能删除 (会报错)
2、如果还想删除,使用级联删除(操作一个对象,同时操作其关联的对象,慎用,尤其是一对多情况下,避免从删库跑路)

没有从表数据引用:随便删

在实际开发中,级联删除请慎用!(在一对多的情况下)
在企业里面 逻辑删除用的多, 就是status 逻辑删除状态字段
/
@Test
public void test2() {
EntityManager em = JpaUtils.getEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
//先查询再删除
Customer c = em.find(Customer.
class, 1L);
//如果客户有联系人引用的情况下,删除必然会报错
//非得删除就是先把从表数据里面删除,再删除主表数据(级联删除)
em.remove(c);

tx.commit();
em.close();
}

需要在一的一方@OneToMany注解加个配置cascade
级联操作:
指操作一个对象同时操作它的关联对象

使用方法:只需要在操作主体的注解上配置cascade
/

cascade:配置级联操作
CascadeType.MERGE 级联更新
CascadeType.PERSIST 级联保存:
CascadeType.REFRESH 级联刷新:
CascadeType.REMOVE 级联删除:
CascadeType.ALL 包含所有
*/
@OneToMany(mappedBy=”customer”,cascade=CascadeType.ALL,targetEntity=LinkMan.class)
@JoinColumn(name=”lkm_cust_id”,referencedColumnName=”cust_id”)
private Set linkmans = new HashSet(0)

3.多对多的配置使用

需要使用到中间表

多对多的实体类配置问题

SpringDataJPA[老的有时间整理一下] - 图9
/
@JoinTable:针对中间表的配置
属性:
name:配置中间表的名称

joinColumns:当前这个类所对应的表的外键
中间表的外键字段对应多个表的外键字段
name:当前的文件列名
referencedColumnName:主键列名

inverseJoinColumn:对方表在中间表对应的关系
中间表的外键字段关联对方表的主键字段
/
@ManyToMany(cascade = CascadeType.ALL)

@JoinTable(name = “sys_user_role”, joinColumns = { @JoinColumn(name = “sys_role_id”, referencedColumnName = “role_id”) }, inverseJoinColumns = {
@JoinColumn(name = “sys_user_id”, referencedColumnName = “user_id”) })
private Set users = new HashSet(0);


/

配置多对多
@ManyToMany注解
属性: targetEntity 对应多对多对方的实体类.class
mappedBy 对应对方实体类引用这个实体类的属性的名字

/
@ManyToMany(targetEntity = SysRole.class, mappedBy = “users”, cascade = CascadeType.ALL)
private Set roles = new HashSet(0);

多对多保存用户和角色操作


/
需求:
保存用户和角色
要求:
创建 2个用户和 3个角色
让 1号用户具有 1号和 2号角色(双向的)
让 2号用户具有 2号和 3号角色(双向的)
保存用户和角色
问题:
在保存时,会出现主键重复的错误,因为都是要往中间表中保存数据造成的。
解决办法:
让任意一方放弃维护关联关系的权利
/
@Test
public void test1() {
EntityManager em = JpaUtils.getEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();

// 创建 2个用户
SysUser u1 =
new SysUser();
u1.setUserName(“用户1”);

SysUser u2 =
new SysUser();
u2.setUserName(“用户2”);
//和 3个角色
SysRole r1 =
new SysRole();
r1.setRoleName(“角色1”);

SysRole r2 =
new SysRole();
r2.setRoleName(“角色2”);

SysRole r3 =
new SysRole();
r3.setRoleName(“角色3”);
//*
这步操作是让中间表产生关系

// 让 1号用户具有 1号和 2号角色(双向的)
//
让 2号用户具有 2号和 3号角色(双向的)

//给用户分配角色
u1.getRoles().add(r1);
u1.getRoles().add(r2);

u2.getRoles().add(r2);
u2.getRoles().add(r3);

//把角色授予用户
r1.getUsers().add(u1);
r2.getUsers().add(u1);

r2.getUsers().add(u2);
r3.getUsers().add(u2);
//**
em.persist(u1);
em.persist(u2);
em.persist(r1);
em.persist(r2);
em.persist(r3);

tx.commit();
em.close();

}

多对多的删除用户和角色操作

不要使用!!!!!!!!!!!很多不相关的数据也会被删除掉.
/
删除操作 **
需要配置级联操作
删除从表数据:可以随时任意删除。
删除主表数据:
有从表数据引用
1、不能删除
2、如果还想删除,使用级联删除
没有从表数据引用:随便删

* 在实际开发中,级联删除请慎用!(在多对多的情况下) ,多对多的关联数据都会删除,
/
@Test
public void test2() {
EntityManager em = JpaUtils.getEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();

SysUser u1 = em.find(SysUser.class, 1L);

em.remove(u1);

tx.commit();
em.close();
}


在用户实体类我里面配置级联 也可以双向级联,就是两个实体类都配置级联,这样三张表的数据都会删除, 不要使用

@ManyToMany(targetEntity = SysRole.class, mappedBy = “users”, cascade = CascadeType.ALL)
private Set roles = *new
HashSet(0);