一. 前言:
在上一篇文章中, 我们讲了使用动态代理完成事务处理, 这种方式将service层的所有public方法都加入事务中, 这显然不是我们需要的,我们需要代理的只是那些需要更改数据库的方法, 在本篇文章中, 我们将使用Java注解来标记需要处理事务的方法.
二. 实例:
1. 代码结构图:
2. 自定义注解:
- @Target (ElementType.METHOD)
- @Retention(RetentionPolicy.RUNTIME)
- public @interface Transactional{
- }
- /**
- * 动态代理
- */
- public class TransactionProxy {
- public static Object proxyFor(Object object) {
- return Proxy.newProxyInstance(
- object.getClass().getClassLoader(),
- object.getClass().getInterfaces(),
- new TransactionInvocationHandler(object)
- );
- }
- }
- class TransactionInvocationHandler implements InvocationHandler {
- private Object proxy;
- TransactionInvocationHandler(Object object) {
- this.proxy = object;
- }
- public Object invoke(Object obj, Method method, Object[] objects) throws Throwable {
- Method originalMethod = proxy.getClass().getMethod(method.getName(), method.getParameterTypes());
- // 判断方法上是否有Transactional注解
- if (!originalMethod.isAnnotationPresent(Transactional.class)) {
- // 没有注解, 直接调用方法不用控制事务
- return method.invoke(proxy, objects);
- }
- TransactionManager.beginTransaction();
- Object result = null;
- try {
- result = method.invoke(proxy, objects);
- TransactionManager.commit();
- } catch (Exception e) {
- TransactionManager.rollback();
- } finally {
- TransactionManager.close();
- }
- return result;
- }
- }
在TransactionInvocationHandle的invoke方法中, 首先判断被代理的方法是否标记有Transactional注解,
如果没有则直接调用method.invoke(proxy, objects), 否则, 进入事务处理代码.
4. 业务代码类:
- /**
- * 业务逻辑层
- */
- public class AccountServiceImpl implements AccountService {
- @Override
- @Transactional
- public void transfer(Account outAccount, Account inAccount, int money) throws SQLException {
- // 查询两个账户
- AccountDAO accountDAO = new AccountDAO();
- outAccount = accountDAO.findAccountById(outAccount.getId());
- inAccount = accountDAO.findAccountById(inAccount.getId());
- // 转账 - 修改原账户金额
- outAccount.setMoney(outAccount.getMoney() - money);
- inAccount.setMoney(inAccount.getMoney() + money);
- // 更新账户金额
- accountDAO.update(outAccount);
- accountDAO.update(inAccount);
- }
- }
参考文章:http://www.davenkin.me/post/2013-02-24/40048572306
源码下载: http://download.csdn.net/detail/zdp072/7908913
一. 前言:
在写这篇博客之前,我们需要弄清楚两个概念:本地事务和分布式事务。
本地事务:只处理单一数据源,比如单个数据库。
分布式事务:处理多种异构的数据源, 比如某个业务操作中同时包含JDBC和JMS或者某个操作需要访问多个不同的数据库。
Java通过JTA完成分布式事务, JTA本身只是一种规范, 本篇博客将使用JOTM作为实现, 后续还会使用Atomikos实现。
二. 业务背景:
假定我们有这样一个需求:当我们新建一个用户的时候需要往一个DB中插入一条用户记录,还需要往另一个DB中记录日志
因为是不同的DB操作,所以这里就涉及到分布式事务的处理。
三. 代码实现:
1. 代码结构图:
2. 建表语句:
- create database log;
- DROP TABLE IF EXISTS `log`;
- CREATE TABLE `log` (
- `id` varchar(20) NOT NULL,
- `content` varchar(100) default NULL,
- PRIMARY KEY (`id`)
- );
- create database user;
- DROP TABLE IF EXISTS `user`;
- CREATE TABLE `user` (
- `id` varchar(20) NOT NULL,
- `name` varchar(40) default NULL,
- PRIMARY KEY (`id`)
- );
3. 配置文件ApplicationContext.xml
- <?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.xsd
- http://www.springframework.org/schema/tx
- http://www.springframework.org/schema/tx/spring-tx.xsd
- http://www.springframework.org/schema/aop
- http://www.springframework.org/schema/aop/spring-aop.xsd">
- <!-- 引用Spring内部所提供的对JOTM支持的工厂类 -->
- <bean id="jotm" class="org.springframework.transaction.jta.JotmFactoryBean" />
- <!-- 配置JTA事务管理器, 并在管理器中使用上面所配置的JOTM -->
- <bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager">
- <property name="userTransaction" ref="jotm" />
- </bean>
- <!-- 配置多个数据源 -->
- <bean id="db1" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource" destroy-method="shutdown">
- <property name="dataSource">
- <bean class="org.enhydra.jdbc.standard.StandardXADataSource" destroy-method="shutdown">
- <property name="transactionManager" ref="jotm" />
- <property name="driverName" value="com.mysql.jdbc.Driver" />
- <property name="url" value="jdbc:MySQL://localhost:3306/user" />
- </bean>
- </property>
- <property name="user" value="root" />
- <property name="password" value="root" />
- </bean>
- <bean id="db2" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource" destroy-method="shutdown">
- <property name="dataSource">
- <bean class="org.enhydra.jdbc.standard.StandardXADataSource" destroy-method="shutdown">
- <property name="transactionManager" ref="jotm" />
- <property name="driverName" value="com.mysql.jdbc.Driver" />
- <property name="url" value="jdbc:MySQL://localhost:3306/log" />
- </bean>
- </property>
- <property name="user" value="root" />
- <property name="password" value="root" />
- </bean>
- <!-- 根据不同的数据源配置两个jdbcTemplate -->
- <bean id="jdbcTemplate1" class="org.springframework.jdbc.core.JdbcTemplate">
- <property name="dataSource" ref="db1" />
- </bean>
- <bean id="jdbcTemplate2" class="org.springframework.jdbc.core.JdbcTemplate">
- <property name="dataSource" ref="db2" />
- </bean>
- <bean id="userDao" class="com.zdp.dao.UserDao">
- <property name="jdbcTemplate" ref="jdbcTemplate1" />
- </bean>
- <bean id="logDao" class="com.zdp.dao.LogDao">
- <property name="jdbcTemplate" ref="jdbcTemplate2" />
- </bean>
- <bean id="userService" class="com.zdp.service.UserService">
- <property name="userDao" ref="userDao" />
- <property name="logDao" ref="logDao" />
- </bean>
- <!-- JTA事务传播特性 -->
- <tx:advice id="txAdviceJTA" transaction-manager="txManager">
- <tx:attributes>
- <tx:method name="save*" isolation="DEFAULT" propagation="REQUIRED" rollback-for="Exception"/>
- <tx:method name="add*" isolation="DEFAULT" propagation="REQUIRED" rollback-for="Exception"/>
- <tx:method name="create*" isolation="DEFAULT" propagation="REQUIRED" rollback-for="Exception"/>
- <tx:method name="insert*" isolation="DEFAULT" propagation="REQUIRED" rollback-for="Exception"/>
- <tx:method name="del*" isolation="DEFAULT" propagation="REQUIRED" rollback-for="Exception"/>
- <tx:method name="update*" isolation="DEFAULT" propagation="REQUIRED" rollback-for="Exception"/>
- <tx:method name="*" read-only="true"/>
- </tx:attributes>
- </tx:advice>
- <aop:config>
- <aop:advisor pointcut="execution(* com.zdp.service..*(..))" advice-ref="txAdviceJTA" />
- </aop:config>
- </beans>
4. service业务类:
- public class UserService {
- private UserDao userDao;
- private LogDao logDao;
- public void saveUser(String id, String name) {
- userDao.insertUser(id, name);
- // int i = 1 / 0; // 制造异常
- logDao.insertLog(id, id + "_" + name);
- }
- public UserDao getUserDao() {
- return userDao;
- }
- public void setUserDao(UserDao userDao) {
- this.userDao = userDao;
- }
- public LogDao getLogDao() {
- return logDao;
- }
- public void setLogDao(LogDao logDao) {
- this.logDao = logDao;
- }
- }
5. Dao类:
- public class UserDao extends JdbcDaoSupport {
- public void insertUser(String id, String name) {
- JdbcTemplate template = getJdbcTemplate();
- template.execute("insert into user values('" + id + "','" + name + "')");
- }
- }
- public class LogDao extends JdbcDaoSupport {
- public void insertLog(String id, String content) {
- JdbcTemplate template = getJdbcTemplate();
- template.execute("insert into log values('" + id + "','" + content + "')");
- }
- }
6. 测试类:
- public class UserTest {
- @Test
- public void testSave() {
- ApplicationContext cxt = new ClassPathXmlApplicationContext("ApplicationContext.xml");
- UserService us = (UserService) cxt.getBean("userService");
- us.saveUser("1", "zhangsan");
- }
- }
源码下载: http://download.csdn.net/detail/zdp072/7950383
一. 前言:
上一篇博客中,我们使用jotm实现了分布式事务, 本篇将使用atomikos实现。
基本的代码都是一样的,就是配置略有不同。
二. 代码实现:
1. 代码结构图:
2. 配置文件:ApplicationContext.xml
- <?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.xsd
- http://www.springframework.org/schema/tx
- http://www.springframework.org/schema/tx/spring-tx.xsd
- http://www.springframework.org/schema/aop
- http://www.springframework.org/schema/aop/spring-aop.xsd">
- <!-- 数据库1 -->
- <bean id="db1" class="com.atomikos.jdbc.SimpleDataSourceBean" init-method="init" destroy-method="close">
- <property name="uniqueResourceName">
- <value>mysql/main</value>
- </property>
- <property name="xaDataSourceClassName">
- <value>com.mysql.jdbc.jdbc2.optional.MysqlXADataSource</value>
- </property>
- <property name="xaDataSourceProperties">
- <value>URL=jdbc:mysql://localhost:3306/user?useUnicode=true&characterEncoding=utf-8;user=root;password=root</value>
- </property>
- <property name="exclusiveConnectionMode">
- <value>true</value>
- </property>
- <property name="connectionPoolSize">
- <value>3</value>
- </property>
- <property name="validatingQuery">
- <value>SELECT 1</value>
- </property>
- </bean>
- <!-- 数据库2 -->
- <bean id="db2" class="com.atomikos.jdbc.SimpleDataSourceBean" init-method="init" destroy-method="close">
- <property name="uniqueResourceName">
- <value>mysql/secondary</value>
- </property>
- <property name="xaDataSourceClassName">
- <value>com.mysql.jdbc.jdbc2.optional.MysqlXADataSource</value>
- </property>
- <property name="xaDataSourceProperties">
- <value>URL=jdbc:mysql://localhost:3306/log?useUnicode=true&characterEncoding=utf-8;user=root;password=root</value>
- </property>
- <property name="exclusiveConnectionMode">
- <value>true</value>
- </property>
- <property name="connectionPoolSize">
- <value>3</value>
- </property>
- <property name="validatingQuery">
- <value>SELECT 1</value>
- </property>
- </bean>
- <bean id="userTransactionManager" init-method="init" destroy-method="close"
- class="com.atomikos.icatch.jta.UserTransactionManager">
- <property name="forceShutdown" value="true" />
- </bean>
- <bean id="userTransactionImp" class="com.atomikos.icatch.jta.UserTransactionImp">
- <property name="transactionTimeout" value="300"/>
- </bean>
- <bean id="jtaTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
- <property name="transactionManager" ref="userTransactionManager" />
- <property name="userTransaction" ref="userTransactionImp" />
- <property name="allowCustomIsolationLevels" value="true"/>
- </bean>
- <!-- 配置事务传播特性 -->
- <tx:advice id="txAdvice" transaction-manager="jtaTransactionManager">
- <tx:attributes>
- <tx:method name="save*" propagation="REQUIRED" rollback-for="Exception"/>
- <tx:method name="add*" propagation="REQUIRED" rollback-for="Exception"/>
- <tx:method name="create*" propagation="REQUIRED" rollback-for="Exception"/>
- <tx:method name="insert*" propagation="REQUIRED" rollback-for="Exception"/>
- <tx:method name="del*" propagation="REQUIRED" rollback-for="Exception"/>
- <tx:method name="update*" propagation="REQUIRED" rollback-for="Exception"/>
- <tx:method name="*" read-only="true"/>
- </tx:attributes>
- </tx:advice>
- <!-- 事务管理 -->
- <aop:config proxy-target-class="true">
- <aop:advisor pointcut="execution(* com.zdp.service..*.*(..))" advice-ref="txAdvice"/>
- </aop:config>
- <bean id="userDao" class="com.zdp.dao.UserDao">
- <property name="dataSource" ref="db1" />
- </bean>
- <bean id="logDao" class="com.zdp.dao.LogDao">
- <property name="dataSource" ref="db2" />
- </bean>
- <bean id="userService" class="com.zdp.service.UserService">
- <property name="userDao" ref="userDao" />
- <property name="logDao" ref="logDao" />
- </bean>
- </beans>
源码下载: http://download.csdn.net/detail/zdp072/7950391