文章目录
一、事务的ACID原则
原子性:原子性指事务是数据库工作的最小单位,一个事务中的所有操作要么全部成功提交,要么全部失败回滚。
一致性:一致性指事务操作不能破坏数据的一致性,数据库在一个事务的执行前后都应处于一致性状态。
隔离性:多个事务可能操作同一个资源,防止数据的损坏
持久性:数据一单提交,结果不可被改变
二、制造一个异常/错误
xxxMapper.xml
<!--删除-->
<delete id="delUserById" parameterType="int">
deletes from `user` where id=#{id}
</delete>
UserMappperImp02
public class UserMapperImp02 extends SqlSessionDaoSupport implements UserMapper {
public List<User> selUsers() {
SqlSession sqlSession = getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map<String, String> map = new HashMap();
map.put("userId","8");
map.put("userName","妮妮");
map.put("userPasswd","868sgayi");
mapper.insertUser(map);
mapper.delUserById(8);
return mapper.selUsers();
}
public List<User> selUserById(int id) {
return getSqlSession().getMapper(UserMapper.class).selUserById(id);
}
public List<User> selUserByOther(Map map) {
return getSqlSession().getMapper(UserMapper.class).selUserByOther(map);
}
public int insertUser(Map map) {
return getSqlSession().getMapper(UserMapper.class).insertUser(map);
}
public int delUserById(int id) {
return getSqlSession().getMapper(UserMapper.class).delUserById(id);
}
public int updateUser(Map map) {
return getSqlSession().getMapper(UserMapper.class).updateUser(map);
}
}
测试前:
测试:
@Test
public void test02(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper userMapper = context.getBean("UserMapperImp02", UserMapper.class);
List<User> users = userMapper.selUsers();
for (User user : users) {
System.out.println(user);
}
System.out.println("test02()完成......");
}
我们可以看到,程序执行出错,但是插入开始成功了,这并不是我们希望的。如下
三、配置声明式事务
不会改变原有的任何代码,利用的是AOP实现的。
第一步:引入事务头(tx)
第二步:配置事务管理器
<!-- 1. 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
第三步:配置事务通知(基本上是固定写法)
当然你也可以偷个懒,使用“*”,将所有方法都配置事务通知,如下:
介绍一下spring中事务的七大转播(propagation)特性:
REQUIRED :如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是最常见的选择。(默认)
SUPPORTS :支持当前事务,如果没有当前事务,就以非事务方法执行。
MANDATORY :使用当前事务,如果没有当前事务,就抛出异常。
REQUIRES_NEW :新建事务,如果当前存在事务,把当前事务挂起。
NOT_SUPPORTED :以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
NEVER :以非事务方式执行操作,如果当前事务存在则抛出异常。
NESTED :如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED 类似的操作
第四步:结合AOP实现事务的织入
第四步:测试
测试前:
测试代码:
@Test
public void test02(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper userMapper = context.getBean("UserMapperImp02", UserMapper.class);
List<User> users = userMapper.selUsers();
for (User user : users) {
System.out.println(user);
}
System.out.println("test02()完成......");
}
很明显,和没有使用声明式事务相比,在删除失败后,插入也被回滚了,id=8 的信息并没有被插入成功。
spring-dao.xml的整体代码
<?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:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd">
<!--DataSource:使用Spring的DataSource取代mybatis中的dataSource-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&
characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="root123123"/>
</bean>
<!--在spring中配置sqlSessionFactoryBean-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!--绑定mybatis的核心配置文件-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!--实现mybatis中的mapper的注册-->
<property name="mapperLocations" value="classpath:com/sdpei/mapper/*.xml"/>
</bean>
<!--使用SqlSessionTemplate取代原本mybatis中的SqlSession-->
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<!--因为没有set方法,所以注入的时候只能使用构造器注入-->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
<!-- #########################声明式事务(事务ACID原则的实现) #########################-->
<!-- 1. 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--2. 结合AOP实现事务的织入-->
<!--配置事务通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!--给那些方法配置事务-->
<!--配置事务的转播特性 propagation="REQUIRED" (默认)-->
<tx:method name="selUsers" read-only="true"/>
<tx:method name="selUserById" read-only="true"/>
<tx:method name="selUserByOther" read-only="true"/>
<tx:method name="insertUser" propagation="REQUIRED"/>
<tx:method name="delUserById" propagation="REQUIRED"/>
<tx:method name="updateUser" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!--3. 事务的切入点-->
<aop:config>
<aop:pointcut id="txPointCut" expression="execution(* com.sdpei.mapper.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>
</beans>