第六章 MyBatis 与Spring 整合
持久层整合
(1)Spring为什么要与持久层整合
1.JavaEE开发需要持久层进行数据库访问操作
2.JDBC hibernate MyBatis进行持久开发又大量的代码冗余
3.Spring基于模板设计模式对于上述持久层技术进行了封装
(2)Spring 与 持久层整合?
1.JDBC -- JDBCTemplate
2.Hibernate(JPA) -- HibernateTemplate
3.MyBatis -- SqlSessionFactoryBean
-- MapperScannerConfigure
MyBatis 在开发中存在的问题
配置繁琐 代码冗余
1. 实体
2. 实体别名 配置繁琐
3. 表
4. 创建DAO接⼝
5. 实现Mapper⽂件
6. 注册Mapper⽂件 配置繁琐
7. MybatisAPI调⽤ 代码冗余
Spring与MyBatis整合思路分析
Spring 和MyBatis 整合开发
(1)环境搭建
gradle 添加如下包依赖
// https://mvnrepository.com/artifact/org.springframework/spring-jdbc
compile group: 'org.springframework', name: 'spring-jdbc', version: '5.2.0.RELEASE'
compile group: 'org.mybatis', name: 'mybatis-spring', version: '2.0.4'
// https://mvnrepository.com/artifact/com.alibaba/druid
compile group: 'com.alibaba', name: 'druid', version: '1.1.21'
compile group: 'mysql', name: 'mysql-connector-java', version: '5.1.18'
// https://mvnrepository.com/artifact/org.mybatis/mybatis
compile group: 'org.mybatis', name: 'mybatis', version: '3.4.6'
(2)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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--
<context:property-placeholder location="db.properties"/>
-->
<!--连接池配置-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<!-- <property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>-->
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/demo"/>
<property name="username" value="root"/>
<property name="password" value="manager1"/>
</bean>
<!--
创建sqlSessionFactory SqlSessionFactoryBean-->
<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!--配置类别名 实体类所在的包-->
<property name="typeAliasesPackage" value="com.springmybatis.basic"></property>
<!--配置映射文件的路径-->
<property name="mapperLocations">
<list>
<value>
classpath:com.springmybatis.mapper.pz/*Mapper.xml
</value>
</list>
</property>
</bean>
<!--
创建DAO对象 MapperScannerConfigure-->
<bean id="scannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean"/>
<!--指定dto接口放的包-->
<property name="basePackage" value="com.springmybatis.mapper"/>
</bean>
</beans>
(3)按照配置文件配置的类别名,映射文件路径,指定dto对象的路径放对应的类或者xml文件
1. 实体
2. 表
3. DAO接⼝
4. Mapper⽂件配置
Spring 与MyBatis整合细节
Spring与MyBatis整合后,DTO不提交事务,但是数据能够插入数据库
1. Mybatis提供的连接池对象 ---> 创建Connection
Connection.setAutoCommit(false)⼿⼯的控制了事务 , 操作完成后,⼿⼯提交
2. Druid(C3P0 DBCP)作为连接池---> 创建Connection
Connection.setAutoCommit(true)true默认值 保持⾃动控制事务,⼀条sql⾃动提交
答案:因为Spring与Mybatis整合时,引⼊了外部连接池对象,保持⾃动的事务提交这个机制(Connection.setAutoCommit(true)),不需要⼿⼯进⾏事务的操作,也能进⾏事务的提交
Spring对事务的处理
(1)什么是事务
保证业务操作完整性的⼀种数据库机制
事务的4特点: A C I D
1. A 原⼦性
2. C ⼀致性
3. I 隔离性
4. D 持久性
(2)如何控制事务
JDBC:
Connection.setAutoCommit(false);
Connection.commit();
Connection.rollback();
MyBatis:
Mybatis⾃动开启事务
sqlSession(Connection).commit();
sqlSession(Connection).rollback();
---控制事务的底层 都是Connection对象完成的。
(3)Spring控制事务的开发
Spring是通过AOP的方式进行事务开发
原始对象
扫描二维码关注公众号,回复:
12416396 查看本文章

1. 原始对象 ---》 原始⽅法 ---》核⼼功能 (业务处理+DAO调⽤)
2. DAO作为Service的成员变量,依赖注⼊的⽅式进⾏赋值
额外功能
1.org.springframework.jdbc.datasource.DataSourceTransactionManager
2. 注⼊DataSource
1. MethodInterceptor
public Object invoke(MethodInvocation invocation){
try{
Connection.setAutoCommit(false);
Object ret = invocation.proceed();
Connection.commit();
}catch(Exception e){
Connection.rollback();
}
return ret;
}
2. @Aspect
@Around
切入点
@Transactional
事务的额外功能加⼊给那些业务⽅法。
1. 类上:类中所有的⽅法都会加⼊事务
2. ⽅法上:这个⽅法会加⼊事务
组装切面
1. 切⼊点
2. 额外功能
<tx:annotation-driven transactionmanager=""/>
(4)开发
①添加依赖
//spring mybatis事务控制
// https://mvnrepository.com/artifact/org.springframework/spring-tx
compile group: 'org.springframework', name: 'spring-tx', version: '5.2.0.RELEASE'
②原始对象并在原始对象设值切入点
package com.springmybatis.service;
import com.springmybatis.basic.User;
import com.springmybatis.mapper.UserMapper;
import com.springmybatis.service.imp.UserServiceImp;
import org.springframework.transaction.annotation.Transactional;
/**
* @author PitterWang
* @create 2020/6/15
* @since 1.0.0
*/
@Transactional
public class UserService implements UserServiceImp {
private UserMapper userMapper;
public UserMapper getUserMapper() {
return userMapper;
}
public void setUserMapper(UserMapper userMapper) {
this.userMapper = userMapper;
}
@Override
public void insertUser(User user) {
userMapper.insertUser(user);
}
}
<bean id="userService" class="com.springmybatis.service.UserService">
<property name="userMapper" ref="userMapper"/>
</bean>
③额外功能
<bean id= "dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
④组装切面
<tx:annotation-driven transaction-manager="dataSourceTransactionManager" proxy-target-class="true"/>
---proxy-target-class为true为用CGlib创建动态代理
---proxy-target-class为false为用JDK创建动态代理
Spring中的事务属性(Transaction Attribute)
(1)什么是事务属性
事务属性:描述事务特征的一系列值
1.隔离属性
2.传播属性
3.只读属性
4.超时属性
5.异常属性
事务属性的配置方法
@Transactional(isloation=,propagation=,readOnly=,timeout=,rollbackFor=,noRollbackFor=,)
(2)事务属性详解
隔离属性(ISOLATION)
1.隔离属性:事务解决并发问题的特征
2.事务并发产生的问题
脏读:一个事务,读取另一个事务没有提交的数据,会在本事务中产生不一致的问题
解决方法:@Transactional(isolation=Isolation.READ_COMMITTED)
不可重复读:一个事务中,多次读取相同的数据,但是读取结果不一样,会在本事务中产生数据不一致问题(一个事务中)
解决方法:@Transactional(isolation=Isolation.REPEATABLE_READ)行锁
幻读:⼀个事务中,多次对整表进⾏查询统计,但是结果不⼀样,会在本事务中产⽣数据不⼀致的问题
解决方法:@Transactional(isolation=Isolation.SERIALIZABLE)表锁
3.总结:
并发安全:SERIALIZABLE>REPEATABLE_READ>READ_COMMITTED
运行效率:READ_COMMITTED>REPEATABLE_READ>SERIALIZABLE
4.数据库对于隔离属性的⽀持
Oracle不⽀持REPEATABLE_READ值
5.默认隔离属性
ISOLATION_DEFAULT:会调⽤不同数据库所设置的默认隔离属性
MySQL : REPEATABLE_READ
Oracle: READ_COMMITTED
传播属性(PROPAGATION)
1.传播属性:他描述了事务解决嵌套问题的特征
2.参数的问题
⼤事务中融⼊了很多⼩的事务,他们彼此影响,最终就会导致外部⼤的事务,丧失了事务的原⼦性
3.传播属性的值及其⽤法
见下表
4.默认传播属性
REQUIRED是传播属性的默认值
5.推荐传播属性的使⽤⽅式
增删改⽅法:直接使⽤默认值REQUIRED
查询操作:显示指定传播属性的值为SUPPORTS
传播属性的值 | 外部不存在事务 | 外部存在事务 | ⽤法 |
---|---|---|---|
REQUIRED | 开启 新的 事务 | 融合到外部 事务中 | @Transactional(propagation = Propagation.REQUIRED) |
SUPPORTS | 不开 启事 务 | 融合到外部 事务中 | @Transactional(propagation = Propagation.SUPPORTS) |
REQUIRES_NEW | 开启 新的 事务 | 挂起外部事 务,创建新 的事务 | @Transactional(propagation = Propagation.REQUIRES_NEW) |
NOT_SUPPORTED | 不开 启事 务 | 挂起外部事 务 | @Transactional(propagation = Propagation.NOT_SUPPORTED) |
NEVER | 不开 启事 务 | 抛出异常 | @Transactional(propagation = Propagation.NEVER) |
MANDATORY | 抛出 异常 | 融合到外部 事务中 | @Transactional(propagation = Propagation.MANDATORY) |
只读属性(readOnly)
针对于只进⾏查询操作的业务⽅法,可以加⼊只读属性,提供运⾏效率
默认值:false
超时属性(timeout)
指定了事务等待的最⻓时间
1. 为什么事务进⾏等待?
当前事务访问数据时,有可能访问的数据被别的事务进⾏加锁的处理,那么此时本事务就必须进⾏等待。
2. 等待时间 秒
3. 如何应⽤
@Transactional(timeout=2)
4. 超时属性的默认值 -1
最终由对应的数据库来指定
异常属性
Spring事务处理过程中
默认对于RuntimeException及其⼦类 采⽤的是回滚的策略
默认对于Exception及其⼦类 采⽤的是提交的策略
rollbackFor ={java.lang.Exception,xxx,xxx}
noRollbackFor ={java.lang.RuntimeException,xxx,xx}
@Transactional(rollbackFor ={java.lang.Exception.class},noRoll
backFor ={java.lang.RuntimeException.class})
建议:实战中使⽤RuntimeExceptin及其⼦类 使⽤事务异常属性的默认值
(3)基于标签的事务配置⽅式
原始方法
<bean id="userService" class="com.springmybatis.service.UserService">
<property name="userMapper" ref="userMapper"/>
</bean>
扩展方法
<bean id= "dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
切入点
以前是注解方式,现在用配置文件
<tx:advice id = "txAdvice" transaction-manager="dataSourceTransactionManager">
<tx:attributes>
<tx:method name="insertUser" isolation="READ_COMMITTED" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
切面
<aop:config>
<aop:pointcut id="pc" expression="execution(* com.springmybatis.service.UserService.insertUser(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pc"/>
</aop:config>