编程式事务使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。对于编程式事务管理,spring推荐使用TransactionTemplate。
声明式事务是建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过基于@Transactional注解的方式),便可以将事务规则应用到业务逻辑中。
显然声明式事务管理要优于编程式事务管理,这正是spring倡导的非侵入式的开发方式。声明式事务管理使业务代码不受污染,一个普通的POJO对象,只要加上注解就可以获得完全的事务支持。和编程式事务相比,声明式事务唯一不足地方是,它的最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。但是即便有这样的需求,也存在很多变通的方法,比如,可以将需要进行事务管理的代码块独立为方法等等。
声明式事务管理也有两种常用的方式,一种是基于tx和aop名字空间的xml配置文件,另一种就是基于@Transactional注解。显然基于注解的方式更简单易用,更清爽
5.7 声明式事务
ActiveRecord支持声明式事务,声明式事务需要使用ActiveRecordPlugin提供的拦截器来实现,拦截器的配置方法见Interceptor有关章节。以下代码是声明式事务示例:
// 本例仅为示例, 并未严格考虑账户状态等业务逻辑@Before(Tx.class)
public void trans_demo() {
// 获取转账金额
Integer transAmount = getParaToInt("transAmount")
// 获取转出账户id
Integer fromAccountId = getParaToInt("fromAccountId")
// 获取转入账户id
Integer toAccountId = getParaToInt("toAccountId")
// 转出 *** 作
Db.update("update account set cash = cash - ? where id = ?",
transAmount, fromAccountId)
// 转入 *** 作
Db.update("update account set cash = cash + ? where id = ?",
transAmount, toAccountId)
}
以上代码中,仅声明了一个Tx拦截器即为action添加了事务支持。除此之外ActiveRecord还配备了TxByActionKeys、TxByActionKeyRegex、TxByMethods、TxByMethodRegex,分别支持actionKeys、actionKey正则、actionMethods、actionMethod正则声明式事务,以下是示例代码:
public void configInterceptor(Interceptors me) {me.add(new TxByMethodRegex("(.*save.*|.*update.*)"))
me.add(new TxByMethods("save", "update"))
me.add(new TxByActionKeyRegex("/trans.*"))
me.add(new TxByActionKeys("/tx/save", "/tx/update"))
}
上例中的TxByRegex拦截器可通过传入正则表达式对action进行拦截,当actionKey被正则匹配上将开启事务。TxByActionKeys可以对指定的actionKey进行拦截并开启事务,TxByMethods可以对指定的method进行拦截并开启事务。
注意:MySql数据库表必须设置为InnoDB引擎时才支持事务,MyISAM并不支持事务。
网页链接
我提供一个实例:数据库是MYSQL , 数据源是C3P0 ,
-----------
1) 建表语句 :
----------------
DROP TABLE IF EXISTS book
CREATE TABLE book(
id int(11) NOT NULL auto_increment COMMENT 'primary key',
name varchar(60) default NULL COMMENT 'book name',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='my book table'
----------------
注意了,在MYSql 中 表的引擎一定要用InnoDB ,如果用MyISAM 的话,是不支持事务的。
2) JAVA Bean
Book.java
-------------------
package com.spring.demo.bean
import java.io.Serializable
public class Book implements Serializable {
public Book(){}
public Book(String name){
this.name = name
}
/**
* ID
*/
private Integer id
/**
* 书名
*/
private String name
// getter and setter
public Integer getId() {
return id
}
public void setId(Integer id) {
this.id = id
}
public String getName() {
return name
}
public void setName(String name) {
this.name = name
}
}
-------------------
3)Book.xml 配置
---------------------
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"
"http://ibatis.apache.org/dtd/sql-map-2.dtd">
<sqlMap namespace="Book" >
<typeAlias alias="book" type="com.spring.demo.bean.Book"/>
<resultMap id="result_map_book" class="book" >
<result property="id" column="id" javaType="integer"/>
<result property="name" column="name" javaType="string"/>
</resultMap>
<insert id="insertBook" parameterClass="book">
insert book(name) values(#name#)
</insert>
<update id="updateBook" parameterClass="book">
update book set name=#name# where id=#id#
</update>
<delete id="deleteBook" parameterClass="int">
delete from book where id=#value#
</delete>
<select id="selectBookById" parameterClass="int" resultMap="result_map_book">
select id , name from book where id=#value#
</select>
<select id="selectBookByName" parameterClass="string" resultMap="result_map_book">
select id , name from book where name like #value#
</select>
</sqlMap>
---------------------
4)spring 的 applicationContext.xml
---------------
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans>
<!-- 引入参数配置文件 -->
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>jdbc.properties</value>
</list>
</property>
</bean>
<!-- 配置数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass"><value>${jdbc.driverClass}</value></property>
<property name="jdbcUrl"><value>${jdbc.url}</value></property>
<property name="user"><value>${jdbc.user}</value></property>
<property name="password"><value>${jdbc.password}</value></property>
<property name="initialPoolSize"><value>${jdbc.initialPoolSize}</value></property>
<property name="minPoolSize"><value>${jdbc.minPoolSize}</value></property>
<property name="maxPoolSize"><value>${jdbc.maxPoolSize}</value></property>
</bean>
<!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource">
<ref local="dataSource"/>
</property>
</bean>
<!-- 事务代理拦截器的配置 -->
<bean id="baseTransactionProxy" abstract="true" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager">
<ref bean="transactionManager" />
</property>
<property name="transactionAttributes">
<props>
<prop key="insert*">PROPAGATION_REQUIRED</prop>
<prop key="update*">PROPAGATION_REQUIRED</prop>
<prop key="delete*">PROPAGATION_REQUIRED</prop>
<prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
</bean>
<!-- ibatis sqlMapClient 配置 -->
<bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
<property name="configLocation">
<value>classpath:sqlMapConfig.xml</value>
</property>
<property name="dataSource">
<ref bean="dataSource"/>
</property>
</bean>
<!-- dao配置 -->
<bean id="ibatis_BookDao" class="com.spring.demo.dao.Ibatis_BookDao">
<property name="sqlMapClient" >
<ref bean="sqlMapClient"/>
</property>
</bean>
<!-- 添加了事务的管理类 -->
<bean id="ibatis_BookManager" parent="baseTransactionProxy">
<property name="target">
<bean class="com.spring.demo.manager.Ibatis_BookManager">
<property name="dao">
<ref bean="ibatis_BookDao"/>
</property>
</bean>
</property>
</bean>
</beans>
---------------
5) 数据源参数配置文件:jdbc.properties
【说明 :按照你的具体情况来配置】:
------------
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/springdemo?useUnicode=true&characterEncoding=utf-8
jdbc.user=root
jdbc.password=root
jdbc.initialPoolSize=1
jdbc.minPoolSize=1
jdbc.maxPoolSize=10
------------
6) IBATIS 的初始化配置 sqlMapConfig.xml
------------
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMapConfig
PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN"
"http://ibatis.apache.org/dtd/sql-map-config-2.dtd">
<sqlMapConfig>
<settings useStatementNamespaces="true" />
<sqlMap resource="com/spring/demo/bean/Book.xml"/>
</sqlMapConfig>
------------
配置完毕,下面是DAO 和 测试方法:
Ibatis_BookDao.java
----------------
package com.spring.demo.dao
import java.util.List
import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport
import com.spring.demo.bean.Book
public class Ibatis_BookDao extends SqlMapClientDaoSupport {
/**
* 添加一本书
* @param book
*/
public void insertBook(Book book){
this.getSqlMapClientTemplate().insert("Book.insertBook", book)
}
/**
* 修改一本书
* @param book
*/
public void updateBook(Book book){
this.getSqlMapClientTemplate().update("Book.updateBook", book)
}
/**
* 根据ID来删除一本书
* @param id
*/
public void deleteBook(Integer id){
this.getSqlMapClientTemplate().delete("Book.deleteBook", id)
}
/**
* 根据ID来查询书对象
* @param id
* @return
*/
public Book selectBookById(Integer id){
return (Book)this.getSqlMapClientTemplate().queryForObject("Book.selectBookById", id)
}
/**
* 根据书名来查询相关书记录列表
* @param name
* @return
*/
public List selectBookByName(String name){
return this.getSqlMapClientTemplate().queryForList("Book.selectBookByName", "%"+name+"%" )
}
}
----------------
Ibatis_BookManager.java
-------------------
package com.spring.demo.manager
import java.util.List
import com.spring.demo.bean.Book
import com.spring.demo.dao.Ibatis_BookDao
public class Ibatis_BookManager {
private Ibatis_BookDao dao = null
/**
* 批量添加书本,如果列表长度超过3,就抛出异常
* @param books
*/
public void insertBooks(List books){
for(int i=0 i<books.size() i++){
if(i>2){
System.out.println("列表太长,中断事务")
throw new RuntimeException("中断事务异常,当列表长度大于3的时候故意抛出,看看事务是否回滚")
}
Book book = (Book)books.get(i)
dao.insertBook(book)
}
}
/**
* 添加一本书
* @param book
*/
public void insertBook(Book book){
this.dao.insertBook(book)
}
/**
* 修改一本书
* @param book
*/
public void updateBook(Book book){
this.dao.updateBook(book)
}
/**
* 根据ID来删除一本书
* @param id
*/
public void deleteBook(Integer id){
this.dao.deleteBook(id)
}
/**
* 根据ID来查询书对象
* @param id
* @return
*/
public Book selectBookById(Integer id){
return this.dao.selectBookById(id)
}
/**
* 根据书名来查询相关书记录列表
* @param name
* @return
*/
public List selectBookByName(String name){
return this.dao.selectBookByName(name)
}
// getter and setter //
public Ibatis_BookDao getDao() {
return dao
}
public void setDao(Ibatis_BookDao dao) {
this.dao = dao
}
-------------------
测试类:
IbatisDemo.java
------------------
package com.spring.demo
import java.util.ArrayList
import java.util.List
import org.springframework.context.ApplicationContext
import org.springframework.context.support.ClassPathXmlApplicationContext
import com.spring.demo.bean.Book
import com.spring.demo.manager.Ibatis_BookManager
import com.spring.demo.manager.Jdbc_BookManager
public class IbatisDemo {
/**
* @param args
*/
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml")
Ibatis_BookManager manager = (Ibatis_BookManager)context.getBean("ibatis_BookManager")
List books = new ArrayList()
books.add(new Book("book_10"))
books.add(new Book("book_11"))
books.add(new Book("book_12"))
books.add(new Book("book_13"))
manager.insertBooks(books)
System.out.println("OK")
}
}
------------------
说明:MYSQL 的事务完整性我是测试过的。
之前有个朋友吧数据库换成 SQL SERVER 的时候事务完整性就出现问题。
这个问题有待研究。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)