1.Spring的事务处理
1.1什么是事务
保证业务操作完整性的一种数据库机制:在一个业务当中涉及到的多步操作,要么一起成功,要么一起失败,而且不能产生相互的影响;事务这个机制是由数据库来保证的,我们通过Java代码仅仅完成的是对这种机制的调用。
事物的4个特点:
A:原子性
C:一致性
I:隔离性
D:持久性
1.2如何控制事务
不同的持久层框架,有不同的事务控制机制。
1.JDBC:依赖于Connection对象控制事务
- 开启事务:Connection.setAutoCommit(false);
- 提交事务:Connection.commit();
- 回滚事务:Connection.rollback();
2.Mybatis:SqlSession底层也封装了Connection对象
- Mybatis自动地开启事务
- sqlSession(Connection).commiy();
- sqlSession(Connection).rollback();
3.结论:无论使用的什么持久化框架,控制事务的底层都是Connection对象来完成的。
1.3.Spring控制事务的开发
1.Spring通过AOP的方式进行事务的开发:事务不是核心的业务功能,可以看做额外的功能,那么最好的方式就是通过AOP的方式进行开发。
2.步骤
-
1.导入Spring的事务控制依赖
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.1.14.RELEASE</version> </dependency>
-
2.原始对象
public class UserServiceImpl implements UserService { private UserDao userDao; public UserDao getUserDao() { return userDao; } public void setUserDao(UserDao userDao) { this.userDao = userDao; } @Override public void save(User user) { } }
-
3.原始对象的bean配置和整合mybatis配置
<!--1.配置数据源--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="username" value="root"/> <property name="password" value="root"/> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/test_db?useSSL=false"/> </bean> <!--2.配置创建SqlSessionFactory的SqlSessionFactoryBean--> <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="typeAliasesPackage" value="com.txl.model"/> <property name="mapperLocations"> <list> <value>classpath*:mappers/*.xml</value> </list> </property> </bean> <!--3.MapperScannerConfigure: 创建Dao接口的实现对象:id是Dao接口首字母小写 SqlSession:通过sqlSessionFactoryBean得到SqlSession--> <bean id="mapperScannerConfigure" class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean"/> <property name="basePackage" value="com.txl.dao"/> </bean> <!--创建Service对象,并注入dao属性--> <bean id="userService" class="com.txl.service.UserServiceImpl"> <property name="userDao" ref="userDao"></property> </bean>
-
4.额外功能(事务)的开发
额外功能不需要我们再写代码了,Spring帮我们封装好了一个叫做DataSourceTransactionManager
的类,我们直接对其进行配置即可。配置时要为其注入连接池。<!--配置事务控制类--> <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean>
-
5.切入点:通过注解
@Transactional
来指定,添加的@Transactional
的切入点会被Spring扫描到作为切入点。
加载类上:类中的所有方法都被事务控制。
加载方法:指定的方法被事务控制。@Transactional public class UserServiceImpl implements UserService { private UserDao userDao; public UserDao getUserDao() { return userDao; } public void setUserDao(UserDao userDao) { this.userDao = userDao; } @Override public void save(User user) { } }
-
6.组装切面
<!--组装切面--> <tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
-
7.注意:
这一步配置好:意味着Spring会扫描所有的加上
@Transactional
注解的地方作为切入点,然后通过AOP(动态代理)
将额外功能(事务:dataSourceTransactionManager)加到上面作为切面。
所以我们在那拿入点对象时(Service),拿到的是代理对象。
-
8.完整的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" 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 https://www.springframework.org/schema/tx/spring-tx.xsd"> <!--1.配置数据源--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="username" value="root"/> <property name="password" value="root"/> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/test_db?useSSL=false"/> </bean> <!--2.配置创建SqlSessionFactory的SqlSessionFactoryBean--> <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="typeAliasesPackage" value="com.txl.model"/> <property name="mapperLocations"> <list> <value>classpath*:mappers/*.xml</value> </list> </property> </bean> <!--3.MapperScannerConfigure: 创建Dao接口的实现对象:id是Dao接口首字母小写 SqlSession:通过sqlSessionFactoryBean得到SqlSession--> <bean id="mapperScannerConfigure" class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean"/> <property name="basePackage" value="com.txl.dao"/> </bean> <!--创建Service对象,并注入dao属性--> <bean id="userService" class="com.txl.service.UserServiceImpl"> <property name="userDao" ref="userDao"></property> </bean> <!--配置事务控制类--> <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!--组装切面--> <tx:annotation-driven transaction-manager="dataSourceTransactionManager"/> </beans>
-
9.测试
结果:没有插入成功,事务回滚
1.4.Spring控制事务的细节分析
<!--组装切面:为切入点生成代理对象,默认是JDK动态代理方式-->
<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
这一步配置好:意味着Spring会扫描所有的加上@Transactional
注解的地方作为切入点,然后通过AOP(动态代理)
将额外功能(事务:dataSourceTransactionManager)加到上面作为切面(生成代理对象)。
所以我们在那拿入点对象时(Service),拿到的是代理对象。
这个标签还有一个属性:进行动态代理底层的切换,指定生成代理对象的方式。
<tx:annotation-driven transaction-manager="dataSourceTransactionManager" proxy-target-class="false"/>
proxy-target-class="false":默认false--->JDK
proxy-target-class="true":true---->CGlib