转账业务(涉及事务、ThreadLocal、动态代理)

  • 转账业务是一种事务,所以需要保证数据的安全性。在转账中途如果出现错误,需要将数据进行回滚。
  • 本次示例用到了事务、ThreadLocal、动态代理等多个概念。
  • 本次实例需要用到的包如下:
lib/c3p0-0.9.1.2.jar c3p0需要对应的配置文件:c3p0-config.xml,其中配置了数据库连接、连接池大小等参数
lib/commons-dbutils-1.4.jar
lib/mysql-connector-java-5.0.8-bin.jar
  • 下面首先展示测试代码:
package test;

import service.AccountService;
import util.ObjectFactory;

public class TestTransfer {
    public static void main(String[] args) throws Exception {
        AccountService as = ObjectFactory.getAccountService();
        as.transfer("aaa", "bbb", 100);
    }
}
  • 接下来是实现转账业务的代码:
package service.impl;

import dao.AccountDao;
import dao.impl.AccountDaoImpl;
import domain.Account;
import service.AccountService;

public class AccountServiceImpl implements AccountService {
    /**
     * 实现转账功能
     * @param fromname 从该账户
     * @param toname   向该账户
     * @param money    转该数量的钱
     * @throws Exception
     */
    public void transfer(String fromname, String toname, double money) throws Exception {
        AccountDao ad = new AccountDaoImpl();

        //分别得到转出和转入账户对象
        Account fromAccount = ad.findAccountByName(fromname);
        Account toAccount = ad.findAccountByName(toname);

        //修改账户各自的金额
        fromAccount.setMoney(fromAccount.getMoney() - money);
        toAccount.setMoney(toAccount.getMoney() + money);

        //完成转账操作
        ad.updateAccout(fromAccount);
        ad.updateAccout(toAccount);
    }
}
  • 下面展示了实现与数据库操作的底层类:
package dao.impl;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;

import dao.AccountDao;
import domain.Account;
import util.ManagerThreadLocal;

public class AccountDaoImpl implements AccountDao {
    /**
     * 根据账户信息修改金额
     * @param account
     * @throws Exception
     */
    public void updateAccout(Account account) throws Exception {
        QueryRunner qr = new QueryRunner();
        qr.update(ManagerThreadLocal.getConnection(), "update account set money=? where name=?", account.getMoney(), account.getName());
    }

    /**
     * 根据用户名查找账户信息
     * @param name
     * @return
     * @throws Exception
     */
    public Account findAccountByName(String name) throws Exception {
        QueryRunner qr = new QueryRunner();
        return qr.query(ManagerThreadLocal.getConnection(), "select * from account where name=?", new BeanHandler<Account>(Account.class), name);
    }
}
  • 下面展示管理连接和事务的工具类,为低层数据库交互类和代理对象工厂类提供支持
package util;

import java.sql.Connection;
import java.sql.SQLException;

public class ManagerThreadLocal {
    /*
    将Connection的对象存储在ThreadLocal当中
    ThreadLocal中维护了一个Map,用来存储与线程相关的对象
    Map的键为线程,值为存储的对象
    所以,不同的线程对应的Connection会是不同的,而同一个线程也只有一个Connection
     */
    private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();

    //得到一个连接
    public static Connection getConnection() {
        Connection conn = tl.get();//从当前线程中取出一个连接
        if (conn == null) {//如果当前线程没有Connection被取出,说明当前还没有创建任何连接
            conn = C3P0Util.getConnection();//从池中取出一个Connection
            tl.set(conn);//把conn对象放入到当前线程对象中
        }
        return conn;
    }

    //开始事务
    public static void startTransacation() {
        try {
            Connection conn = getConnection();
            conn.setAutoCommit(false);//从当前线程对象中取出的连接,并开始事务
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public static void commit() {
        try {
            getConnection().commit();//提交事务
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public static void rollback() {
        try {
            getConnection().rollback();//回滚事务
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public static void close() {
        try {
            getConnection().close();//把连接放回池中
            tl.remove();//把当前线程对象中的conn移除
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}
  • 下面展示提供数据库连接的工具类,为管理连接的工具类提供支持
package util;

import java.sql.Connection;
import java.sql.SQLException;

import javax.sql.DataSource;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class C3P0Util {
    //得到一个数据源
    private static DataSource dataSource = new ComboPooledDataSource();

    public static DataSource getDataSource() {
        return dataSource;
    }

    //从数据源中得到一个连接对象
    public static Connection getConnection() {
        try {
            return dataSource.getConnection();
        } catch (SQLException e) {
            throw new RuntimeException("服务器错误");
        }
    }
}
  • 下面展示了提供动态代理对象的工厂类代码:
package util;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import service.AccountService;
import service.impl.AccountServiceImpl;

public class ObjectFactory {
    /*
    方法返回一个代理对象,是对AccountServiceImpl对象的动态代理
    在执行转账业务方法时,动态代理对象会在AccountServiceImpl中的方法执行之前和之后加上事务代码
    这样一来就会实现业务代码和事务代码的分离
     */
    public static AccountService getAccountService() {
        final AccountService as = new AccountServiceImpl();

        AccountService proxy = (AccountService) Proxy.newProxyInstance(as.getClass().getClassLoader(), as.getClass().getInterfaces(), new InvocationHandler() {

            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object invoke = null;
                try {
                    ManagerThreadLocal.startTransacation();//begin
                    //执行的是真实对象的转账方法
                    invoke = method.invoke(as, args);

                    ManagerThreadLocal.commit();//提交事务
                } catch (Exception e) {
                    try {
                        ManagerThreadLocal.rollback();//回滚事务
                    } catch (Exception e1) {
                        e1.printStackTrace();
                    }
                } finally {
                    try {
                        ManagerThreadLocal.close();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }//关闭
                }
                return invoke;
            }
        });
        return proxy;
    }
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值