事务简介
- 把一组业务当成一个业务来做;要么都成功,要么都失败!
- 事务在项目开发中,十分重要,涉及到数据一致性的问题,需要十分注意!
- 确保完整性和一致性!
事务的ACID原则:
- 原子性
- 一致性
- 隔离性
- 多个业务在操作临界资源的时候,需要防止数据损坏。
- 持久性
案例:
先整合spring和mybatis
0、前置用户User类-通过注解帮我们生成有参和无参构造
import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; /** * @Data 帮助生成toString、get和set等方法 * @AllArgsConstructor 有参构造 * @NoArgsConstructor 无参构造 */ @Data @AllArgsConstructor @NoArgsConstructor public class User { private int id; private String name; private String pwd; @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + ", pwd='" + pwd + '\'' + '}'; } }
1、我们定义一个接口类 UserMapper,它抽象了我们对数据库的操作。
public interface UserMapper { public List<User> selectUser(); public int addUser(User user); public int deleteUserById(int id); }
2、配置UserMapper.xml,绑定接口中的方法,并实现SQL的实现
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//OTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.study.mapper.UserMapper"> <select id="selectUser" resultType="user"> select * from mybatis.user; </select> <insert id="addUser" parameterType="user"> insert into mybatis.user(id,name,pwd) values(#{id},#{name},#{pwd}); </insert> <delete id="deleteUserById" parameterType="int"> delete from mybatis.user where id = #{id}; </delete> </mapper>
3、写一个接口的实现类UserMapperImpl,对接口的功能进行实现:
import com.study.pojo.User; import org.mybatis.spring.support.SqlSessionDaoSupport; import java.util.List; public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper{ @Override public List<User> selectUser() { UserMapper mapper = getSqlSession().getMapper(UserMapper.class); return mapper.selectUser(); } @Override public int addUser(User user) { return getSqlSession().getMapper(UserMapper.class).addUser(user); } @Override public int deleteUserById(int id) { return getSqlSession().getMapper(UserMapper.class).deleteUserById(id); } }
4、我们在spring的配置文件applicationContext.xml中注册这个实现类的bean
<?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"> <import resource="spring-dao.xml"/> <bean id="userMapper" class="com.study.mapper.UserMapperImpl"> <property name="sqlSessionFactory" ref="sqlSessionFactory"/> </bean> </beans>
5、测试
import com.study.mapper.UserMapper; import com.study.pojo.User; import com.sun.org.apache.bcel.internal.util.ClassPath; import org.springframework.context.support.ClassPathXmlApplicationContext; import java.util.List; public class MyTest { static ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); static UserMapper mapper = context.getBean("userMapper", UserMapper.class); public static void main(String[] args) { delUser(3); selectAll(); System.out.println("============"); addUser(new User(4,"李大喜","112233")); selectAll(); } public static void selectAll(){ List<User> list = mapper.selectUser(); for (User user : list) { System.out.println(user); } } public static int delUser(int id){ return mapper.deleteUserById(id); } public static int addUser(User user){ return mapper.addUser(user); } }
事务:
比如我们在selectAll()方法中增加一个用户并将新用户删除,而且我们将删除用户的SQL语句故意写错,这样执行的时候我们就是发现,我们的这个selectAll方法并没有体现出事务的特点,我们说事务应该是统一完成或者统一失败的,但是我们的这个方法结果却是添加了新用户但是并没有删除成功(因为我们的删除SQL是错误的),所以,我们要想一种办法来实现事务:
spring中事务的实现
spring 中事务的实现有两种方式:
- 声明式事务(通过AOP实现,不需要修改原业务代码)
- 编程式事务(通过try-catch,需要修改原业务代码)
我们更多的不去直接修改原本的业务代码,而是使用声明式事务
事务的实现
一个使用 MyBatis-Spring 的其中一个主要原因是它允许 MyBatis 参与到 Spring 的事务管理中。而不是给 MyBatis 创建一个新的专用事务管理器,MyBatis-Spring 借助了 Spring DataSourceTransactionManager 来实现事务管理。
一旦配置好了 Spring 的事务管理器,你就可以在 Spring 中按你平时的方式来配置事务。并且支持 @Transactional 注解和 AOP 风格的配置。在事务处理期间,一个单独的 SqlSession 对象将会被创建和使用。当事务完成时,这个 session 会以合适的方式提交或回滚。
事务配置好了以后,MyBatis-Spring 将会透明地管理事务。这样在你的 DAO 类中就不需要额外的代码了。
标准配置
要开启 Spring 的事务处理功能,在 Spring 的配置文件中创建一个 DataSourceTransactionManager 对象,也就是注册一个bean。
在我们的spring配置文件(可以在applicationContext.xml中区配置,也可以在我们数据源bean-dataSource所在的配置文件中配置,这里我们直接在spring-dao.xml中操作,因为我们的数据源bean在这个配置文件中,我们将所有与数据库操作相关的代码都放到这个配置文件中去便于后期查找和维护),因此这样我们中注册这样一个bean:
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <constructor-arg ref="dataSource" /> </bean>
我们可以看到,其中需要指定引用-也就是我的数据源(数据库),这里我们是通过构造器注入的事务管理器对象,我们也可以通过属性来注入:
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean>
交由容器管理事务
注册完成事务管理器后,我们需要交由容器来管理事务:
我们在spring含有sqlSessionFactory的配置文件中添加以下配置代码:
1、配置事务通知advice
1.1、导入 tx 约束文件
<?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:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
1.2、设置事务属性
<tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="selectUser" propagation="REQUIRED"/> <tx:method name="*" propagation="REQUIRED"/> </tx:attributes> </tx:advice>
2、配置事务切入
2.1、导入切入约束
<?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:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" 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/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
2.2、设置切入点
我们设置切入点的作用范围为com.study.mapper包下所有类的所有方法。
<tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="selectUser" propagation="REQUIRED"/> <tx:method name="*" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <!--对应tx标签内的id--> <aop:config> <aop:pointcut id="txPointCut" expression="execution(* com.study.mapper.*.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/> </aop:config>
测试
我们继续测试selectUser方法,我们故意将其中嵌套的delUser方法的SQL雨具写错,观察先执行的addUser方法是否执行,因为它是在delUser方法执行前的,他们是一整个事务,如果配置成功应该是一起成功或失败:
我们发现,配置事务之后,我们的selectUser方法内部的方法也都是一起成功或失败的,事务功能实现!
总结
主要就配置三部分:
- 配置事务管理器
- 配置事务通知
- 配置切入点
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <constructor-arg ref="dataSource" /> <property name="dataSource" ref="dataSource"/> </bean> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="selectUser" propagation="REQUIRED"/> <tx:method name="*" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id="txPointCut" expression="execution(* com.study.mapper.*.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/> </aop:config>
事务在项目中十分重要!