您的位置:首页 > 其它

Treadlocal的实现转账思想解读MVC

2016-09-23 17:23 225 查看
一、ThreadLocal是默认使用当前线程作为键(key)

  其实现原理如下:

public class ThreadLocal{
private Map<Runnable,Object> container = new HashMap<Runnable,Object>();
public void set(Object value){
container.put(Thread.currentThread(),value);//用当前线程作为key
}
public Object get(){
return container.get(Thread.currentThread());
}
public void remove(){
container.remove(Thread.currentThread());
}


 准备工作:

domain中有属性:int id;String name;int price

导入C3P0等各种包及对应的工具类

二、用普通的方法实现转账功能

     注:要保证使用的是同一个Connection对象

1、dao实现层

private Connection conn;

public AccountDaoImpl(Connection conn) {
this.conn = conn;
}

/**
* 更新账户余额的功能
*/
public void updateAccount(Account account) throws SQLException {
QueryRunner qr = new QueryRunner();
qr.update(conn, "update account set money=? where name=?",
account.getMoney(), account.getName());
}

/**
* 根据用户名查询用户信息的功能
*/
public Account findAccountByName(String name) throws SQLException {
QueryRunner qr = new QueryRunner();
// 返回一个Accout对象
return qr.query(conn, "select * from account where name=?",
new BeanHandler<Account>(Account.class), name);
}


2、service业务实现层

private AccountDao  accountDao;

public void transfer(String fromname, String toname, int money)  {
Connection conn = null;
try {			//注:一个sql语句就是一个事务,要把两个事务放在一起
conn = C3P0Util.getConnection();
//设置AccountDaoImpl使用的也是 这个conn
accountDao = new AccountDaoImpl(conn);

conn.setAutoCommit(false);
//得到转账人的信息
Account from = accountDao.findAccountByName(fromname);
//得到收钱人的信息
Account to = accountDao.findAccountByName(toname);

from.setMoney(from.getMoney()-money);
to.setMoney(to.getMoney()+money);

//转账--更新
accountDao.updateAccount(from);
int num = 5/0;
accountDao.updateAccount(to);

conn.commit();
} catch (Exception e) {
try {
conn.rollback();
e.printStackTrace();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
}


三、用ThreadLocal实现转账功能

1、dao层仅仅处理与数据库增删改查相关的sql语句,AccountDaoImpl代码实现如下:

/**
* 更新账户余额的功能
*/
public void updateAccount(Account account) throws SQLException {
QueryRunner qr = new QueryRunner();
qr.update(ManagerThreadLocal.getConnection(),"update account set money=? where name=?", account.getMoney(),account.getName());
}

/**
* 根据用户名查询用户信息的功能
*/
public Account findAccountByName(String name) throws SQLException {
QueryRunner qr = new QueryRunner();
return qr.query(ManagerThreadLocal.getConnection(),"select * from account where name=?", new BeanHandler<Account>(Account.class),name);
}

}


2.service层负责具体的业务逻辑,AccountServiceImpl层实现如下:

private AccountDao  accountDao = new AccountDaoImpl();

public void transfer(String fromname, String toname, int money)  {

try {		//注:一个sql语句就是一个事务,要把两个事务放在一起

//需要手动控制事务则是service 先得到Connection对象,否则是Dao先得到 Connection对象
ManagerThreadLocal.startTransaction();
//得到转账人的信息
Account from = accountDao.findAccountByName(fromname);
//得到收钱人的信息
Account to = accountDao.findAccountByName(toname);

from.setMoney(from.getMoney()-money);
to.setMoney(to.getMoney()+money);

//转账
accountDao.updateAccount(from);
//int num = 5/0;
accountDao.updateAccount(to);

ManagerThreadLocal.commitTrancsaction();
} catch (Exception e) {
try {	//有异常回滚事务
e.printStackTrace();
ManagerThreadLocal.roolBackTransaction();

} catch (SQLException e1) {
e1.printStackTrace();
}
}finally{
try {	//最后关闭资源
ManagerThreadLocal.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}


编写测试类:
AccountService accountService = new AccountServiceImpl();

accountService.transfer("王健健", "王思思", 100);
dao中QueryRunner实现时如果传入数据源对象(C3P0Util.getDataSource),属于自动控制事务。

dao中QueryRunner实现时如果没有传入数据源对象,而在query方法传入数据源对象,属于手动控制事务,例如转账这种特殊的业务需求,要用到ThreadLocal,即:多条sql语句的操作必须在同一个事务中,使用同一个Connection对象进行控制。否则两条执行的事务中间如果发生异常,第一个事务执行后第二个事务不会继续执行。

四、用代理实现-终极优化

为了使service层更加专注于业务处理,我们用此处使用代理。

新建工具类AccountServiceFactory

package com.zgf.util;

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

import com.qianfeng.service.AccountService;
import com.qianfeng.service.impl.AccountServiceImpl;

//创建AccountService代理对象的工厂类
public class AccountServiceFactory {

public static AccountService getAccountServiceProxy()
{

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 obj=null;

try {		//注:一个sql语句就是一个事务,要把两个事务放在一起

ManagerThreadLocal.startTransaction();
//代理执行transfer 代理的就是他
obj=method.invoke(as,args);//没有参数
ManagerThreadLocal.commitTrancsaction();

} catch (Exception e) {
try {	//有异常回滚事务
e.printStackTrace();
ManagerThreadLocal.roolBackTransaction();

} catch (SQLException e1) {
e1.printStackTrace();
}
}finally{
try {	//最后关闭资源
ManagerThreadLocal.close();
} catch (SQLException e) {
e.printStackTrace();
}
}

return obj;
}

});
return proxy;
}
}
这样在service实现层就可以简单的纯粹的业务处理
//得到转账人的信息
Account from = accountDao.findAccountByName(fromname);
//得到收钱人的信息
Account to = accountDao.findAccountByName(toname);

from.setMoney(from.getMoney()-money);
to.setMoney(to.getMoney()+money);

//转账
accountDao.updateAccount(from);
//int num = 5/0;
accountDao.updateAccount(to);在主函数中调用代理
AccountService proxy=AccountServiceFactory.getAccountServiceProxy();

proxy.transfer("lisi", "zhaosi", 100);

附:工具类下的ManagerThreadLocal

package com.zgf.util;

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

public class ManagerThreadLocal {

private static ThreadLocal<Connection> local = new ThreadLocal<Connection>();

public static Connection getConnection() throws SQLException
{
Connection conn = null;
conn = local.get();//从ThreadLocal中获取Connection 对象
if(conn==null){
conn = C3P0Util.getConnection();
local.set(conn);//把取到的Connection对象 放入ThreadLocal
}
return conn;
}

//开启事务
public static void startTransaction() throws SQLException
{
Connection conn = getConnection();
conn.setAutoCommit(false);
}

//提交事务
public static void commitTrancsaction() throws SQLException
{
getConnection().commit();
}
//回滚事务
public static void roolBackTransaction() throws SQLException
{
getConnection().rollback();
}

public static void close() throws SQLException
{
getConnection().close();//放回连接池
local.remove();//从ThreadLocal中删除
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息