spring之事务管理简单实例分析
首先实例情况如下所示:
张三 卡里有2000元,李四 卡里有2000元 ,现在张三转1000给李四,
这个过程分为2部分实现:
第一步:张三从卡里转走1000元,
第二步:李四卡里进账1000元,
上面这两个过程,当从张三卡里转出1000元成功,而后出现异常,不能继续往李四卡里转钱,则此时就会出现张三钱少了,而李四卡里钱没多的情况。
解决:
可以使用事务进行解决上面的问题,当出现异常时,进行回顾操作,即从张三卡里转走钱1000元成功,然后出现异常,则此时可以把从张三卡里转钱的动作进行回滚,相当于没有转钱
具体的实现步骤如下所示:(分为配置XML文件和注解的方式进行实现)
1.导入jar包和数据库表的相关配置;
2.实现实体类的编写
3.编写配置文件或者注解
4.编写测试文件进行测试
---------------------------------------------------------------------------------------
1.导入相关的jar包:
数据表中相关表的配置:
2.编写实体类,实体类包括service层和dao层
dao层是对数据库进行操作,在dao层中一般不涉及业务,具体的业务在service层中进行实现
1 package com.gp.dao; 2 3 import org.springframework.jdbc.core.JdbcTemplate; 4 5 public class UserDao { 6 private JdbcTemplate jdbcTemplate; 7 public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { 8 this.jdbcTemplate = jdbcTemplate; 9 } 10 11 public void lessMoney(){ 12 String sql="update acount set salary=salary-? where username=? "; 13 jdbcTemplate.update(sql,1000, "张三"); 14 } 15 public void MoreMoney(){ 16 String sql="update acount set salary=salary+? where username=? "; 17 jdbcTemplate.update(sql,1000, "李四"); 18 } 19 }
业务逻辑层的代码如下所示:
1 package com.gp.service; 2 3 import com.gp.dao.UserDao; 4 5 public class UserService { 6 private UserDao usersDao; 7 public void setOrdersDao(UserDao usersDao) { 8 this.usersDao = usersDao; 9 } 10 public void runMoney(){ 11 usersDao.lessMoney(); 12 /** 13 * 此处有意添加异常信息,用于测试其事务的功能 14 */ 15 int n=10/0; 16 17 usersDao.MoreMoney(); 18 } 19 }
3.编写配置文件:
说明:此处的配置文件约束文件比较全,还有就是配置比较多,
大致流程为;先进行数据源的配置,然后以数据源为参数注入到jdbcTemplate和事务管理器中,实现jdbc模板和事务管理器的创建
把jdbcTemplate注入到UserDao中进行对数据库的操作,再把UserDao注入到UserService中,进行业务逻辑调用封装的数据库操作任务,为了实现对业务逻辑中方法的事务管理,需要根据配置好的事务管理器进行配置事务通知和事务操作,最后在配置切面和切入点,把事务通知横切进目标对象的切入点中,实现对业务逻辑中方法的事务管理。
具体代码如下所示:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" 4 xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" 5 xsi:schemaLocation="http://www.springframework.org/schema/beans 6 http://www.springframework.org/schema/beans/spring-beans.xsd 7 http://www.springframework.org/schema/aop 8 http://www.springframework.org/schema/aop/spring-aop.xsd 9 http://www.springframework.org/schema/tx 10 http://www.springframework.org/schema/tx/spring-tx.xsd 11 http://www.springframework.org/schema/context 12 http://www.springframework.org/schema/context/spring-context.xsd"> 13 <!-- 配置c3p0数据源 --> 14 <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> 15 <property name="driverClass" value="com.mysql.jdbc.Driver" /> 16 <property name="jdbcUrl" value="jdbc:mysql:///mybatis" /> 17 <property name="user" value="root" /> 18 <property name="password" value="guo" /> 19 </bean> 20 <!-- 配置jdbcTemplate --> 21 <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> 22 <property name="dataSource" ref="dataSource" /> 23 </bean> 24 25 <!-- 配置事务管理器 --> 26 <bean id="transactionManager" 27 class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 28 <property name="dataSource" ref="dataSource" /> 29 </bean> 30 <!-- 配置事务通知 --> 31 <tx:advice id="txadvice" transaction-manager="transactionManager"> 32 <!-- 做事务操作 --> 33 <tx:attributes> 34 <!-- 设置进行事务操作的方法匹配规则 --> 35 <tx:method name="runMoney" propagation="REQUIRED" /> 36 </tx:attributes> 37 </tx:advice> 38 <!-- 配置切面及切入点 --> 39 <aop:config> 40 <aop:pointcut expression="execution(* com.gp.service.UserService.runMoney(..))" 41 id="pointcut1" /> 42 <aop:advisor advice-ref="txadvice" pointcut-ref="pointcut1" /> 43 </aop:config> 44 <bean id="userDao" class="com.gp.dao.UserDao"> 45 <property name="jdbcTemplate" ref="jdbcTemplate" /> 46 </bean> 47 <bean id="userService" class="com.gp.service.UserService"> 48 <property name="ordersDao" ref="userDao" /> 49 </bean> 50 </beans>
4.最后编写测试类:
1 package com.gp.test; 2 3 import org.springframework.context.ApplicationContext; 4 import org.springframework.context.support.ClassPathXmlApplicationContext; 5 6 import com.gp.service.UserService; 7 8 public class TestUser { 9 public static void main(String[] args) { 10 ApplicationContext ct = new ClassPathXmlApplicationContext("applicationContext.xml"); 11 UserService userService = (UserService) ct.getBean("userService"); 12 userService.runMoney(); 13 } 14 }
运行结果为:
由此可知,在代码int n=10/0处报错,地下的内容无法继续执行下面,而数据库的结果显示为:
显示数据库的结果却是没变,即事务管理起作用,把张三转钱与李四卡里进钱看做一起执行,否则就进行回滚操作。
====================================================分界线
上面的实例中,配置文件的内容较多,可以使用注解的方式进行修改,只需修改配置文件和业务逻辑处理文件Service层
配置文件中修改后的代码如下所示:‘
<?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" xmlns:context="http://www.springframework.org/schema/context" 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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 配置c3p0数据源 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="com.mysql.jdbc.Driver" /> <property name="jdbcUrl" value="jdbc:mysql:///mybatis" /> <property name="user" value="root" /> <property name="password" value="guo" /> </bean> <!-- 配置jdbcTemplate --> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource" /> </bean> <!-- 配置事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <!-- 第二步 开启事务注释 --> <tx:annotation-driven transaction-manager="transactionManager" /> <bean id="userDao" class="com.gp.dao.UserDao"> <property name="jdbcTemplate" ref="jdbcTemplate" /> </bean> <bean id="userService" class="com.gp.service.UserService"> <property name="ordersDao" ref="userDao" /> </bean> </beans>
UserService文件修改后的内容如下所示:
1 package com.gp.service; 2 3 import org.springframework.transaction.annotation.Transactional; 4 import com.gp.dao.UserDao; 5 6 @Transactional 7 public class UserService { 8 private UserDao usersDao; 9 public void setOrdersDao(UserDao usersDao) { 10 this.usersDao = usersDao; 11 } 12 public void runMoney(){ 13 usersDao.lessMoney(); 14 15 int n=10/0; 16 17 usersDao.MoreMoney(); 18 } 19 }
实现结果同上所示。
补充内容:
事务隔离级别内容(四种)
default:使用后端数据库默认的隔离级别(spring中的选择项)
read_uncommit:允许读还未提交的改变了的数据。可能导致脏读,幻读,不可重复读
read_commit:允许在并发事务已经提交后读取。可防止脏读,但仍然可能出现幻读和不可重复读
repeatable_read:对相同字段多次读取的结果是一致的。可防止脏读,不可重复读,但仍然可能出现幻读
serializable:完全服从ACID的隔离级别。确保不会发生脏读,幻读和不可重复读,在所有隔离级别中是最慢的。
脏读:指一个事务在访问数据,并且对数据进行了修改,且这种数据还没提交到数据库,这个时候另一个事务也访问了这个数据,然后使用
幻读:事务在插入已经检查过不存在的数据时,发现数据已经存在了,这种事幻读
不可重复读:指在一个事务中多次读取同一个数据,由于其他事务的修改,导致读取的结果不一致,这种是不可重复读