1. SpringDataJpa的概述
1. 概述
Spring Data JPA 是 Spring 基于 ORM 框架、JPA 规范的基础上封装的一套JPA应用框架,可使开发者用极简的代码即可实现对数据库的访问和操作。它提供了包括增删改查等在内的常用功能,且易于扩展!学习并使用 Spring Data JPA 可以极大提高开发效率!
Spring Data JPA 让我们解脱了DAO层的操作,基本上所有CRUD都可以依赖于它来实现,在实际的工作工程中,推荐使用Spring Data JPA + ORM(如:hibernate)完成操作,这样在切换不同的ORM框架时提供了极大的方便,同时也使数据库层操作更加简单,方便解耦
2. 特性
使用了SpringDataJpa,我们的dao层中只需要写接口,就自动具有了增删改查、分页查询等方法
3. Spring Data JPA和JPA以及hibernate的关系
JPA是一套规范,内部是有接口和抽象类组成的。hibernate是一套成熟的ORM框架,而且Hibernate实现了JPA规范,所以也可以称hibernate为JPA的一种实现方式,我们使用JPA的API编程,意味着站在更高的角度上看待问题(面向接口编程)
Spring Data JPA是Spring提供的一套对JPA操作更加高级的封装,是在JPA规范下的专门用来进行数据持久化的解决方案。
2. 快速入门
1. 需求分析
完成客户的基本CRUD操作
2. 搭建开发环境
1. 引入坐标
<properties>
<spring.version>4.2.4.RELEASE</spring.version>
<hibernate.version>5.0.7.Final</hibernate.version>
<slf4j.version>1.6.6</slf4j.version>
<log4j.version>1.2.12</log4j.version>
<c3p0.version>0.9.1.2</c3p0.version>
<mysql.version>5.1.6</mysql.version>
</properties>
<dependencies>
<!-- junit单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.9</version>
<scope>test</scope>
</dependency>
<!-- spring beg -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.8</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- spring end -->
<!-- hibernate beg -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.2.1.Final</version>
</dependency>
<!-- hibernate end -->
<!-- c3p0 beg -->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>${c3p0.version}</version>
</dependency>
<!-- c3p0 end -->
<!-- log end -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
<!-- log end -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.9.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<!-- el beg 使用spring data jpa 必须引入 -->
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<version>2.2.4</version>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>javax.el</artifactId>
<version>2.2.4</version>
</dependency>
<!-- el end -->
</dependencies>
2. 整合springdata JPA与spring
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jpa="http://www.springframework.org/schema/data/jpa" xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
<!--spring和spring data jpa的配置-->
<!--1. 创建entityManagerFactory对象交给spring容器管理-->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!--配置扫描的包,实体类所在的包-->
<property name="packagesToScan" value="cn.itcast.domain"/>
<!--jpa的实现厂家-->
<property name="persistenceProvider">
<bean class="org.hibernate.jpa.HibernatePersistenceProvider"/>
</property>
<!--jpa的供应商适配器-->
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<!--是否自动创建数据库表-->
<property name="generateDdl" value="false"/>
<!--指定数据库类型-->
<property name="database" value="MYSQL"/>
<!--数据库方言-->
<property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect"/>
<!--是否显示SQL语句-->
<property name="showSql" value="true"/>
</bean>
</property>
<!--jpa的方言,高级的特性-->
<property name="jpaDialect">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/>
</property>
</bean>
<!--2. 创建数据库连接池-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="root"/>
<property name="password" value="root"/>
<property name="jdbcUrl" value="jdbc:mysql:///jpa"/>
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
</bean>
<!--3. 整合spring dataJpa-->
<jpa:repositories base-package="cn.itcast.dao" transaction-manager-ref="txManager"
entity-manager-factory-ref="entityManagerFactory"></jpa:repositories>
<!--4. 配置事务管理器-->
<bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<!--5. 声明式事务-->
<!--6. 配置包扫描-->
<context:component-scan base-package="cn.itcast"/>
</beans>
3. 编写实体类
使用jpa注解配置映射关系,同之前的
3. 编写dao层接口
只需要编写dao接口,不需要编写实现类
dao层接口规范:
- 需要继承两个接口(JpaRepository,JpaSpecificationExecutor)
- 需要提供相应的泛型
JpaRepository<操作的实体类类型,实体类中主键属性的类型>
封装了基本CRUD操作JpaSpecificationExecutor<操作的实体类类型>
封装了复杂查询(分页)
4. 增删改查
findOne(id):根据id查询
save:保存或更新
根据传递的对象是否存在主键id,如果没有id主键属性:保存;存在id主键属性,更新数据
delete(id):根据id删除
findAll():查询全部
@RunWith(SpringJUnit4ClassRunner.class)//声明spring提供的单元测试环境
@ContextConfiguration(locations = "classpath:applicationContext.xml")//spring容器的配置信息
public class CustomerDaoTest {
@Autowired
private CustomerDao customerDao;
@Test
public void testFindOne(){
Customer customer = customerDao.findOne(2L);
System.out.println(customer);
}
@Test
public void testSave(){
Customer customer = new Customer();
customer.setCustName("zhangsan");
customer.setCustLevel("vip");
customer.setCustIndustry("金融");
customerDao.save(customer);
}
@Test
public void testUpdate(){
Customer customer = new Customer();
customer.setCustId(9L);
customer.setCustName("lisi");
customerDao.save(customer);
}
@Test
public void testDelete(){
customerDao.delete(9L);
}
@Test
public void testFindAll(){
List<Customer> list = customerDao.findAll();
for (Customer customer : list) {
System.out.println(customer);
}
}
}
5. 复杂查询
1. 借助接口中定义好的方法完成查询
判断id为4的客户是否存在
- 可以查询id为4的客户,如果值为空,不存在;不为空,存在
- 判断数据库中id为4的客户的数量,如果为0,不存在;大于0,存在
// 测试统计查询:查询客户数量
@Test
public void testCount(){
long count = customerDao.count();
System.out.println(count);
}
// 根据id从数据库查询 事务注解保证正常运行
@Test
public void testExists(){
boolean exists = customerDao.exists(4L);
System.out.println("id 为4的客户 是否存在:"+exists);
}
@Test
@Transactional
public void testGetOne(){
Customer customer = customerDao.getOne(4L);
System.out.println(customer);
}
2. jpql查询
需要将jpql语句配置到接口方法上
针对特有的查询,在新添加的方法上,使用注解的形式(@Query
)配置jpql查询语句
对于多个占位符参数,赋值的时候
-
默认情况下,占位符的位置需要和方法参数的位置保持一致
-
可以指定占位符参数的位置,
?
索引的方式,指定此占位的取值来源eg.
@Query(value = "from Customer where custName=?2 and custId=?1")
springDataJpa使用jpql完成 更新/删除操作
- 需要手动添加事务的支持
- 默认会执行结束后,回滚事务,需要设置是否自动回滚
public interface CustomerDao extends JpaRepository<Customer,Long>, JpaSpecificationExecutor<Customer> {
// 案例1:根据客户行业查询客户
@Query(value = "from Customer where custIndustry=?")
public Customer findJpql(String custIndustry);
// 案例2:根据客户名称和客户id查询客户
@Query(value = "from Customer where custName=? and custId=?")
public Customer findByCustNameAndId(String name,Long id);
// 根据id更新,客户的名称
// @Query 代表的是进行查询,需要声明该方法是进行更新操作
@Query(value = "update Customer set custName=?2 where custId=?1")
@Modifying
public void updateCustom(long custId,String custName);
}
@RunWith(SpringJUnit4ClassRunner.class)//声明spring提供的单元测试环境
@ContextConfiguration(locations = "classpath:applicationContext.xml")//spring容器的配置信息
public class JpqlTest {
@Autowired
private CustomerDao customerDao;
@Test
public void testFindJpql(){
Customer customer = customerDao.findJpql("电商1");
System.out.println(customer);
}
@Test
public void testFindByCustNameAndId(){
Customer customer = customerDao.findByCustNameAndId("tom", 2L);
System.out.println(customer);
}
// 测试jpql的更新操作
@Test
@Transactional
@Rollback(value = false)
public void testUpdateCustomer(){
customerDao.updateCustom(4L,"猪八戒");
}
3. SQL语句的查询
-
特有的查询:需要在dao接口上配置方法
-
在新添加的方法上,使用注解(
@Query
)的形式配置sql查询语句注解:
@Query
属性value
jpql语句 | SQL语句nativeQuery:false | true 是否使用本地查询(也就是sql)
4. 方法名称规则查询
是对jpql查询,更加深入的一层封装
只需要按照springDataJpa提供的方法名称规则定义方法,不需要再去配置jpql语句,完成查询
方法名的约定:
-
findBy开头:代表查询 对象中属性的名称(首字母大写)
含义:根据属性名称进行查询
findBy:查询
对象中的属性名(首字母大写),查询的条件
CustName
* 默认情况: 使用等于的方式查询
特殊的查询方式
findByCustName -- 根据客户名称查询
在springdataJpa的运行阶段
会根据方法名称进行解析 findBy from xxx(实体类)
属性名称 where custName=
findBy+属性名称(根据属性名称进行完成匹配的查询=)
findBy+属性名称+“查询方式(Like | isnull)”
findByCustNameLike
3. 多条件查询
findBy+属性名+“查询方式”+“多条件的连接符(and | or)”+属性名+“查询方式”
// 方法名的约定
public Customer findByCustName(String custName);
public List<Customer> findByCustNameLike(String custName);
//使用客户名称模糊匹配和客户所属行业精准匹配的查询
public Customer findByCustNameLikeAndCustIndustry(String custName,String custIndustry);
// 测试方法命名规则的查询
@Test
public void testNaming(){
Customer customer = customerDao.findByCustName("猪八戒");
System.out.println(customer);
}
// 测试方法命名规则的查询
@Test
public void testFindByCustNameLike(){
List<Customer> customerList = customerDao.findByCustNameLike("猪八%");
for (Customer customer : customerList) {
System.out.println(customer);
}
}
// 测试方法命名规则的查询
@Test
public void testFindByCustNameLikeAndCustIndustry(){
Customer customer = customerDao.findByCustNameLikeAndCustIndustry("猪八%", "电商1");
System.out.println(customer);
}
3. 内部原理
1. 实现过程
4. Specifications动态查询
1. JpaSpecificationExecutor方法列表
-
T findOne(Specification<T> var1);
查询单个对象 -
List<T> findAll(Specification<T> var1);
查询列表 -
Page<T> findAll(Specification<T> var1, Pageable var2);
查询全部:分页其中page是SpringDataJpa提供的
-
List<T> findAll(Specification<T> var1, Sort var2);
查询列表,排序 -
long count(Specification<T> var1);
统计查询
2. Specification:查询条件
1. 概念
自定义自己的Specification实现类
实现:
Predicate toPredicate(Root<T> var1, CriteriaQuery<?> var2, CriteriaBuilder var3);
参数:
- root 查询的根对象(查询的任何属性都可以从根对象中获取)
- CriteriaQuery:顶层的查询对象,自定义查询方式(了解,一般不用)
- CriteriaBuilder:查询的构造器,封装了很多查询条件
2. 代码范例
- 多条件查询
- 模糊查询
- 分页查询
@RunWith(SpringJUnit4ClassRunner.class)//声明spring提供的单元测试环境
@ContextConfiguration(locations = "classpath:applicationContext.xml")//spring容器的配置信息
public class SpecTest {
@Autowired
private CustomerDao customerDao;
/**
* 匿名内部类
*
* 自定义查询条件
* 1. 实现Specification接口(提供泛型,查询的对象类型)
* 2. 实现toPredicate方法(构造查询条件)
* 3. 需要借助方法参数的两个参数(
* root:获取需要查询的对象属性
* CriteriaBuilder:构造查询条件,内部封装很多的查询条件(精确匹配,模糊匹配)
* )
* 案例: 根据客户名称查询,查询客户名为猪八戒的客户
* 查询条件
* 1. 查询方式
* cb对象
* 2. 比较的属性名称
* root对象
*/
@Test
public void testSpec(){
Specification<Customer> spec= new Specification<Customer>() {
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder cb) {
// 1. 获取比较的属性
Path<Object> custName = root.get("custName");
// 2. 构造查询条件
Predicate predicate = cb.equal(custName, "猪八戒");//进行精准匹配(需要比较的属性,比较的属性的取值)
return predicate;
}
};
Customer customer = customerDao.findOne(spec);
System.out.println(customer);
}
/**
* 多条件查询
* 案例:根据客户名(猪八戒)和客户所属行业查询(电商1)
* root:获取属性
* 客户名
* 所属行业
* cb:构造查询
* 1.构造客户名的精准匹配查询
* 2. 构造所属行业的精准匹配查询
* 3. 将以上两个查询联系起来
*/
@Test
public void testMultiSpec(){
Specification<Customer> spec = new Specification<Customer>() {
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder cb) {
Path<Object> custName = root.get("custName");
Path<Object> custIndustry = root.get("custIndustry");
// 构造查询
Predicate p1 = cb.equal(custName, "猪八戒");
Predicate p2 = cb.equal(custIndustry, "电商1");
// 组合
Predicate and = cb.and(p1, p2);//与
//Predicate or = cb.or(p1, p2);//或
return and;
}
};
Customer customer = customerDao.findOne(spec);
System.out.println(customer);
}
/**
* 案例:完成根据客户行业的模糊匹配,返回客户列表
* 客户行业以“电商”开头
* equal:直接得到path对象(属性),然后比较
* gt,lt,ge,le,like:得到path对象,根据path指定比较的参数类型,再比较
* 指定参数类型:path.as(类型的字节码对象)
*/
@Test
public void testSpec3(){
// 构造查询条件
Specification<Customer> spec = new Specification<Customer>() {
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder cb) {
// 查询属性:客户行业
Path<Object> custIndustry = root.get("custIndustry");
// 查询方式:模糊匹配
Predicate like = cb.like(custIndustry.as(String.class), "电商%");
return like;
}
};
/**
* 添加排序
* 创建排序对象,需要调用构造方法实例化sort对象
* 第一个参数:排序的顺序(正序、倒序)
* Sort.Direction.DESC
* Sort.Direction.ASC
* 第二个参数:排序的属性名称
*/
Sort sort = new Sort(Sort.Direction.DESC,"custId");
List<Customer> customerList = customerDao.findAll(spec,sort);
for (Customer customer : customerList) {
System.out.println(customer);
}
}
/**
* 分页查询
* Specification:查询条件
* Pageable: 分页参数
* 分页参数:查询的页码,每页查询的条数
* findAll(Specification,Pageable) 带有条件的分页
* findAll(Pageable):每页条件的分页
* 返回:Page(SpringDataJpa为我们封装好的pageBean对象,数据列表,总条数
*/
@Test
public void testSpec4(){
Specification spec = null;
/**
* PageRequest对象是Pageable接口的实现类
* 创建PageRequest的过程,需要调用它的构造方法传入两个参数
* 第一个参数:当前查询的页数(从0开始)
* 第二个参数:每页查询的数量
*/
Pageable pageable = new PageRequest(0,2);
Page<Customer> page = customerDao.findAll(null, pageable);
System.out.println(page.getTotalElements());// 总条数
System.out.println(page.getContent());// 数据集合列表
System.out.println(page.getTotalPages());//总页数
}
}
5. 多表之间关系和操作多表
1. 概念
1. 表关系
一对一
一对多:
一的一方:主表
多的一方:从表
外键:需要再从表上新建一列作为外键,他的取值来源于主表的主键
多对多:
中间表:中间表中最少应该由两个字段组成,这两个字段做为外键指向两张表的主键,又组成了联合主键
2. 实体类中的关系
包含关系:可以通过实体类中的包含关系描述表关系
继承关系
3. 分析步骤
1.明确表关系
2.确定表关系(描述 外键|中间表)
3.编写实体类,再实体类中描述表关系(包含关系)
4.配置映射关系
2. 案例
1. 一对多操作
案例:客户和联系人的案例(一对多关系)
客户:一家公司;联系人:这家公司的员工
一个客户可以具有多个联系人;一个联系人从属于一家公司
分析步骤
1.明确表关系
一对多关系
2.确定表关系(描述 外键|中间表)
主表:客户表
从表:联系人表
* 在从表上添加外键
3.编写实体类,再实体类中描述表关系(包含关系)
客户:在客户的实体类中包含一个联系人的集合
联系人:在联系人的实体类中包含一个客户的对象
4.配置映射关系
* 使用jpa注解配置一对多映射关系
使用注解的形式配置多表关系
-
声明关系
@OneToMany
: 配置一对多关系 targetEntity: 对方对象的字节码对象
-
配置外键(中间表)
@JoinColumn
: 配置外键 name:外键字段名称
referencedColumnName:参照的主表的主键字段名称
在客户实体类上(一的一方)添加了外键的配置,所以对于客户而言,也具备了维护外键的作用
配置联系人到客户的多对一关系,使用注解的形式配置多对一的关系
-
配置表关系
@ManyToOne
: 配置多对一关系 targetEntity:对方的实体类字节码
-
配置外键(中间表)
配置外键的过程,配置到了多的一方,就会在多的一方维护外键
@Entity//声明实体类
@Table(name = "cst_customer")//配置实体类和表的映射关系
public class Customer implements Serializable {
@Id//声明主键的配置
@GeneratedValue(strategy = GenerationType.IDENTITY)//配置主键的生成策略,自增
@Column(name = "cust_id")//配置属性和字段的映射关系
private Long custId;
// ...
// 配置客户和联系人的关系(一对多关系)
@OneToMany(targetEntity = LinkMan.class)
@JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id")
private Set<LinkMan> linkMans = new HashSet<LinkMan>();
@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;
@RunWith(SpringJUnit4ClassRunner.class)//声明spring提供的单元测试环境
@ContextConfiguration(locations = "classpath:applicationContext.xml")//spring容器的配置信息
public class One2ManyTest {
@Autowired
private CustomerDao customerDao;
@Autowired
private LinkManDao linkManDao;
/**
* 保存一个客户,保存一个联系人
*/
@Test
@Transactional
@Rollback(false)
public void testAdd(){
// 创建一个客户,创建一个联系人
Customer customer = new Customer();
customer.setCustName("百度");
LinkMan linkMan = new LinkMan();
linkMan.setLkmName("小李");
// 配置客户到联系人的关系(一对多)
customer.getLinkMans().add(linkMan);
customerDao.save(customer);
linkManDao.save(linkMan);
}
}
其实有些冗余,可以放弃一方(客户)对外键的维护权,
@OneToMany(mappedBy = "customer")
private Set<LinkMan> linkMans = new HashSet<LinkMan>();
删除
- 删除从表数据,随意
- 删除主表数据
- 有从表数据:
- 在默认情况下,把外键字段设为null,然后删除主表数据,如果在数据库的表结构上,外键字段有非空约束,会报错
- 如果配置了放弃维护关联关系的权利,不能删除,删除时,不会更新从表的外键字段
- 如果还想删除,使用级联删除引用
- 没有从表数据引用:随便删除
实际开发中,级联删除慎用(一对多的情况下)
2. 级联
级联操作:操作一个对象同时操作它的关联对象
使用方法:在操作主体的注解上配置cascade
customer类
// 配置客户和联系人的关系(一对多关系)
// @OneToMany(targetEntity = LinkMan.class)
// @JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id")
@OneToMany(mappedBy = "customer",cascade = CascadeType.ALL)
private Set<LinkMan> linkMans = new HashSet<LinkMan>();
修改jpa的配置
<property name="jpaProperties">
<props>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
级联删除
// 级联删除:删除1号客户的同时,删除对应的所有联系人
@Test
@Transactional
@Rollback(false)
public void testCascadeRemove(){
// 查询1号客户
Customer customer = customerDao.findOne(1L);
// 删除1号客户
customerDao.delete(customer);
}
3. 多对多
案例为:用户和角色
1. 建立实体类关系和映射配置
用户实体类
@Entity
@Table(name = "sys_user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "user_id")
private Long userId;
@Column(name = "user_name")
private String userName;
@Column(name = "age")
private Integer age;
// 配置用户到角色的多对多关系
@ManyToMany(targetEntity = Role.class)
@JoinTable(name = "sys_user_role",
// 当前对象在中间表中的外键
joinColumns = {@JoinColumn(name="sys_user_id",referencedColumnName = "user_id")},
// 对方对象在中间表的外键
inverseJoinColumns = {@JoinColumn(name="sys_role_id",referencedColumnName = "role_id")}
)
private Set<Role> roles = new HashSet<Role>();
角色实体类
@Entity
@Table(name = "sys_role")
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "role_id")
private Long roleId;
@Column(name = "role_name")
private String roleName;
@ManyToMany(mappedBy = "roles")
private Set<User> users = new HashSet<User>();
// ...
多对多关系,被动的一方放弃维护权。角色的配置为上面代码所示
保存操作
@RunWith(SpringJUnit4ClassRunner.class)//声明spring提供的单元测试环境
@ContextConfiguration(locations = "classpath:applicationContext.xml")//spring容器的配置信息
public class Many2ManyTest {
@Autowired
private UserDao userDao;
@Autowired
private RoleDao roleDao;
// 保存一个用户,保存一个角色
@Test
@Transactional
@Rollback(false)
public void testAdd(){
User user = new User();
user.setUserName("小王");
Role role = new Role();
role.setRoleName("java程序员");
// 配置用户到角色关系,可以对中间表的数据进行维护 1-1
user.getRoles().add(role);
userDao.save(user);
roleDao.save(role);
}
级联同一对多。
3. 多表的查询
1. 对象导航查询
查询一个对象的同时,通过此对象查询他的关联对象
案例:客户和联系人
- 从一方查询多方
对象导航查询,默认使用延迟加载的形式查询,调用get方法并不会立即发送查询,而是在使用关联对象的时候才会查询
如果想改为立即加载,如何?
fetch,需要配置到多表映射关系的注解上
@OneToMany(mappedBy = "customer",cascade = CascadeType.ALL,fetch = FetchType.EAGER)
private Set<LinkMan> linkMans = new HashSet<LinkMan>();
- 从多方查询一方:
从表(联系人)对象导航查询他的所属客户(公司),默认是立即加载
示范代码
@RunWith(SpringJUnit4ClassRunner.class)//声明spring提供的单元测试环境
@ContextConfiguration(locations = "classpath:applicationContext.xml")//spring容器的配置信息
public class ObjectQueryTest {
@Autowired
private CustomerDao customerDao;
@Autowired
private LinkManDao linkManDao;
// 测试id为1的客户
// could not initialize proxy - no Session
@Test
@Transactional// 解决no session
public void testQuery1(){
// 查询id为1的客户
Customer customer = customerDao.getOne(2L);
// 对象导航查询,此客户下的所有联系人
Set<LinkMan> linkMans = customer.getLinkMans();
for (LinkMan linkMan : linkMans) {
System.out.println(linkMan);
}
}
}