欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

模拟数据库转账事务,运用ThreadLocal获得一个当前线程,完成分层

程序员文章站 2022-03-23 17:00:20
...

Acount类,账户,与数据库一致
public class Account {
private int id;
@Override
public String toString() {
return “Account [id=” + id + “, name=” + name + “, money=” + money
+ “]”;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
private String name ;
private double money;
}
一个工具类C3pUtil
public class C3p0Util {
// 连接池可以极大的改善用户的 Java 应用程序的性能,同时减少全部资源的使用。连接池主要的优点有:
// 减少连接创建时间 虽然与其它数据库相比 GBase 提供了较为快速连接功能,但是创建新的 JDBC
// 连接仍会招致网络和 JDBC 驱动的开销。如果这类连接是“循环”使用的,使用该方式这些花销就可避免。
// 简化的编程模式 当使用连接池时,每一个单独的线程能够像创建了一个自己的 JDBC 连接一样操作,允许用
// 户直接使用JDBC编程技术。 受控的资源使用 如果用户不使用连接池,而是每当线程需要时创建一个新的连接,
// 那么用户的应用程序的资源使用会产生非常大的浪费并且可能会导致高负载下的异常发生。
// 配置了C3P0作为连接池子,调用服务器Connection连接时,在调用完了之后会将连接返还回去。

private static ComboPooledDataSource dataSource = new ComboPooledDataSource();

public static ComboPooledDataSource getDataSource() {
	return dataSource;
}

public static Connection getConnection(){
	try {		
		return dataSource.getConnection();
	} catch (SQLException e) {
		
		throw new RuntimeException("服务器繁忙!"); 
	}	
}

public static void closeAll(ResultSet rs,PreparedStatement ps,Connection conn){

	if(rs!=null){
		try {
			rs.close();
		} catch (SQLException e) {
		
			e.printStackTrace();
		}
		rs=null;
	}
	if(ps!=null){
		try {
			ps.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		ps=null;
	}
	if(conn!=null){
		try {
			conn.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		conn=null;
	}
}
/**数据库信息的配置XML**/
<?xml version="1.0" encoding="UTF-8"?>
com.microsoft.sqlserver.jdbc.SQLServerDriver jdbc:sqlserver://localhost:1433;DatabaseName=Account sa 123 10 30 100 10 200

AccountDao接口,
public interface AccountDao {
/得到一个账户/

public void updatedao(Account account) throws Exception;

/根据用户名返回账户/

public Account updatename(String name) throws Exception ;

}
实现接口的方法

public class AccountDaoimpl implements AccountDao {

public void updatedao(Account account) throws Exception {
	//根据账户进行更新
	QueryRunner qr=new QueryRunner(C3p0Util.getDataSource());
	qr.update( ManageThreadLocal.getConnection(),"update account set money=? where name=? ",account.getMoney(),account.getName());
}
public Account updatename(String name) throws Exception {
	//根据用户名查询,然后返回一个账户
	QueryRunner qr=new QueryRunner(C3p0Util.getDataSource());
	return qr.query(ManageThreadLocal.getConnection(),"select * from account where name=?",new BeanHandler<Account>(Account.class),name);
}

}
Server层的接口接口,输入转账人,收账人,金额
public interface AccountServer {

public void tranfer (String fromname,String toname,double money);

}
实现AccountServer的方法
public class AccountServerimpl implements AccountServer {

public void tranfer(String fromname, String toname, double money){
	//并将连接传到AccountDaoimpl,保持同一个事务连接
	AccountDao dao=new AccountDaoimpl();
	try {
	//开启事务
	ManageThreadLocal.starTransacation();
	//根据用户名得到一个转账人的账户
	Account fromAccount=dao.updatename(fromname);
	//根据用户名得到一个收账人的账户
	Account toAccount=dao.updatename(toname);
	//将得到转账人的账户金额减少转账的金额
	fromAccount.setMoney(fromAccount.getMoney()-money);
	//将获得收账人的收账金额加上现有的金额
	toAccount.setMoney(toAccount.getMoney()+money);
	if(fromAccount.getMoney()>=0){
		//更新转账人账户
		dao.updatedao(fromAccount);
		//更新收账人账户
		dao.updatedao(toAccount);
	}else{
		
	}
	//如果不发生异常,事务结束
	ManageThreadLocal.commit();
	} catch (Exception e) {
		try {
			//事务异常,则回滚事务
			ManageThreadLocal.rollback();
		} catch (Exception e1) {
			e1.printStackTrace();
		}
	}finally{
		try {
			//关闭连接,返回连接缓冲池
			ManageThreadLocal.release();
		} catch (Exception e) {
			e.printStackTrace();
		}	
	}
}

}
一个管理当前线程的类,作为工具类,获得当前的连接
public class ManageThreadLocal {

//得到一个管理数据库连接的线程,ThreadLocal<T>永远得到的是当前的连接
private static ThreadLocal<Connection>tl=new ThreadLocal<Connection>();
//得到当前连接线程的方法
public static Connection getConnection(){
	//得到线程的连接
	Connection conn=tl.get();
	//如果当前线程为空,从池中拿到一个链接,
	if(conn==null){
		conn=C3p0Util.getConnection();
		//放入当前线程中
		tl.set(conn);
	}

// 返回这个连接
return conn;
}
// 这是一个开启的事务的方法
public static void starTransacation(){
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 release(){
	try {
		//关闭当前连接的事务
		getConnection().close();
		//然后一定要移除当前的线程
		tl.remove();
	} catch (SQLException e) {
		e.printStackTrace();
	}
}

}
主函数,实现转账
public class TestTransfer {

public static void main(String[] args) throws Exception {
	AccountServer as=new AccountServerimpl();
	as.tranfer("JK", "tom", 90);
}

}