第三十天 出入有道进退自如 —Spring的事务管理

          6月16日,晴。“宜将剩勇追穷寇,不可沽名学霸王。天若有情天亦老,人间正道是沧桑。”

       有始有终、有往有还、进退自如乃Spring事务管理之道,也是万物生生不息、和谐共处之道。遵道而行,但到半途需努力;会心不远,欲登绝顶莫辞劳。            

       事务是一个最小的工作单元,不论成功与否都作为一个整体进行工作。
       不会有部分完成的事务。由于事务是由几个任务组成的,因此如果一个事务作为一个整体是成功的,则事务中的每个任务都必须成功。如果事务中有一部分失败,则整个事务失败。
       当事务失败时,系统返回到事务开始前的状态。这个取消所有变化的过程称为“回滚”( rollback )。例如,如果一个事务成功更新了两个表,在更新第三个表时失败,则系统将两次更新恢复原状,并返回到原始的状态。
             数据库的更新通常都是由客观世界的所发生的事件引起的。为保证数据库内容的一致,就要将数据库的一组操作作为一个整体来进行,要么全部成功完成,要么全部失败退出。如果由于故障或其它原因而使一组操作中有一些完成,有一些未完成,则必然会使得数据库中的数据出现不一致,从而使得数据库的完整性受到破坏。因此,更新操作序列必须作为一个整体在DBMS执行时出现,即“要么全做,要么全不做”。SQL提供了事务处理的机制,来帮助DBMS实现上述的功能。

          一、下面看一个单纯的spring的声明式(XML 式)事务管理的例子

     1、创建MySQL数据库

               见 第十九天 慵懒的投射在JDBC上的暖阳 —Hibernate的使用(一)

           因为要演示数据回滚,需要把MySQL默认的ENGINE = MyISAM改为ENGINE =InnoDB,目的是让数据库支持回滚。

           如何修改,最简单的做法,就是使用Navicat这个工具,设计表—>选项—>表类型

          

       2、创建DAO接口

package edu.eurasia.dao;

public interface TmDao {
	public void insert();
	public void select();
}

      3、创建DAO的实现类

package edu.eurasia.dao;

import java.util.List;

import org.springframework.jdbc.core.ColumnMapRowMapper;
import org.springframework.jdbc.core.support.JdbcDaoSupport;

public class TmDaoImpl extends JdbcDaoSupport implements TmDao {
	public void insert() {
		try {
			super.getJdbcTemplate().update(
					"update userinfo set username = '唐伯虎' where id =2");
			super.getJdbcTemplate()
					.update("INSERT INTO userinfo(username,password) VALUES('李苦禅','88888888')");
			super.getJdbcTemplate().update("DELETE FROM userinfo where id =2");

		} catch (Exception e) {
			logger.error("小心地雷!" + e.getMessage());
			e.printStackTrace();
			throw new RuntimeException();
			// 如果注释掉throw new RuntimeException,那么事务将不能回滚,因为spring捕捉不到Exception
		}
	}

	public void select() {
		List list = super.getJdbcTemplate().query("select * from userinfo",
				new ColumnMapRowMapper());

		System.out.println(list);

	}
}
        4、创建service接口

package edu.eurasia.service;

public interface TmService {
	public void insert();
	public void select();
}
      5、实现service接口
package edu.eurasia.service;

import edu.eurasia.dao.TmDao;

public class TmServiceImpl implements TmService {
	private TmDao tmDao;

	public TmDao getTmDao() {
		return tmDao;
	}

	public void setTmDao(TmDao tmDao) {
		this.tmDao = tmDao;
	}

	@Override
	public void insert() {
	    tmDao.insert();		
	}

	@Override
	public void select() {
	    tmDao.select();
	}
}

      6编写spring的配置文件applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:aop="http://www.springframework.org/schema/aop" 
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:jee="http://www.springframework.org/schema/jee" 
	xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/aop 
	http://www.springframework.org/schema/aop/spring-aop-3.0.xsd   
	http://www.springframework.org/schema/beans 
	http://www.springframework.org/schema/beans/spring-beans-3.0.xsd   
	http://www.springframework.org/schema/context 
	http://www.springframework.org/schema/context/spring-context-3.0.xsd   
	http://www.springframework.org/schema/jee 
	http://www.springframework.org/schema/jee/spring-jee-3.0.xsd   
	http://www.springframework.org/schema/tx 
	http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
    
    <!-- 数据源  配置-->
	<bean id="dataSource"
		class="org.springframework.jdbc.datasource.DriverManagerDataSource"
		destroy-method="close">
		<property name="driverClassName" value="com.mysql.jdbc.Driver" />
		<property name="url"
			value="jdbc:mysql://localhost:3306/hib?useUnicode=true&characterEncoding=UTF-8" />
		<property name="username" value="root" />
		<property name="password" value="root" />		
	</bean>
	
	<!-- 事务管理器  注入dataSource -->
	<bean id="transactionManager"
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource" />
	</bean>
	
	<!-- spring提供的操作数据库用的句柄,类似JDBC的Statement类 ,bean可以不写-->
	<!-- <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSource" />
	</bean> -->
	
	<!-- 本例中继承了JdbcDaoSupport的父类,它里面有"jdbcTemplate"和"dataSource"的set接口,只需注入任意一个就行了 -->
	<bean id="tmDao" class="edu.eurasia.dao.TmDaoImpl">
		<!-- <property name="jdbcTemplate" ref="jdbcTemplate" /> -->
		<property name="dataSource" ref="dataSource" />
	</bean>
	       <!--   Service配置   -->
	<bean id="tmService" class="edu.eurasia.service.TmServiceImpl">
	      <property name="tmDao" ref="tmDao"></property> 
	</bean>
	
	<!-- 通知器: name="*"是表示切入目标类的所有方法,即TmDao类的所有方法,REQUIRED的意思是在同个方法类的所有sql操作在同一个进程中,这样才能实现回滚操作,dao类里抛出的Exception,所以要求的是凡是发生Exception就回滚-->
	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<tx:attributes>
			<tx:method name="*" propagation="REQUIRED" rollback-for="java.lang.Exception" />
		</tx:attributes>
	</tx:advice>
	
	<!-- 切面配置:第一个*表示返回类型,edu.eurasia.dao.表示edu.eurasia.dao包及其它的子包的类,第二个*表示类中的所有方法名,(..)表示任意类型,任意个数的参数 -->
	<aop:config>
		<aop:pointcut id="pointcut" expression="execution(* edu.eurasia.dao.*.*(..))" />
		<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut" />
	</aop:config>
</beans>
         7、编写测试类 TestTm.java

package edu.eurasia.test;

import org.apache.log4j.Logger;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import edu.eurasia.service.TmService;

public class TestTm {
	static Logger logger = Logger.getLogger(TestTm.class);

	@Test
	public void tsettm() {
		ApplicationContext context = new ClassPathXmlApplicationContext(
				"applicationContext.xml");
		TmService tmservice = (TmService) context.getBean("tmService");
		tmservice.insert();
		tmservice.select();
	}
}

         8、运行结果分析

         Spring的事务管理默认只对出现运行期异常(java.lang.RuntimeException及其子类)进行回滚。 如果一个方法抛出Exception或者Checked异常,Spring事务管理默认不进行回滚。

             所以,DAO的实现类TmDaoImpl.java中要catch RuntimeException()异常只抛出普通exception的话,不会回滚。有的童鞋按上面代码进行测试,发现没有回滚,数据库正常插入一条数据。尴尬原因是程序没有异常,需要人为制造一个,比如修改SQL语句"DELETE FROM userinfo where id1 =2" ,把字段id 改成id1,再试一试,观察控制台你会发现:com.mysql.jdbc.exceptions.MySQLSyntaxErrorException异常抛出。还有如下内容:

log4j.properties的编写见第二十八天 月出惊山鸟 —Spring的AOP

DEBUG [main] (AbstractPlatformTransactionManager.java:821) - Initiating transaction rollback
DEBUG [main] (DataSourceTransactionManager.java:273) - Rolling back JDBC transaction on Connection [com.mysql.jdbc.Connection@9283b0]

再刷新数据库,也不会新插入一条数据。回滚成功!偷笑

        注意:像删除或更新一条不存在的记录,类似这些操作,程序是不报异常的,自然不会回滚。可以根据需要自定义异常,让自定义的异常继承自RuntimeException,这样抛出的时候才会被Spring默认的事务处理准确处理。

        9、环境配置

        本例使用spring 2.5.6,除了找出spring.jarcommons-logging-1.1.1.jar两个jar包,外加一个log4j.jar还要下载aspectjweaver.jar。别忘了mysql-connector-java-5.0.8-bin.jar。  

        项目结构如图:


                 二、用注解实现事务管理

            使用注解来实现声明式事务这个方法简洁方便。把上面的例子稍加改动即可。

                   1、修改配置文件applicationContext.xml,将所有具有@Transactional 注解的bean自动配置为声明式事务支持。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:aop="http://www.springframework.org/schema/aop" 
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:jee="http://www.springframework.org/schema/jee" 
	xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/aop 
	http://www.springframework.org/schema/aop/spring-aop-3.0.xsd   
	http://www.springframework.org/schema/beans 
	http://www.springframework.org/schema/beans/spring-beans-3.0.xsd   
	http://www.springframework.org/schema/context 
	http://www.springframework.org/schema/context/spring-context-3.0.xsd   
	http://www.springframework.org/schema/jee 
	http://www.springframework.org/schema/jee/spring-jee-3.0.xsd   
	http://www.springframework.org/schema/tx 
	http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
    
    <!-- 数据源  配置-->
	<bean id="dataSource"
		class="org.springframework.jdbc.datasource.DriverManagerDataSource"
		destroy-method="close">
		<property name="driverClassName" value="com.mysql.jdbc.Driver" />
		<property name="url"
			value="jdbc:mysql://localhost:3306/hib?useUnicode=true&characterEncoding=UTF-8" />
		<property name="username" value="root" />
		<property name="password" value="root" />		
	</bean>
	
	<!-- 事务管理器  注入dataSource -->
	<bean id="transactionManager"
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource" />		
	</bean>
	
	<!-- spring提供的操作数据库用的句柄,类似JDBC的Statement类 ,bean可以不写-->
	<!-- <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSource" />
	</bean> -->
	
	<!-- 本例中继承了JdbcDaoSupport的父类,它里面有"jdbcTemplate"和"dataSource"的set接口,只需注入任意一个就行了 -->
	<bean id="tmDao" class="edu.eurasia.dao.TmDaoImpl">
		<!-- <property name="jdbcTemplate" ref="jdbcTemplate" /> -->
		<property name="dataSource" ref="dataSource" />
	</bean>
	
	<!--   Service配置   -->
	<bean id="tmService" class="edu.eurasia.service.TmServiceImpl">
	      <property name="tmDao" ref="tmDao"></property> 
	</bean>
	
	 <!-- 用 注解来实现事务管理 -->  
    <tx:annotation-driven transaction-manager="transactionManager" />  
</beans>
          2、在接口或类的声明处 ,写一个@Transactional。要是只的接口上写, 接口的实现类就会继承下来。

接口的实现类的具体方法,还可以覆盖类声明处的设置。

          修改DAO的实现类,加上注解即可。

package edu.eurasia.dao;

import java.util.List;

import org.springframework.jdbc.core.ColumnMapRowMapper;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Transactional   
public class TmDaoImpl extends JdbcDaoSupport implements TmDao {
	@Transactional(propagation=Propagation.REQUIRED,rollbackFor={Exception.class}) 
	public void insert() {
		try {
			super.getJdbcTemplate().update(
					"update userinfo set username = '唐伯虎' where id =3");
			super.getJdbcTemplate()
					.update("INSERT INTO userinfo(username,password) VALUES('李苦禅','88888888')");
			super.getJdbcTemplate().update("DELETE FROM userinfo where id =4");

		} catch (Exception e) {
			logger.error("小心地雷!" + e.getMessage());
			e.printStackTrace();
			throw new RuntimeException();
			// 如果注释掉throw new RuntimeException,那么事务将不能回滚,因为spring捕捉不到Exception
		}
	}

	public void select() {
		List list = super.getJdbcTemplate().query("select * from userinfo",
				new ColumnMapRowMapper());

		System.out.println(list);

	}
}
         运行效果和上面的例子是一致的。




    



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
非常感谢您的提问!作为一个AI语言模型,我并不具备实际的测试能力,但我可以向您介绍一些关于网易有道词典软件测试缺陷管理的一般性知识。 在软件测试中,缺陷管理是非常重要的一个环节,它涉及到缺陷的发现、记录、跟踪、修复和验证等多个方面。对于有道词典这样的大型软件产品来说,缺陷管理更是至关重要。 通常情况下,软件测试缺陷管理包括以下几个步骤: 1. 缺陷的发现:测试人员通过测试用例、测试脚本、手动测试等方式,发现软件中的缺陷。 2. 缺陷的记录:测试人员将发现的缺陷记录在缺陷管理系统中,包括缺陷的描述、重现步骤、截图等信息。 3. 缺陷的跟踪:测试人员跟踪缺陷的处理过程,包括缺陷被什么人员处理、缺陷的状态、缺陷的优先级等信息。 4. 缺陷的修复:开发人员根据缺陷的描述和重现步骤,修复软件中的缺陷。 5. 缺陷的验证:测试人员验证修复后的缺陷是否已经被解决。 在网易有道词典软件测试缺陷管理方面,具体的实现可能会有所不同,但一般需要建立一个完善的缺陷管理系统,包括缺陷的分类、优先级、状态等信息,并且需要测试人员、开发人员、项目经理等多个角色之间的协作。同时,为了提高缺陷管理的效率和质量,还可以采用自动化测试、持续集成等技术手段来辅助缺陷管理工作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值