在Spring中实现编程式的事务管理(2)

更新时间:2023-05-17 03:07:01 阅读量: 实用文档 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

在Spring中实现编程式的事务管理(2)

在Spring中实现编程式的事务管理

1、编程式的事务管理实现----传统的JDBC事务管理

(1)对每个请求都是从数据源中重新取出一个连接

以往使用JDBC进行数据操作时,一般采用DataSource,从数据源中得到Connection,我们知道数据源DataSource是线程安全的,而数据连接对象Connection不是线程安全的,所以对每个请求都是从数据源中重新取出一个连接。

一般的数据源由容器进行管理,包括连接池。例如TOMCAT,WEBSPHERE,WEBLOGIC等这些J2EE商业容器都提供了这个功能。

(2)JDBC标准的事务管理实现的代码

Connection conn = null;

try

{

conn = DBConnectionFactory.getConnection;

conn.setAutoCommit(false); //(1) 缺省方式是自动提交

//完成对数据库的修改操作

Update

update

mit(); //(2)自己提交

}

catch(Exception e)

{

conn.rollback(); //(3) 恢复修改

//do sth

}

finally

{

try

{

conn.close();

}

catch(SQLException se)

{

//do sth

}

}

(3)JDBC标准的事务管理实现的代码的缺点

按照以往的思路来写代码,不仅代码量比较长,而且也很容易疏忽或者忘掉一些try/catch语句,引发一些异常无法catch,因此,我们会写DBTool类,来关闭这些资源,并且保证在关闭这些资源时,不向外抛异常。

2、Spring JdbcTemplate的缺省的事务管理模式

(1)Spring为我们对JDBC事务管理模式进行包装,从而在一定的程度上简化了编程

Spring提供了几个关于事务处理的类:

TransactionDefinition //事务属性定义

TranscationStatus //代表了当前的事务,可以提交,回滚。

在Spring中实现编程式的事务管理(2)

这些类是spring提供的用于管理事务的基础接口,其下有一个实现的抽象类AbstractPlatformTransactionManager,我们使用的事务管理类例如DataSourceTransactionManager等都是这个类的子类。

(2)JdbcTemplate的缺省的事务管理模式采用的是JDBC默认的AutoCommit模式

前面的例中的JdbcTemplate操作采用的是JDBC默认的AutoCommit模式,也就是说我们还无法保证数据操作的原子性(要么全部生效,要么全部无效)。如下面的操作:

JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);

jdbcTemplate.update("UPDATE user SET age = 10 WHERE id = '1234'");

jdbcTemplate.update("UPDATE user SET age = age+1 WHERE id = '1234'");

由于采用了AutoCommit模式,第一个update操作完成之后被自动提交,数据库中"1234"对应的记录已经被更新,如果第二个操作失败,我们无法使得整个事务回滚到最初状态。

对于这个例子也许无关紧要,但是对于一个金融帐务系统而言,这样的问题将导致致命错误。为了实现数据操作的原子性,我们需要在程序中引入事务逻辑。

(3)利用DataSourceTransactionManager类实现代码控制的事务管理

org.springframework.jdbc.datasource.DataSourceTransactionManager类为JDBC DataSource类型的数据源的事务管理组件。

只需要在Bean的定义配置的*.xml文件中对它进行配置,然后将其引入到我们的DAO类中。

(4)使用Spring 编程式的事务管理的基本流程

声明数据源

声明一个事务管理类,例如

DataSourceTransactionManager,HibernateTransactionManger,JTATransactionManager等

在我们的代码中加入事务处理代码

(5)代码示例如下:

TransactionDefinition td = new TransactionDefinition();

TransactionStatus status = transactionManager.getTransaction(td);

try

{

JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);

jdbcTemplate.update("UPDATE user SET age = 10 WHERE id = '1234'");

jdbcTemplate.update("UPDATE user SET age = age+1 WHERE id = '1234'");

mit(status);

}

catch(Exception e)

{

transactionManager.rollback(status);

}

在Spring中实现编程式的事务管理(2)

在JBuilder中的本Project中实现Spring的代码控制的事务管理

1、问题:在本项目中再添加一个对用户信息进行修改的功能模块

2、添加一个

updateUserInfo.jsp

在该页面中添加一个表单

<%@ page contentType="text/html; charset=GBK" %>

<html><head><title>updateUserInfo</title></head>

<body bgcolor="#ffffff">

<form action="/SpringMVCWeb/updateUserInfo.do" method="POST">

<input name="menuID" type="hidden" value="3">

请输入用户名称:<input type="text" name="userName"><br>

请输入用户密码:<input type="password" name="userPassWord"><br>

<input type="submit" value="开始修改">

<input type="reset" value="取消修改">

</form>

</body>

</html>

3、修改我们的控制器程序,在其中增加下面的代码(黑体部分)

package springwebapp;

import org.springframework.web.servlet.mvc.SimpleFormController;

import org.springframework.web.servlet.ModelAndView;

在Spring中实现编程式的事务管理(2)

import javax.servlet.http.HttpServletResponse;

public class UserLoginController extends SimpleFormController {

private UserLoginInterface userLoginImpleBean = null;

public void setUserLoginImpleBean(UserLoginInterface newUserLoginImpleBean) {

userLoginImpleBean = newUserLoginImpleBean;

}

public void setLoginFailure(String loginFailure) {

this.loginFailure = loginFailure;

}

public void setLoginSuccess(String loginSuccess) {

this.loginSuccess = loginSuccess;

}

public UserLoginInterface getUserLoginImpleBean() {

return userLoginImpleBean;

}

public String getLoginFailure() {

return loginFailure;

}

public String getLoginSuccess() {

return loginSuccess;

}

public UserLoginController() {

}

protected ModelAndView onSubmit(Object formBean) throws Exception

{

UserLoginForm userLoginForm = (UserLoginForm) formBean;

ModelAndView targetModelAndView = null;

switch (Integer.parseInt(userLoginForm.getMenuID())) {

case 1: //用户登录

targetModelAndView = doUserLogin(userLoginForm);

break;

case 2: //用户注册

targetModelAndView = doUserRegister(userLoginForm);

break;

case 3: //修改用户信息

targetModelAndView = doUpdateUserInfo(userLoginForm);

break;

}

return targetModelAndView;

}

public ModelAndView doUpdateUserInfo(UserLoginForm userLoginForm)

{

String userName = userLoginForm.getUserName();

String userPassWord = userLoginForm.getUserPassWord();

在Spring中实现编程式的事务管理(2)

oneUserInfoVO.setUserName(userName);

oneUserInfoVO.setUserPassWord(userPassWord);

boolean okOrNot = userLoginImpleBean.doUpdateUserInfo(oneUserInfoVO);

if (okOrNot)

{

return new ModelAndView(this.getUpdateSuccess());

}

else

{

return new ModelAndView(this.getUpdateFailure());

}

}

public ModelAndView doUserRegister(UserLoginForm userLoginForm) {

java.util.Date rightNow = new java.util.Date();

String registerTime = rightNow.toLocaleString();

UserInfoVO userInfo = new UserInfoVO();

userInfo.setUserName( userLoginForm.getUserName());

userInfo.setUserPassWord(userLoginForm.getUserPassWord());

userInfo.setUserDepartment( userLoginForm.getUserDepartment());

userInfo.setUserAdminLevel(Integer.parseInt(userLoginForm.getUserAdminLevel())); userInfo.setDepartAdminLevel(Integer.parseInt(userLoginForm.getUserAdminLevel())); userInfo.setUserImage(userLoginForm.getUserImage());

userInfo.setRegisterTime(registerTime);

boolean okOrNot = userLoginImpleBean.doUserRegister(userInfo);

if (okOrNot) {

return new ModelAndView(registerSuccess);

} else {

return new ModelAndView(registerFailure);

}

}

public ModelAndView doUserLogin(UserLoginForm userLoginForm)

{

String userName = userLoginForm.getUserName();

String userPassWord = userLoginForm.getUserPassWord();

UserInfoVO oneUserInfoVO = new UserInfoVO();

oneUserInfoVO.setUserName(userName);

oneUserInfoVO.setUserPassWord(userPassWord);

boolean okOrNot = userLoginImpleBean.doUserLogin(oneUserInfoVO);

if (okOrNot)

{

return new ModelAndView(getLoginSuccess());

}

else

{

在Spring中实现编程式的事务管理(2)

}

}

private String updateSuccess;

private String updateFailure;

public String getUpdateFailure() {

return updateFailure;

}

public String getUpdateSuccess() {

return updateSuccess;

}

public void setUpdateSuccess(String updateSuccess) {

this.updateSuccess = updateSuccess;

}

public void setUpdateFailure(String updateFailure) {

this.updateFailure = updateFailure;

}

private String registerSuccess;

private String registerFailure;

public String getRegisterSuccess() {

return registerSuccess;

}

public void setRegisterSuccess(String registerSuccess) {

this.registerSuccess = registerSuccess;

}

public String getRegisterFailure() {

return registerFailure;

}

public void setRegisterFailure(String registerFailure) {

this.registerFailure = registerFailure;

}

private String loginSuccess;

private String loginFailure;

}

4、修改我们的业务层组件

(1)修改我们的业务接口以增加新的方法定义

package springwebapp;

public interface UserLoginInterface {

public abstract boolean doUserLogin(UserInfoVO oneUserInfo);

public abstract boolean doUserRegister(UserInfoVO oneUserInfo);

public abstract boolean doUpdateUserInfo(UserInfoVO oneUserInfo);

}

在Spring中实现编程式的事务管理(2)

(2)修改我们的业务实现类以增加对该方法的具体实现-----修改用户的信息并采用事务来加以控制

在该类中添加一个transactionManager属性,并且实现我们的带事务处理的方法----事务管理的控制最好应该放到商业逻辑层。我们可以设计一个处理商业逻辑的JavaBean,在该JavaBean中调用DAO,然后把实现商业逻辑的JavaBean的方法纳入Spring的事务管理。

package springwebapp;

import org.springframework.context.ApplicationListener;

import org.springframework.context.ApplicationContextAware;

import org.springframework.context.ApplicationContext;

import org.springframework.beans.BeansException;

import org.springframework.jdbc.core.JdbcTemplate;

import org.springframework.transaction.support.DefaultTransactionDefinition;

import org.springframework.transaction.TransactionStatus;

import org.springframework.dao.DataAccessException;

import org.springframework.transaction.PlatformTransactionManager;

public class UserLoginImple implements UserLoginInterface,ApplicationContextAware

{

ApplicationListener actionEventResponse = null;

public void setActionEventResponse(ApplicationListener actionEventResponse) {

this.actionEventResponse = actionEventResponse;

}

private JdbcTemplate jdbcTemplate;

public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {

this.jdbcTemplate = jdbcTemplate;

}

private PlatformTransactionManager transactionManager;

public void setTransactionManager(PlatformTransactionManager transactionManager) {

this.transactionManager = transactionManager;

}

public UserLoginImple()

{

}

public boolean doUserRegister(UserInfoVO userInfo)

{

return true;

在Spring中实现编程式的事务管理(2)

public boolean doUpdateUserInfo(UserInfoVO oneUserInfo)

{

String userName=oneUserInfo.getUserName();

String userPassWord=oneUserInfo.getUserPassWord();

Object parameter[]={userPassWord,userName};

//我们需要事务定义,作为示例,我们采用DefaultTransactionDefinition

DefaultTransactionDefinition def = new DefaultTransactionDefinition();

TransactionStatus status = transactionManager.getTransaction(def);

String updateSQL1 = "update userInfo set userPassWord = ? where userName =?"; String updateSQL2 = "update userInfo1 set userPassWord = ? where userName =?"; try

{

this.jdbcTemplate.update(updateSQL1,parameter);

this.jdbcTemplate.update(updateSQL2,parameter);

/*

为了说明事务处理,我们对数据库表进行了两次修改,其中第二次修改应该是失败的(并注意到我們故意在数据库表名称上写错,

这使得SQL句子不合语法,因而造成DataAccessException(它封裝了SQLException),

这个异常被catch捕捉,因而执行rollback()取消前面的正确执行SQL的操作结果,如果沒有发生错误,

则最后我们使用commit()来提交操作。

*/

}

catch (DataAccessException ex)

{

transactionManager.rollback(status); // 也可以执行status.setRollbackOnly(); throw ex;

}

mit(status);

return true;

}

public boolean doUserLogin(UserInfoVO oneUserInfo)

{

boolean okOrNot=false;

String userName=oneUserInfo.getUserName();

String userPassWord=oneUserInfo.getUserPassWord();

String sqlStatement="select * from UserInfo where userName='"+userName+"' and userPassWord ='"+userPassWord+"'";

java.util.List resultRows = jdbcTemplate.queryForList(sqlStatement);

if ((resultRows!=null)&&(resultRows.size() != 0)) //现在已经对对数据库表的访问 {

UserLoginActionEvent userLoginActionEvent = new UserLoginActionEvent(actionEventResponse); //注意指定事件的目标对象

this.applicationContext.publishEvent(userLoginActionEvent); //触发事件

okOrNot=true;

在Spring中实现编程式的事务管理(2)

else

{

okOrNot=false;

}

return okOrNot;

}

private ApplicationContext applicationContext;

public void setApplicationContext(ApplicationContext applicationContext) throws BeansException

{

this.applicationContext = applicationContext;

}

}

5、修改我们的*.xml配置文件

(1)在springapp-servlet.xml文件中添加下面的与事务相关的一些配置项目

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

<property name="dataSource">

<ref bean="dataSource"/>

</property>

</bean>

(2)修改我们的业务实现类的对象声明

<bean id="userLoginImpleBean" class="erLoginImple">

<property name="actionEventResponse">

<ref bean="actionEventResponse"/>

</property>

<property name="jdbcTemplate">

<ref bean="jdbcTemplate"/>

</property>

<property name="transactionManager">

<ref bean="transactionManager"/>

</property>

在Spring中实现编程式的事务管理(2)

(3)修改我们的控制层组件以增加下面的属性

<bean id="userLoginController" class="erLoginController">

<property name="commandClass">

<value>erLoginForm</value>

</property>

<property name="userLoginImpleBean">

<ref bean="userLoginImpleBean"/>

</property>

<property name="updateSuccess">

<value>index</value>

</property>

<property name="updateFailure">

<value>userLogin/updateUserInfo</value>

</property>

<property name="loginSuccess">

<value>userLogin/loginSuccess</value>

</property>

<property name="loginFailure">

<value>userLogin/loginFailure</value>

</property>

<property name="registerSuccess">

<value>userLogin/registerSuccess</value>

</property>

<property name="registerFailure">

<value>userLogin/registerFailure</value>

</property>

</bean>

(4)对我们的<prop key="/updateUserInfo.do">userloginController</prop>加以声明

<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">

在Spring中实现编程式的事务管理(2)

<props>

<prop key="/hello.do">springappController</prop>

<prop key="/userlogin.do">userloginController</prop>

<prop key="/userRegister.do">userloginController</prop>

<prop key="/updateUserInfo.do">userloginController</prop>

</props>

</property>

</bean>

6、执行该程序,观察数据库表中的数据是否被修改了!

(1)观察数据库表中的用户密码

(2)执行本应用

输入用户名称为admin,但是密码为12345678。

在Spring中实现编程式的事务管理(2)

(3)将出现下面的错误

(4)同时在数据库表中的数据并没有被修改

(5)将代码中的

String updateSQL2 = "update userInfo1 set userPassWord = ? where userName =?";

中的数据库表名称由“userInfo1”改变为正确的“userInfo”,再执行本应用,将能够正确地对用户的密码进行修改。

在Spring中实现编程式的事务管理(2)

采用TransactionTemplate类来简化操作

1、TransactionTemplate类

TransactionTemplate封装了事务管理的功能,包括异常时的事务回滚,以及操作成功后的事务提交。和JdbcTemplate一样,它使得我们无需在琐碎的try/catch/finally代码中徘徊---也就是为我们省去了部分事务提交、回滚代码。

2、修改原来的doUpdateUserInfo方法

public boolean doUpdateUserInfo(UserInfoVO oneUserInfo)

{

String userName=oneUserInfo.getUserName();

String userPassWord=oneUserInfo.getUserPassWord();

Object parameter[]={userPassWord,userName};

TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);

String updateSQL1 = "update userInfo set userPassWord = ? where userName =?";

String updateSQL2 = "update userInfo1 set userPassWord = ? where userName =?";

/*

下面使用TransactionCallbackWithoutResult,并在它的doInTransactionWithoutResult()中进行SQL操作,如果执行中发生异常,則之前所有的操作将被取消,否则最后自动提交操作。

*/

transactionTemplate.execute(

new TransactionCallbackWithoutResult()

{

public void doInTransactionWithoutResult(TransactionStatus status)

{

this.jdbcTemplate.update(updateSQL1,parameter);

this.jdbcTemplate.update(updateSQL2,parameter);

}

});

return true;

}

(3)说明

在doInTransactionWithoutResult方法中进行的操作,如果抛出未捕获异常将被自动回滚,如果成功执行,则将被自动提交。和JdbcTemplate一样,它使得我们无需在琐碎的try/catch/finally代码中徘徊。

3、如果需要使用基于容器的数据源(JNDI)来实现事务

我们可以采用如下配置,使用JtaTransactionManager,它为PlatformTransactionManager接口的带JNDI数据源的事务实现。

<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">

<property name="jndiName">

<value>jdbc/sample</value>

</property>

</bean>

<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>

在Spring中实现编程式的事务管理(2)

如果我们使用JTA 我们需要使用通过JNDI获得的容器数据源,和一个JtaTransactionManager实 现。 <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">

<property name="jndiName">

<value>jdbc/sample</value>

</property>

</bean>

<bean id="transactionManager"

class="org.springframework.transaction.jta.JtaTransactionManager"/>

默认的Spring JtaTransactionManager设置将从标准的JNDI位置获取JTA的erTransaction对象,该JNDI位置由J2EE指定:java:comp/UserTransaction。对于大多数标准J2EE环境下的用例来说,它工作良好。

但是,默认的JtaTransactionManager不能执行事务挂起操作(即它不支持PROPAGATION_REQUIRES_NEW和PROPAGATION_NOT_SUPPORTED)。原因是标准的JTA UserTransaction接口不支持挂起或恢复事务的操作;它只支持开始和完成新事务的操作。

本文来源:https://www.bwwdw.com/article/dqz4.html

Top