(六)Spring-事务式声明
(六)Spring-事务式声明
一、回顾事务
1.1 事务特性:
- ACID
- 把一组业务当成一个业务来做,要么都成功,要么都失败!
- 事务在项目开发中,十分的重要,涉及到数据的一致性问题。不能马虎!
- 确保完整性和一致性!
1.2 事务ACID原则:
- 原子性 A
- 一致性 C
- 隔离性 I
- 多个业务可能操作同一个资源,如一条记录,防止数据损坏即确保完整性和一致性。
- 持久性 D
二、spring中的事务管理
2.1 声明式事务:AOP
1 编写service层业务逻辑,包含事务
-
service层实现,通过容器注入属性mapper
-
在业务逻辑中使用mapper即dao层
-
编写事务,
doTx()方法先添加后修改user,中间制造错误
,如下:
package com.happy.service.user.impl; import com.happy.mapper.user.UserMapper; import com.happy.pojo.user.User; import com.happy.service.user.UserService; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.stereotype.Service; import java.util.List; @Service("userService") public class UserServiceImpl implements UserService { @Autowired @Qualifier("userMapper") UserMapper userMapper; @Override public List<User> getUserList() { List<User> userList = userMapper.getUserList(); return userList; } @Override public User getUserById(int id) { User user = userMapper.getUserById(id); return user; } @Override public int addUser(User user) { int result = userMapper.addUser(user); return result; } @Override public int deleteUser(User user) { int result = userMapper.deleteUser(user); return result; } @Override public int updateUser(User user) { int result = userMapper.updateUser(user); return result; } @Override public void doTx(){ List<User> userList = userMapper.getUserList(); System.out.println(userList); System.out.println("==========执行一组业务,先增后删=============="); User user = new User(521, "gao518", "1987518"); /*1 增加*/ System.out.println("1.增加用户"); userMapper.addUser(user); /*制造报错*/ int error=1/0; /*2 删除*/ User user2 = new User(520, "gao518518", null); System.out.println("2.删除用户"); userMapper.updateUser(user2); userList = userMapper.getUserList(); System.out.println(userList); } }
2 编写mapper接口和mapper.xml文件
package com.happy.mapper.user; import com.happy.pojo.user.User; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; import java.util.List; public interface UserMapper { @Select("select * from user") List<User> getUserList(); User getUserById(@Param("id") int id); /*增*/ int addUser(User user); /*删*/ int deleteUser(User user); /*改*/ int updateUser(User user); }
<?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.happy.mapper.user.UserMapper"> <!-- <select id="getUserList" resultType="user">--> <!-- select * from user;--> <!-- </select>--> <select id="getUserById" resultType="user"> select * from user where id = #{id}; </select> <insert id="addUser" parameterType="user"> insert into user(`id`, `name`, `pwd`) values (#{id}, #{name}, #{pwd}) </insert> <delete id="deleteUser" parameterType="user"> <if test="id!=null"> delete from user where id=#{id}; </if> </delete> <update id="updateUser" parameterType="user"> <if test="id!=null"> update user <set> <if test="name!=null"> name=#{name}, </if> <if test="pwd!=null"> pwd=#{pwd}, </if> </set> where id=#{id}; </if> </update> </mapper>
3 开启Spring的事务处理功能
在相关的spring xml中如spring-mybatis.xml,要开启 Spring 的事务处理功能,在 Spring 的配置文件中创建一个 DataSourceTransactionManager
对象:
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <constructor-arg ref="dataSource" /> </bean>
4 配置切面(通知)和切点
-
配置切面
- 结合aop实现事务的织入
- 配置事务的类即:定义事务切面和通知即事务增强方法
经过实测,方法名必须和严格一致或使用通配符,如method name="do*" propagation="REQUIRED"
- 配置事务的传播特性
-
配置切点
- 即配置通知的作用点
- 作用点一般定义在service层,请注意
- service层,作用在接口和实现类均可。即可以是com.happy.service.user.UserService.,也可以是com.happy.service.user.impl.UserServiceImpl.
<?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" xmlns:aop="http://www.springframework.org/schema/aop" 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/context https://www.springframework.org/schema/context/spring-context.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"> <context:property-placeholder location="db.properties"/> <!--注册bean--> <!--1.注册一个datasource,使用spring的datasource替换mybatis的配置,c3p0,dbcp,druid--> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="${jdbc.driver}"></property> <property name="url" value="${jdbc.url}"></property> <property name="username" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> </bean> <!--2.注册一个sqlSessionFactory 用于生成sqlSession--> <!-- String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="configLocation" value="classpath:mybatis-config.xml"></property> <!--<property name="mapperLocations" value="classpath:com/happy/mapper/user/UserMapper.xml"></property>--> </bean> <!--3.注册一个MapperFactoryBean 用于获取mapper--> <!--需要关联UserMapper 相当于原来的sqlSession.getMapper(UserMapper.class)--> <bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean"> <property name="mapperInterface" value="com.happy.mapper.user.UserMapper" /> <property name="sqlSessionFactory" ref="sqlSessionFactory" /> </bean> <!--spring开启事务--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> <!-- <constructor-arg name="dataSource" ref="dataSource" />--> </bean> <!--结合aop实现事务的织入--> <!--1.配置事务的类即:定义事务切面和通知即事务增强方法--> <!--2.配置事务的传播特性--> <tx:advice id="transactionInterceptor" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="addUser" propagation="REQUIRED"/> <tx:method name="deleteUser" propagation="REQUIRED"/> <tx:method name="updateUser" propagation="REQUIRED"/> <tx:method name="do*" propagation="REQUIRED"/> <!--get方法即查询不需要事务--> <tx:method name="getUserList" read-only="true"/> <tx:method name="getUserById" read-only="true"/> <tx:method name="*" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <!--定义aop事务管理的切点--> <aop:config> <aop:pointcut id="txPointCut" expression="execution(* com.happy.service.*.*.*(..))"/> <aop:advisor advice-ref="transactionInterceptor" pointcut-ref="txPointCut"></aop:advisor> </aop:config> <!--<tx:jta-transaction-manager />--> </beans>
5 模拟controller调用service层方法,测试事务
- 注意:
必须从容器获取service层实例context.getBean("userService", UserService.class)
;,不能自己用构造器new
package com.happy.service.user; import com.happy.pojo.user.User; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import java.util.List; public class UserServiceTest { @Test public void testGetUserList() { ApplicationContext context = new ClassPathXmlApplicationContext("application-context.xml"); UserService userService = context.getBean("userService", UserService.class); List<User> userList = userService.getUserList(); for (User user : userList) { System.out.println(user); } } @Test public void testGetUserById() { ApplicationContext context = new ClassPathXmlApplicationContext("application-context.xml"); UserService userService = context.getBean("userService", UserService.class); User user = userService.getUserById(3); System.out.println(user); } @Test public void testTransaction() { ApplicationContext context = new ClassPathXmlApplicationContext("application-context.xml"); UserService userService = context.getBean("userService", UserService.class); //注意:必须是被容器管理的对象,执行相关方法,才能被AOP拦截。 userService.doTx(); } }
2.2 编程式事务:需要在代码中,进行事务的管理。
public class UserService { private final PlatformTransactionManager transactionManager; public UserService(PlatformTransactionManager transactionManager) { this.transactionManager = transactionManager; } public void createUser() { TransactionStatus txStatus = transactionManager.getTransaction(new DefaultTransactionDefinition()); try { userMapper.insertUser(user); } catch (Exception e) { transactionManager.rollback(txStatus); throw e; } transactionManager.commit(txStatus); } }
-
需要在原有业务逻辑上,通过手动代码实现事务,即try-catch+rollback
-
要改变代码,现在一般不用了
2.3 小结
思考:
为什么需要事务
- 如果不配置事务,可能存在数据提交不一致的情况。如银行转账绝对不允许。
- 如果我们不在spring中去配置声明式事务,我们就需要在代码中手动配置事务
- 事务在项目的开发中是否重要,涉及到数据的一致性和完整性问题,不容马虎。
- 工作的时候,都建议配置上声明式事务,都是死的。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】