第三十 访问财富进退自如 —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 [[email protected]]

再刷新数据库。也不会新插入一条数据。

回滚成功!

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

9、环境配置

本例使用spring 2.5.6。除了找出spring.jar,commons-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);

	}
}

执行效果和上面的样例是一致的。

版权声明:本文博客原创文章。博客,未经同意,不得转载。

时间: 2024-11-08 05:05:22

第三十 访问财富进退自如 —Spring交易管理的相关文章

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

6月16日,晴."宜将剩勇追穷寇,不可沽名学霸王.天若有情天亦老,人间正道是沧桑." 有始有终.有往有还.进退自如乃Spring事务管理之道,也是万物生生不息.和谐共处之道.遵道而行,但到半途需努力:会心不远,欲登绝顶莫辞劳. 事务是一个最小的工作单元,不论成功与否都作为一个整体进行工作. 不会有部分完成的事务.由于事务是由几个任务组成的,因此如果一个事务作为一个整体是成功的,则事务中的每个任务都必须成功.如果事务中有一部分失败,则整个事务失败. 当事务失败时,系统返回到事务开始前的状

精选Spring Boot三十五道必知必会知识点!

Spring Boot.Spring MVC 和 Spring 有什么区别? 1.Spring Spring最重要的特征是依赖注入.所有 SpringModules 不是依赖注入就是 IOC 控制反转. 当我们恰当的使用 DI 或者是 IOC 的时候,我们可以开发松耦合应用.松耦合应用的单元测试可以很容易的进行. 2.Spring MVC Spring MVC 提供了一种分离式的方法来开发 Web 应用.通过运用像 DispatcherServelet,MoudlAndView 和 ViewRe

Python进阶(三十四)-Python3多线程解读

Python进阶(三十四)-Python3多线程解读 线程讲解 ??多线程类似于同时执行多个不同程序,多线程运行有如下优点: 使用线程可以把占据长时间的程序中的任务放到后台去处理. 用户界面可以更加吸引人,这样比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度. 程序的运行速度可能加快. 在一些等待的任务实现上如用户输入.文件读写和网络收发数据等,线程就比较有用了.在这种情况下我们可以释放一些珍贵的资源如内存占用等等. ??线程在执行过程中与进程还是有区别的.每个独立

QT开发(三十八)——Model/View框架编程

QT开发(三十八)--Model/View框架编程 一.自定义模型 1.自定义只读模型 QAbstractItemModel为自定义模型提供了一个足够灵活的接口,能够支持数据源的层次结构,能够对数据进行增删改操作,还能够支持拖放.QT提供了 QAbstarctListModel和QAbstractTableModel两个类来简化非层次数据模型的开发,适合于结合列表和表格使用. 自定义模型需要考虑模型管理的的数据结构适合的视图的显示方式.如果模型的数据仅仅用于列表或表格的显示,那么可以使用QAbs

NeHe OpenGL教程 第三十五课:播放AVI

转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线教程的编写,以及yarn的翻译整理表示感谢. NeHe OpenGL第三十五课:播放AVI 在OpenGL中播放AVI: 在OpenGL中如何播放AVI呢?利用Windows的API把每一帧作为纹理绑定到OpenGL中,虽然很慢,但它的效果不错.你可以试试. 首先我得说我非常喜欢这一章节.Jonat

JAVA之旅(三十四)——自定义服务端,URLConnection,正则表达式特点,匹配,切割,替换,获取,网页爬虫

JAVA之旅(三十四)--自定义服务端,URLConnection,正则表达式特点,匹配,切割,替换,获取,网页爬虫 我们接着来说网络编程,TCP 一.自定义服务端 我们直接写一个服务端,让本机去连接,可以看到什么样的效果 package com.lgl.socket; import java.io.IOException; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; publ

三十项调整助力 Ubuntu 13.04 更上一层楼

在Ubuntu 13.04 Raring Ringtail安装完成之后,我们还有三十项调整需要进行. 1.Ubuntu 13.04 Raring Ringtail安装完毕后,我又进行了一系列工作 大家想知道Ubutnu最新版本带来哪些新内容吗?我认为其中引发讨论最多的话题在于,与前代版本相比(即12.10'Quantal Quetzal')新系统的性能表现并不理想.它不仅延迟明显,而且存在严重的稳定性问题.Raring Ringtail也并不在黄油计划的适用范围之内.但无论如何,Ubuntu 1

马哥学习笔记三十二——计算机及操作系统原理

缓存方式: 直接映射 N路关联 缓存策略: write through:通写 write back:回写 进程类别: 交互式进程(IO密集型) 批处理进程(CPU密集型) 实时进程(Real-time) CPU: 时间片长,优先级低IO:时间片短,优先级高 Linux优先级:priority 实时优先级: 1-99,数字越小,优先级越低 静态优先级:100-139,数据越小,优先级越高 实时优先级比静态优先级高 nice值:调整静态优先级   -20,19:100,139   0:120 ps

第三百三十四节,web爬虫讲解2—Scrapy框架爬虫—Scrapy爬取百度新闻,爬取Ajax动态生成的信息

第三百三十四节,web爬虫讲解2-Scrapy框架爬虫-Scrapy爬取百度新闻,爬取Ajax动态生成的信息 crapy爬取百度新闻,爬取Ajax动态生成的信息,抓取百度新闻首页的新闻标题和rul地址 有多网站,当你浏览器访问时看到的信息,在html源文件里却找不到,由得信息还是滚动条滚动到对应的位置后才显示信息,那么这种一般都是 js 的 Ajax 动态请求生成的信息 我们以百度新闻为列: 1.分析网站 首先我们浏览器打开百度新闻,在网页中间部分找一条新闻信息 然后查看源码,看看在源码里是否有