spring的事务控制应该说是所有用spring的项目中必不可少的东西。在我所经历过的所有用spring的项目中,IOC和事务控制是必用的。其它的一些东西,根据项目不同会有所选择。为什么要用spring的这两个呢?使用IOC可以让我们的代码进行完全的松耦合。而事务控制,可以让我们基本上在写代码的时候短暂的忘记事务的存在。专心于业务层的实现。下面给出一个spring 事务控制的例子。首页是Action层的代码:
public
String createUser()
throws
Exception
...
{
user = new User();
user.setUsername("Gavin");
user.setPassword("123");
user.setUserDetail(new UserDetail());
if (logger.isDebugEnabled()) ...{
logger.debug("创建一个新的用户. " + user.toString());
}
try ...{
userService.saveUser(user);
} catch (Exception e) ...{
if (logger.isDebugEnabled()) ...{
logger.debug("用户操作出错. " + e);
}
}
return Action.SUCCESS;
}
Action层只是简单的去调用service层,为了使例子简单,我去掉了一些不必要的东西。只剩下核心的一些。下面给出service层的代码(所有接口用处不大,不给出来)。
package com.websidesoft.service.impl;
import
org.apache.commons.logging.Log;
import
org.apache.commons.logging.LogFactory;
import
com.websidesoft.dao.iface.FriendDAO;
import
com.websidesoft.dao.iface.UserDAO;
import
com.websidesoft.entities.User;
import
com.websidesoft.exception.WSSException;
import
com.websidesoft.service.iface.UserService;
public
class
UserServiceManager
implements
UserService
...
{
private static final Log logger = LogFactory.getLog(UserServiceManager.class);
private FriendDAO friendDao;
private UserDAO userDao;
public void deleteUser(int userId) throws WSSException ...{
}
public User getUser(int userId) throws WSSException ...{
return null;
}
public void saveUser(User user) throws WSSException ...{
userDao.saveUser(user);
try ...{
friendDao.saveFriend(user);
} catch (Exception e) ...{
if (logger.isDebugEnabled()) ...{
logger.debug("保存friend出错. " + e);
}
}
// throw new RuntimeException("出错.............");
}
public void updateUser(User user) throws WSSException ...{
}
public FriendDAO getFriendDao() ...{
return friendDao;
}
public void setFriendDao(FriendDAO friendDao) ...{
this.friendDao = friendDao;
}
public UserDAO getUserDao() ...{
return userDao;
}
public void setUserDao(UserDAO userDao) ...{
this.userDao = userDao;
}
}
service是重点,给出了所有service层的代码。先给出所有的代码,之后再来详细说明。下面是DAO层代码:
package com.websidesoft.dao.impl;
import java.util.HashMap;
import java.util.Map;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import com.websidesoft.dao.iface.UserDAO;
import com.websidesoft.entities.User;
import com.websidesoft.exception.DataAccessException;
public
class
UserDAOImpl
extends
JdbcDaoSupport
implements
UserDAO
...
{
public void deleteUser(int userId) throws DataAccessException ...{
}
public User getUser(int userId) throws DataAccessException ...{
return null;
}
public void saveUser(User user) throws DataAccessException ...{
String sql = "insert into wss_users(username, password) values (:username, :password)";
NamedParameterJdbcTemplate pstmt = new NamedParameterJdbcTemplate(this.getJdbcTemplate().getDataSource());
Map nameParameters = new HashMap();
nameParameters.put("username", user.getUsername());
nameParameters.put("password", user.getPassword());
pstmt.update(sql, nameParameters);
}
public void updateUser(User user) throws DataAccessException ...{
}
}
package com.websidesoft.dao.impl;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import com.websidesoft.dao.iface.FriendDAO;
import com.websidesoft.exception.DataAccessException;
public
class
FriendDAOImpl
extends
JdbcDaoSupport
implements
FriendDAO
...
{
public void saveFriend(Object o) throws DataAccessException ...{
throw new NullPointerException("不能传入空的参数.");
}
}
为了简单和测试,我在FriendDAO中直接抛出异常。下面给出xml配置文件,只是核心的,还有一些bean的ioc就不写出来了。
<? xml versinotallow="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"
xmlns:jee ="http://www.springframework.org/schema/jee"
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
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.0.xsd" >
< bean id
="dataSource"
class
="org.springframework.jndi.JndiObjectFactoryBean"
>
< property name
="jndiName"
value
="java:comp/env/jdbc/wss"
/>
</ bean >
< bean id
="transactionManager"
class
="org.springframework.jdbc.datasource.DataSourceTransactionManager"
>
< property name
="dataSource"
ref
="dataSource"
/>
</ bean >
< tx:advice id
="txAdvice"
transaction-manager
="transactionManager"
>
< tx:attributes >
< tx:method name
="save*"
/>
< tx:method name
="get*"
/>
</ tx:attributes >
</ tx:advice >
< aop:config >
< aop:pointcut id
="userServicePointcut"
expression
="execution(* com.websidesoft.service.iface.UserService.*(..))"
/>
< aop:advisor advice-ref
="txAdvice"
pointcut-ref
="userServicePointcut"
/>
< aop:aspect id
="userAspect"
ref
="userLog"
>
< aop:before pointcut
="execution(* com.websidesoft.service.iface.UserService.*(..)) and args(user)"
method
="saveUser"
/>
</ aop:aspect >
</ aop:config >
<!-- DAO层 -->
< bean id
="userDao"
class
="com.websidesoft.dao.impl.UserDAOImpl"
>
< property name
="dataSource"
ref
="dataSource"
/>
</ bean >
< bean id
="friendDao"
class
="com.websidesoft.dao.impl.FriendDAOImpl"
>
< property name
="dataSource"
ref
="dataSource"
/>
</ bean >
</ beans >
上面给出了这个例子的所有代码。下面首先说明一下事务控制的部份。核心的事务控制部份如下:
< bean id ="dataSource" class
="org.springframework.jndi.JndiObjectFactoryBean"
>
< property name ="jndiName"
value
="java:comp/env/jdbc/wss"
/>
</ bean >
< bean id ="transactionManager"
class
="org.springframework.jdbc.datasource.DataSourceTransactionManager"
>
< property name ="dataSource"
ref
="dataSource"
/>
</ bean >
< tx:advice id ="txAdvice"
transaction-manager
="transactionManager"
>
< tx:attributes >
< tx:method name ="save*"
/>
< tx:method name ="get*"
/>
</ tx:attributes >
</ tx:advice >
上面的配置实现了以容器的方式配置数据源。对数据源进行事务管理。看一下service层的代码:
public void saveUser(User user) throws WSSException
...
{
userDao.saveUser(user);
try ...{
friendDao.saveFriend(user);
} catch (Exception e) ...{
if (logger.isDebugEnabled()) ...{
logger.debug("保存friend出错. " + e);
}
}
// throw new RuntimeException("出错.............");
}
在上面的代码中,如果出现错误,将抛出WSSException异常。这个异常是实现了Exception的全局异常。spring在对事务控制时,只对运行时异常和未检测异常才会进行事务的rollback,也就是说当抛出检测异常将不会被rollback。看上面的例子,我对friendDao.saveFriend(user)进行了try catch动作。因为在friendDao中,我直接抛出抛常。也就是说如果friendDao出错,将不能影响到用户的正常注册。上面的异常不会导致事务回滚。上面的代码运行进,将会用户正常的注册。在下面,我把一行代码注释了,throw new RuntimeException("出错.............");这一代码有什么作用呢?这一句最重要的作用就是,如果打开,那么所有的事务将进行rollback操作。也就是说保存用户将会出错。不会保存成功。
上面只是简单的举了个例子来说明spring2.0中事务控制。当然,还可以控制指定哪些异常将会导致回滚动作。只需要在事务控制的xml中加上rollback-for="异常全名"即可。