Aaronlong31

  BlogJava :: 首页 :: 联系 :: 聚合  :: 管理
  12 Posts :: 3 Stories :: 17 Comments :: 0 Trackbacks
 

使用平台:

1.    Mysql 5.0.8

2.    ibatis 2.3.4

3.    spring 2.5

4.    eclipse 3.3

完成账户的转账功能,在出现异常时回滚。由spring管理各个类之间的关系和事务处理。

  1. 首先建立两个数据库,一个在本机上,一个在远端机上,脚本分别如下
     1DROP DATABASE IF EXISTS testdb_a_zhanglong;
     2
     3CREATE DATABASE testdb_a_zhanglong;
     4
     5USE testdb_a_zhanglong;
     6
     7CREATE TABLE Account(
     8    aid        INT        AUTO_INCREMENT,
     9    owner        VARCHAR(20)    NOT NULL,
    10    balance        Float(10,3)    DEFAULT 0.0,
    11    PRIMARY KEY(aid)
    12);
    13
    14INSERT INTO Account (owner,balance) VALUES ('zhanglong',10000.00);
    15
     1DROP DATABASE IF EXISTS testdb_b_zhanglong;
     2
     3CREATE DATABASE testdb_b_zhanglong;
     4
     5USE testdb_b_zhanglong;
     6
     7CREATE TABLE Account(
     8    aid        INT        AUTO_INCREMENT,
     9    owner        VARCHAR(20)    NOT NULL,
    10    balance        Float(10,3)    DEFAULT 0.0,
    11    PRIMARY KEY(aid)
    12);
    13
    14INSERT INTO Account (owner,balance) VALUES ('zhengtao',10000.00);
    15

    在这里两个数据库是完全一样的,是为了方便起见

  2. 建立AccountJavaBean
    1package com.aaron.atomikos.domain;
    2
    3public class Account {
    4
    5    private int accountId;
    6    private String owner;
    7    private float balance;
    8}

    9
  3. 建立业务层接口AccountService
     1package com.aaron.atomikos.service;
     2import com.aaron.atomikos.domain.Account;
     3public interface AccountService {
     4    void addAccount(Account account);
     5    
     6    /**
     7     * 转账
     8     * @param ida
     9     * @param idb
    10     * @param amount
    11     * @return
    12     */

    13    boolean transfer(int ida,int idb,float amount); 
    14    
    15    Account getAccountById(int id);
    16}

    17
     
  4. 建立业务层接口实现类AccountServiceImpl
     1package com.aaron.atomikos.service.impl;
     2import com.aaron.atomikos.dao.AccountDao;
     3import com.aaron.atomikos.domain.Account;
     4import com.aaron.atomikos.service.AccountService;
     5public class AccountServiceImpl implements AccountService {
     6
     7    // 数据库a的Dao类
     8    private AccountDao accountaDao;
     9    // 数据库b的Dao类
    10    private AccountDao accountbDao;
    11    public AccountDao getAccountbDao() {
    12        return accountbDao;
    13    }

    14    public void setAccountbDao(AccountDao accountbDao) {
    15        this.accountbDao = accountbDao;
    16    }

    17    public AccountDao getAccountDao() {
    18        return accountaDao;
    19    }

    20    public void setAccountaDao(AccountDao accountaDao) {
    21        this.accountaDao = accountaDao;
    22    }

    23    @Override
    24    public void addAccount(Account account) {
    25        accountaDao.insertAccount(account);
    26    }

    27
    28    @Override
    29    public boolean transfer(int ida, int idb, float amount) {
    30        boolean isSuccess = false;
    31
    32        Account accounta = new Account();
    33        Account accountb = new Account();
    34
    35        //检查账户是否存在
    36        if (!accountaDao.isExists(ida) || !accountbDao.isExists(idb)) {
    37            throw new RuntimeException("账户" + ida + "" + idb + "不存在");
    38        }

    39        
    40        //得到账户余额
    41        float balancea = accountaDao.getBalance(ida);
    42        float balanceb = accountbDao.getBalance(idb);
    43        if (balancea < amount) {
    44            throw new RuntimeException("账户" + ida + "的余额不足!");
    45        }

    46        
    47        //转账
    48        accounta.setAccountId(ida);
    49        accountb.setAccountId(idb);
    50        accounta.setBalance(balancea - amount);
    51        accountb.setBalance(balanceb + amount);
    52
    53        accountaDao.updateAccount(accounta);
    54
    55        //两次更新之间如果跑出异常,则会回滚操作
    56        // if(true){
    57        // throw new RuntimeException();
    58        // }
    59
    60        accountbDao.updateAccount(accountb);
    61        isSuccess = true;
    62        return isSuccess;
    63    }

    64
    65    @Override
    66    public Account getAccountById(int id) {
    67        return accountaDao.selectAccountById(id);
    68    }

    69}

    70
  • 建立DAO层AccountDao,继承自spring的SqlMapClientDaoSupport,这里为了简单起见,没有使用接口,直接写了具体实现类
     1package com.aaron.atomikos.dao;
     2
     3import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;
     4
     5import com.aaron.atomikos.domain.Account;
     6
     7public class AccountDao extends SqlMapClientDaoSupport {
     8
     9    /**
    10     * 插入账户
    11     * 
    12     * @param account
    13     */

    14    public void insertAccount(Account account) {
    15        getSqlMapClientTemplate().insert("Account.insertAccount", account);
    16    }

    17
    18    /**
    19     * 根据id删除账户
    20     * 
    21     * @param id
    22     */

    23    public void deleteAccountById(int id) {
    24        getSqlMapClientTemplate().delete("Account.deleteAccountById", id);
    25    }

    26
    27    /**
    28     * 更新账户余额
    29     * 
    30     * @param account
    31     */

    32    public void updateAccount(Account account) {
    33        getSqlMapClientTemplate().update("Account.updateBalance", account);
    34    }

    35
    36    /**
    37     * 根据id查找账户
    38     * 
    39     * @param id
    40     * @return
    41     */

    42    public Account selectAccountById(int id) {
    43        return (Account) getSqlMapClientTemplate().queryForObject(
    44                "Account.selectAccountById", id);
    45    }

    46
    47    /**
    48     * 根据账户编号得到余额
    49     * 
    50     * @param idb
    51     * @return
    52     */

    53    public float getBalance(int idb) {
    54        return Float.parseFloat(getSqlMapClientTemplate().queryForObject(
    55                "Account.selectBalance", idb).toString());
    56    }

    57
    58    /**
    59     * 判断账户是否存在
    60     * 
    61     * @param ida
    62     * @return
    63     */

    64    public boolean isExists(int ida) {
    65        return (Integer) getSqlMapClientTemplate().queryForObject(
    66                "Account.selectCount", ida) > 0 ? true : false;
    67    }

    68}
  • 编写iBATIS的sql映射文件Account.xml
     1<?xml version="1.0" encoding="UTF-8"?>
     2<!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN" "http://ibatis.apache.org/dtd/sql-map-2.dtd" >
     3<sqlMap namespace="Account">
     4    <typeAlias alias="Account" type="com.aaron.atomikos.domain.Account" />
     5    <select id="selectAccountById" parameterClass="int" resultClass="Account">
     6        select * from Account where aid = #value#
     7    </select>
     8    
     9    <select id="selectBalance" parameterClass="int" resultClass="float">
    10        select balance from Account where aid = #value#
    11    </select>
    12    
    13    <select id="selectCount" parameterClass="int" resultClass="int">
    14        select count(*) from Account where aid = #value#
    15    </select>
    16    
    17    <update id="updateBalance" parameterClass="Account">
    18        update Account set balance = #balance# where aid = #accountId#
    19    </update>
    20    
    21    <insert id="insertAccount" parameterClass="Account">
    22        insert into Account (owner,balance) values (#owner#,#balance#);
    23    </insert>
    24</sqlMap>
    还要建立两个ibatis的配置文件sql-map-config_A.xml和sql-map-config_B.xml,由于有两个数据库,所以要建两个。具体不列出。
  • 编写spring的applicationContext.xml
      1<?xml version="1.0" encoding="UTF-8"?>
      2<beans xmlns="http://www.springframework.org/schema/beans"
      3    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      4    xmlns:jee="http://www.springframework.org/schema/jee"
      5    xmlns:aop="http://www.springframework.org/schema/aop"
      6    xmlns:tx="http://www.springframework.org/schema/tx"
      7    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd 
      8                     http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.0.xsd 
      9                     http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd 
     10                     http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
     11
     12    <!-- 读取数据库连接配置文件 -->
     13    <bean id="propertyConfig"
     14        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
     15        <property name="locations">
     16            <list>
     17                <value>classpath:jdbc.properties</value>
     18            </list>
     19        </property>
     20    </bean>
     21
     22    <!-- 数据源A(使用com.atomikos.jdbc.AtomikosDataSourceBean作为数据源) -->
     23    <bean id="datasourceA"
     24        class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init"
     25        destroy-method="close">
     26        <property name="uniqueResourceName">
     27            <value>mysql/db_a</value>
     28        </property>
     29        <property name="xaDataSourceClassName">
     30            <value>${jdbc.driverClassName}</value>
     31        </property>
     32        <property name="poolSize">
     33            <value>3</value>
     34        </property>
     35         <property name="xaProperties">
     36             <props>
     37                 <prop key="url">${jdbc.url}</prop>
     38                 <prop key="user">${jdbc.username}</prop>
     39                 <prop key="password">${jdbc.password}</prop>
     40             </props>
     41         </property>
     42    </bean>
     43
     44    <!-- 数据源B(使用com.atomikos.jdbc.SimpleDataSourceBean作为数据源) -->
     45    <bean id="datasourceB"
     46        class="com.atomikos.jdbc.SimpleDataSourceBean" init-method="init"
     47        destroy-method="close">
     48        <property name="uniqueResourceName">
     49            <value>mysql/db_b</value>
     50        </property>
     51        <property name="xaDataSourceClassName">
     52            <value>${jdbc2.driverClassName}</value>
     53        </property>
     54        <property name="xaDataSourceProperties">
     55            <value>user=${jdbc2.username};password=${jdbc2.password};url=${jdbc2.url};encoding=${jdbc2.encoding}</value>
     56        </property>
     57        <property name="exclusiveConnectionMode">
     58            <value>true</value>
     59        </property>
     60        <property name="validatingQuery">
     61            <value>SELECT 1</value>
     62        </property>
     63        <property name="connectionPoolSize">
     64            <value>3</value>
     65        </property>
     66    </bean>
     67
     68    <!-- 配置atomikos的事务管理器 -->
     69    <bean id="atomikosTransactionManager"
     70        class="com.atomikos.icatch.jta.UserTransactionManager"
     71        init-method="init" destroy-method="close">
     72        <property name="forceShutdown" value="true" />
     73    </bean>
     74
     75    <bean id="atomikosUserTransaction"
     76        class="com.atomikos.icatch.jta.UserTransactionImp">
     77        <property name="transactionTimeout" value="300" />
     78    </bean>
     79
     80    <!-- spring的JTA事务管理器 -->
     81    <bean id="springTransactionManager"
     82        class="org.springframework.transaction.jta.JtaTransactionManager">
     83        <property name="transactionManager"
     84            ref="atomikosTransactionManager" />
     85        <property name="userTransaction" ref="atomikosUserTransaction" />
     86    </bean>
     87
     88    <!-- 通知配置 -->
     89    <tx:advice id="txAdvice"
     90        transaction-manager="springTransactionManager">
     91        <tx:attributes>
     92            <tx:method name="delete*" rollback-for="Exception" />
     93            <tx:method name="save*" rollback-for="Exception" />
     94            <tx:method name="update*" rollback-for="Exception" />
     95            <tx:method name="*" read-only="true"
     96                rollback-for="Exception" />
     97        </tx:attributes>
     98    </tx:advice>
     99
    100    <!-- 事务切面配置 -->
    101    <aop:config>
    102        <aop:pointcut id="serviceOperation"
    103            expression="execution(* *..service*..*(..))" />
    104        <aop:advisor advice-ref="txAdvice"
    105            pointcut-ref="serviceOperation" />
    106    </aop:config>
    107
    108    <!-- 根据dataSourceA和sql-map-config_A.xml创建一个SqlMapClientA -->
    109    <bean id="sqlMapClientA"
    110        class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
    111        <property name="dataSource">
    112            <ref local="datasourceA" />
    113        </property>
    114        <property name="configLocation">
    115            <value>classpath:/sql-map-config_A.xml</value>
    116        </property>
    117    </bean>
    118
    119    <!-- 根据dataSourceB和sql-map-config_B.xml创建一个SqlMapClientB -->
    120    <bean id="sqlMapClientB"
    121        class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
    122        <property name="dataSource">
    123            <ref local="datasourceB" />
    124        </property>
    125        <property name="configLocation">
    126            <value>classpath:/sql-map-config_B.xml</value>
    127        </property>
    128    </bean>
    129
    130    <!--根据sqlMapClientA创建一个SqlMapClientTemplate的模版类实例sqlMapClientTemplateA-->
    131    <bean id="sqlMapClientTemplateA"
    132        class="org.springframework.orm.ibatis.SqlMapClientTemplate">
    133        <property name="sqlMapClient" ref="sqlMapClientA" />
    134    </bean>
    135
    136    <!--根据sqlMapClientB创建一个SqlMapClientTemplate的模版类实例sqlMapClientTemplateB-->
    137    <bean id="sqlMapClientTemplateB"
    138        class="org.springframework.orm.ibatis.SqlMapClientTemplate">
    139        <property name="sqlMapClient" ref="sqlMapClientB" />
    140    </bean>
    141
    142    <!-- 配置DAO -->
    143    <bean id="accountaDao" class="com.aaron.atomikos.dao.AccountDao">
    144        <property name="sqlMapClientTemplate"
    145            ref="sqlMapClientTemplateA" />
    146    </bean>
    147
    148    <bean id="accountbDao" class="com.aaron.atomikos.dao.AccountDao">
    149        <property name="sqlMapClientTemplate"
    150            ref="sqlMapClientTemplateB" />
    151    </bean>
    152
    153    <!-- 配置service -->
    154    <bean id="accountService"
    155        class="com.aaron.atomikos.service.impl.AccountServiceImpl">
    156        <property name="accountaDao" ref="accountaDao" />
    157        <property name="accountbDao" ref="accountbDao" />
    158    </bean>
    159</beans>
    在这里,我使用了两个不同类的数据源,只是为了测试,具体配置如上。两者设置属性时有点差别,数据源com.atomikos.jdbc.AtomikosDataSourceBean中设置url,user,password属性是用xaProperties这个属性,它是个Properties类型,所以要用<props>标签分开设置,数据源com.atomikos.jdbc.SimpleDataSourceBean中设置url,user,password属性使用xaDataSourceProperties这个属性,它是String类型,查看源代码可以知道,这个类会把xaDataSourceProperties按“;”进行分割,得到user=root,password=root等几个字符串,再将“=”两边的字符串分别作为属性名和属性值放入一个properties变量中。有一点要注意,
    1<value>user=${jdbc2.username};password=${jdbc2.password};url=${jdbc2.url};encoding=${jdbc2.encoding}</value>
    中的<value>和user=${jdbc2.username}之间不能换行,因为该类不会忽略换行,会把换行加上user当成一个字符串,之后会报错的。
  • 编写测试类AccountServiceTest
     1package com.aaron.atomikos.test;
     2
     3import org.springframework.context.ApplicationContext;
     4import org.springframework.context.support.ClassPathXmlApplicationContext;
     5import com.aaron.atomikos.service.AccountService;
     6import junit.framework.Assert;
     7import junit.framework.TestCase;
     8
     9public class AccountServiceTest extends TestCase {
    10
    11    protected static ApplicationContext             applicationContext;
    12    protected static AccountService accountService;
    13    static {
    14        applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
    15        accountService = (AccountService) applicationContext.getBean("accountService");
    16    }

    17    
    18    public void testTransfer(){
    19        int ida = 1;
    20        int idb = 1;
    21        Assert.assertNotNull(accountService.transfer(ida, idb, 666));
    22    }

    23}

  • 执行,查看数据库,成功!若将AccountserviceImpl中的抛出异常注释去掉,则会在两个数据库中回滚。
  • posted on 2010-01-29 13:34 Aaronlong31 阅读(3745) 评论(0)  编辑  收藏

    只有注册用户登录后才能发表评论。


    网站导航: