Spring的事务管理

最近因为项目开发,用到Spring事务管理,在网上查了很多资料,总是零零散散,说得都不太全,结果让我试了很多方法,用了好几天才解决。
这里总结一下希望给用到的人有些帮助。

Spring的事务管理,要从以下几个方面都满足条件才能实现异常时事务回滚:

1、数据库事务支持。
2、Spring事务配置正确。
3、软件代码正确。


以下分别说明:

1、数据库事务支持

如果是Oracle数据库,默认都是支持事务的,不多说。很多开发用的是mysql,用mysql的话要使用innerDB和DBD模式,MyISam不支持事务。mysql默认的是MyISam 这种类型的表是没有事务的.所以不管spring如何配置,对此表事务无效.正确做法是改为innerDB。
在建表语句中加上 “engine=INNODB”即可建成innerDB模式的表:

CREATE TABLE parent(
id INT NOT NULL,
PRIMARY KEY (id)
)engine=INNODB;

其它数据库我不太了解,可以去网上查一下。

2、Spring事务配置正确。

这是网上能找到很多答案的地方,也很容易糊涂。网上搜一下,会找到很多。

但要分清处,是Spring + hibernate,还是 Spring+ibatis。

http://jiake.iteye.com/blog/599418

根据代理机制的不同,总结了五种Spring事务的配置方式,配置文件如下:


第一种方式:每个Bean都有一个代理
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">

<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="configLocation" value="classpath:hibernate.cfg.xml" />
<property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
</bean>

<!-- 定义事务管理器(声明式的事务) -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>

<!-- 配置DAO -->
<bean id="userDaoTarget" class="com.bluesky.spring.dao.UserDaoImpl">
<property name="sessionFactory" ref="sessionFactory" />
</bean>

<bean id="userDao"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<!-- 配置事务管理器 -->
<property name="transactionManager" ref="transactionManager" />
<property name="target" ref="userDaoTarget" />
<property name="proxyInterfaces" value="com.bluesky.spring.dao.GeneratorDao" />
<!-- 配置事务属性 -->
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
</beans>

第二种方式:所有Bean共享一个代理基类

<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">

<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="configLocation" value="classpath:hibernate.cfg.xml" />
<property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
</bean>

<!-- 定义事务管理器(声明式的事务) -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>

<bean id="transactionBase"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
lazy-init="true" abstract="true">
<!-- 配置事务管理器 -->
<property name="transactionManager" ref="transactionManager" />
<!-- 配置事务属性 -->
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>

<!-- 配置DAO -->
<bean id="userDaoTarget" class="com.bluesky.spring.dao.UserDaoImpl">
<property name="sessionFactory" ref="sessionFactory" />
</bean>

<bean id="userDao" parent="transactionBase" >
<property name="target" ref="userDaoTarget" />
</bean>
</beans>

第三种方式:使用拦截器

<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">

<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="configLocation" value="classpath:hibernate.cfg.xml" />
<property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
</bean>

<!-- 定义事务管理器(声明式的事务) -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>

<bean id="transactionInterceptor"
class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager" ref="transactionManager" />
<!-- 配置事务属性 -->
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>

<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames">
<list>
<value>*Dao</value>
</list>
</property>
<property name="interceptorNames">
<list>
<value>transactionInterceptor</value>
</list>
</property>
</bean>

<!-- 配置DAO -->
<bean id="userDao" class="com.bluesky.spring.dao.UserDaoImpl">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
</beans>

第四种方式:使用tx标签配置的拦截器

<?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-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">

<context:annotation-config />
<context:component-scan base-package="com.bluesky" />

<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="configLocation" value="classpath:hibernate.cfg.xml" />
<property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
</bean>

<!-- 定义事务管理器(声明式的事务) -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>

<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED" />
</tx:attributes>
</tx:advice>

<aop:config>
<aop:pointcut id="interceptorPointCuts"
expression="execution(* com.bluesky.spring.dao.*.*(..))" />
<aop:advisor advice-ref="txAdvice"
pointcut-ref="interceptorPointCuts" />
</aop:config>
</beans>

第五种方式:全注解

<?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-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">

<context:annotation-config />
<context:component-scan base-package="com.bluesky" />

<tx:annotation-driven transaction-manager="transactionManager"/>

<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="configLocation" value="classpath:hibernate.cfg.xml" />
<property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
</bean>

<!-- 定义事务管理器(声明式的事务) -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>

</beans>
此时在DAO上需加上@Transactional注解,如下:
package com.bluesky.spring.dao;

import java.util.List;

import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.stereotype.Component;

import com.bluesky.spring.domain.User;

@Transactional
@Component("userDao")
public class UserDaoImpl extends HibernateDaoSupport implements UserDao {

public List<User> listUsers() {
return this.getSession().createQuery("from User").list();
}


}


3、软件代码正确。

3.1 异常要是RuntimeException异常,事务才会回滚。
只有RuntimeException异常,事务才会回滚(当然,也可以是继承于RuntimeException的异常)。所以,需要你事务方法里面出错时的异常要为RuntimeExcptin异常,或者事务方法里的调用方法是抛出这类异常的。NullPointerException是继承于RuntimeException的异常,也是可以回滚的。
最好的处理方式,就是使自己的业务异常类继承于RuntimeException。然后事务方法类里面的方法,抛出这种业务异常。

3.2 方法的事务注释要正确(对于第五种Spring事务配置)。

这里针对的是第五种Spring事务配置,其它配置不用方法上进行事务注释。
但这里要说一下,事务几种方式,一般选择rep就可以了。
对于只读设置为true还是false;一般来说是只有查询的用true,只要方法中有增,删,改,或是调用别的方法里有增,删,改时,都要用false;

3.3 service 和 Dao 要是 接口形式(对于第四种Spring事务配置)。
如果不以接口形式,运行时会出现类型转换错误。刚开始出现这个错,我不知所措,因为我原来代码是没有问题的,只是修改了一下Spring配置,怎么就出转型错呢。
我不愿意使用这种方式,因为这样一来每个类都要增加接口,增加了很多开发工作量。修改类也要同时修改接口,维护起来也不方便。


总结一个最简单的实现要3点:

1、数据库支持,oracle不用设置,mysql建表脚步要用innerDB和DBD模式:

--菜单表(E_BS_MENU)--
CREATE TABLE IF NOT EXISTS E_BS_MENU
(
FMENUID NUMERIC(20,0) NOT NULL COMMENT '菜单ID',
FNUMBER VARCHAR(4) NOT NULL COMMENT '编码',
FFULLNUMBER VARCHAR(320) NOT NULL COMMENT '全编码',
FNAME VARCHAR(100) NOT NULL COMMENT '菜单名',
FPARENTID NUMERIC(20,0) NOT NULL COMMENT '上级菜单ID',
FROOTID NUMERIC(20,0) NOT NULL COMMENT '根ID',
FLEVEL INT(4) NOT NULL COMMENT '级次',
FISDETAIL INT(1) NOT NULL COMMENT '是否为明细',
....
PRIMARY KEY (FKEY,FLOCALE),
INDEX EX_BS_MENU (FLOCALE,FFULLNUMBER)
) ENGINE=INNODB DEFAULT CHARSET=utf8
COMMENT = '菜单表';


2、spring配置中有如下 tx的配置项:

<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd"
default-lazy-init="true">

<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>

<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" />

</beans>


3、调用的代码都在服务器层方法中,(可以再里面去调用别的server方法),方法上面注明Transactional:

如下面注册公司时,同时注册一个用户,并初始化公司的一些数据,如果注册用户或者初始化公司数据失败,不会在数据库中新增公司。

/**
* 注册公司
* 测试事务
* @param company Company
*/
@Transactional(readOnly=false, propagation=Propagation.REQUIRED)
public User regist2(Company company,String userName, String pw,int salt) throws ValidateException {
Assert.notNull(company);

//名称重复校验
validateFieldNotEmpty("name", company.getName());
if(this.findExistByName(company.getName())){
throw new ValidateException("公司名称重复:"+company.getName());
}
//生成COID
company.setCoId(getNextCoID());

//编码设置为COID
company.setNumber(String.valueOf(company.getCoId()));
company.setRegistTime(DateUtilsWs.now());
company.setFee(true);

this.validate(company); // 先做校验

dao.insert(company); // 再做保存

if (LOG.isInfoEnabled()) LOG.info("addNew Company, coid " + company.getCoId() + ", value " + company);

//新增一个用户超级用户,同时也是系统管理员
UserService userService = UserService.getInstance();
User user = new User();
user.setCoId(company.getCoId());
user.setName(userName);
user.setNumber(userName);
user.setPassword(pw);
user.setAdmin(true);
user.setSuperAdmin(true);
user.setDelete(false);

wsmsContext.setUserId(user.getCoId());
wsmsContext.setUserName(userName);
wsmsContext.setUserNumber(userName);

userService.registUser(user, salt);

//写日志
LogService logService = LogService.getInstance();
logService.addLog(OperateObject.company, OperateType.add, company.getNumber(),company.getName(), company.getCoId(), null);

initCompany(company.getCoId());

return user;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值