Spring学习(十)Spring事务管理
声明式事务
回顾事务
- 把一组业务当成一个业务来做,要么都成功,要么都失败!
- 事务在项目开发中十分重要,涉及到数据的一致性问题!
- 确保完整性和一致性、
事务的ACID原则:
- 原子性
- 不可分割,一组事务不可被分割,要么都完成,要么都不完成
- 一致性
- 保证从一个正确的状态,迁移到另一个正确的状态。
- 隔离性
- 多个业务操作同一个资源,要保证多个业务是独立的,防止数据损坏
- 持久性
- 事务一旦提交了,无论系统再发生什么问题,结果都不会被影响,被持久化写到存储器中了。
如果不配置事务,可能存在数据提交不一致的情况!
实战准备,先整合spring和mybatis
首先对spring和mybatis的整合参考 Spring学习(九)Spring整合Mybatis
创建一个实体类User
package com.hj.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String name;
private String pwd;
}
然后写UserMapper接口和其实现类以及对应的maper.xml
接口
package com.hj.mapper;
import com.hj.pojo.User;
import java.util.List;
public interface UserMapper {
// 查询所有用户
public List<User> selectUser();
// 添加一个用户
public int addUser(User user);
// 删除一个用户
public int deleteUser(int id);
}
实现类
package com.hj.mapper;
import com.hj.pojo.User;
import org.mybatis.spring.SqlSessionTemplate;
import java.util.List;
public class UserMapperImpl implements UserMapper {
private SqlSessionTemplate sqlSessionTemplate;
public UserMapperImpl(SqlSessionTemplate sqlSessionTemplate) {
this.sqlSessionTemplate = sqlSessionTemplate;
}
public List<User> selectUser() {
return sqlSessionTemplate.getMapper(UserMapper.class).selectUser();
}
public int addUser(User user) {
return sqlSessionTemplate.getMapper(UserMapper.class).addUser(user);
}
public int deleteUser(int id) {
return sqlSessionTemplate.getMapper(UserMapper.class).deleteUser(id);
}
public void transactionTest() {
User user = new User(6, "小李", "232216500");
UserMapper mapper = sqlSessionTemplate.getMapper(UserMapper.class);
mapper.addUser(user);
// 若这句删除出错了,上一句的插入还是会执行,事务不会自动回滚。
mapper.deleteUser(5);
}
}
mapper文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org/DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--核心配置文件-->
<mapper namespace="com.hj.mapper.UserMapper">
<select id="selectUser" resultType="com.hj.pojo.User">
select * from mybatis.user
</select>
<insert id="addUser" parameterType="com.hj.pojo.User">
insert into mybatis.user (id,name,pwd) values (#{id},#{name},#{pwd})
</insert>
<delete id="deleteUser" parameterType="int">
delete from mybatis.user where id=#{id}
</delete>
</mapper>
然后开始写spring的配置文件applicationcontext.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--DataSource:使用spring的数据源替换mybatis的配置 c3p0 dbcp druid
我们这里使用spring提供的jdbc
有了这段后,可以把mybatis-config.xml里的environments标签以及内容都删除
-->
<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<!--sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="datasource"/>
<!--有了这段后,可以把mybatis-config.xml里的mapper标签以及内容都删除-->
<property name="mapperLocations" value="classpath:com/hj/mapper/*.xml"/>
</bean>
<!--这就是mybatis里的sqlSession,有了以后就不需要自己再去new sqlSession对象了-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!--这里只能用构造器注入sqlSessionFatory,因为源码里没有set方法,但有构造方法-->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
<bean id="userMapperImpl" class="com.hj.mapper.UserMapperImpl">
<constructor-arg name="sqlSessionTemplate" ref="sqlSession"/>
</bean>
</beans>
这样spring和mybatis就整合成功了。接下来实现事务功能。
spring中的事务管理
为了演示事务,我们将UserMapper.xml中的delete故意改错
<delete id="deleteUser" parameterType="int">
deletes from mybatis.user where id=#{id}
</delete>
这样我们执行delete语句的时候就肯定会报错,我们就是要看我们delete报错的时候,前面的操作有没有被执行,如果没有被执行说明事务管理成功,否则就失败了。
同时给UserMapper接口多一个方法public void transactionTest() throws Exception;
根据spring-mybatis的文档:事务 可以看到spring中的事务管理分为以下两类:
声明式事务:通过AOP技术实现事务的管理
在spring配置文件中,加入以下配置:不会aop的可以参考 Spring学习(八)Spring AOP
<!--配置声明式事务-->
<!--或者直接 <tx:jta-transaction-manager /> 也可以-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="datasource"/>
</bean>
<!--结合aop实现事务的织入-->
<!--配置事务通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--给哪些方法配置事务-->
<!--配置事务的传播特性 propagation 默认是REQUIRED-->
<tx:attributes>
<tx:method name="transactionTest" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!--配置事务切入-->
<aop:config>
<aop:pointcut id="txPointCut" expression="execution(* com.hj.mapper.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>
测试使用
@Test
public void test2() throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationcontext.xml");
UserMapper userMapperImpl = context.getBean("userMapperImpl", UserMapper.class);
userMapperImpl.transactionTest();
}
可以看到删除出错时,插入也没有执行
编程式事务:需要在代码中进行事务的管理
重新写一个UserMapperImpl2实现类
package com.hj.mapper;
import com.hj.pojo.User;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import java.util.List;
public class UserMapperImpl2 implements UserMapper {
private SqlSessionTemplate sqlSessionTemplate;
private final PlatformTransactionManager transactionManager;//事务
public UserMapperImpl2(SqlSessionTemplate sqlSessionTemplate, PlatformTransactionManager transactionManager) {
this.sqlSessionTemplate = sqlSessionTemplate;
this.transactionManager = transactionManager;
}
public List<User> selectUser() {
return sqlSessionTemplate.getMapper(UserMapper.class).selectUser();
}
public int addUser(User user) {
return sqlSessionTemplate.getMapper(UserMapper.class).addUser(user);
}
public int deleteUser(int id) {
return sqlSessionTemplate.getMapper(UserMapper.class).deleteUser(id);
}
public void transactionTest() throws Exception {
TransactionStatus txStatus =
transactionManager.getTransaction(new DefaultTransactionDefinition());
UserMapper mapper = sqlSessionTemplate.getMapper(UserMapper.class);
try {
User user = new User(7, "李雷", "232216500");
mapper.addUser(user);
mapper.deleteUser(6);
} catch (Exception e) {
transactionManager.rollback(txStatus);//回滚事务
throw e;
}
transactionManager.commit(txStatus);//提交事务
}
}
然后在spring中配置它的bean
<!--编程式配置事务-->
<bean id="userMapperImpl2" class="com.hj.mapper.UserMapperImpl2">
<constructor-arg name="sqlSessionTemplate" ref="sqlSession"/>
<constructor-arg name="transactionManager" ref="transactionManager"/>
</bean>
可以进行测试使用了
@Test
public void test3() throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationcontext.xml");
UserMapper userMapperImpl2 = context.getBean("userMapperImpl2", UserMapper.class);
userMapperImpl2.transactionTest();
}
可以看到删除出错时,插入也没有执行