通过AoacheDbUtile模拟转账功能
- 创建两个工具类JDBCUtile DataSourceUtile
JDBCUtile:用来管理事务和Connection对象
package cn.kz.utile;
import java.sql.Connection;
public class JDBCUtile {
// 可以防止并发且事务在同一次连接
// 原理ThreadLocal在获取对象的时候是通过创建对象 的副本
// 好处:不用创建多个Connection对象就可以使用 且 达到事务在同一个连接下
private static ThreadLocal<Connection> threadLocal = new ThreadLocal();
// 获取连接的方法
public static Connection getConnection() throws Exception {
Connection connection = threadLocal.get();
// 如果从threadLocal中获取不到副本 则新建一个Connection对象
if (connection == null) {
connection = DataSourceUtle.getDataSource().getConnection();
threadLocal.set(connection);
}
return connection;
}
// 开启事务
public static void beginTransaction() throws Exception {
Connection connection = getConnection();
connection.setAutoCommit(false);// 开启事务
}
// 正常。提交事务
public static void commitTransaction() throws Exception {
// 连接
Connection connection = getConnection();
if (connection != null) {
connection.commit();
}
}
// 失败。回滚事务
public static void rollbackTransaction() throws Exception {
// 连接
Connection connection = getConnection();
if (connection != null) {
connection.rollback();
}
}
//关闭连接
public static void close() throws Exception {
// 连接
Connection connection = getConnection();
if (connection != null) {
connection.close();
}
threadLocal.remove();
connection = null;
}
}
DataSourceUtile:用来获取DataSource对象
package cn.kz.utile;
import org.apache.commons.dbcp.BasicDataSource;
import javax.sql.DataSource;
public class DataSourceUtle {
// dbcp连接池
public static DataSource getDataSource(){
BasicDataSource dataSource = new BasicDataSource();
dataSource.setUrl("jdbc:mysql://localhost:3306/kz?serverTimezone=Hongkong");
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setPassword("123456");
dataSource.setUsername("root");
return dataSource;
}
}
- 创建一个Account bean把数据库返回的值存到该对象
package cn.kz.bean;
public class Account {
private int id;
private String name;
private long balance;
// 有参构造器
public Account(int id, String name, long balance) {
this.id = id;
this.name = name;
this.balance = balance;
}
// 无参构造器
public Account() {
}
// setter getter方法
- AccountDaoImpl dao层封装无逻辑的 查询和修改 数据库操作
package cn.kz.dao;
import cn.kz.bean.Account;
import cn.kz.utile.JDBCUtile;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import java.sql.Connection;
public class AccountDaoImpl{
// 通过银行卡id来查询 并返回一个Account对象
public Account queryAccountByCard(int cardId) throws Exception {
QueryRunner runner = new QueryRunner();
Connection connection = JDBCUtile.getConnection();
Account account= runner.query(connection,
"select * from account where id = ?",
new BeanHandler<Account>(Account.class),cardId);
return account;
}
// 修改数据库的账户信息 传入一个Account对象
public void updataAccount(Account account) throws Exception{
QueryRunner runner = new QueryRunner();
Connection connection = JDBCUtile.getConnection();
runner.update(connection,
"update account set balance = ? where id = ?",
new Object[]{account.getBalance(),
account.getId()});
}
}
- service层 通过逻辑来处理转账
package cn.kz.service;
import cn.kz.bean.Account;
import cn.kz.dao.AccountDaoImpl;
import cn.kz.utile.JDBCUtile;
public class AccountServiceImpl{
/**
*
* @param fromCardId 付款方的id
* @param toCardId 收款方的id
* @param money 转账的金额
*/
public void transfer(int fromCardId, int toCardId, int money) {
AccountDaoImpl accountDao = new AccountDaoImpl();
// 开启事务
try {
JDBCUtile.beginTransaction();
// 各种dml操作
// a -1000 b +1000
// 根据cardId查询对应的账户
Account fromAccount = accountDao.queryAccountByCard(fromCardId);
Account toAccount = accountDao.queryAccountByCard(toCardId);
// 转账 先判断付款方余额是否大于转账金额(判断付款方的钱够不够转)
if (fromAccount.getBalance() > money) {
// 付款方-1000
long fromBalance = fromAccount.getBalance() - money;
fromAccount.setBalance(fromBalance);
// 收款方+1000
long toBalance = toAccount.getBalance() + money;
toAccount.setBalance(toBalance);
accountDao.updataAccount(fromAccount);
accountDao.updataAccount(toAccount);
// 正常提交事务
JDBCUtile.commitTransaction();
System.out.println("转账成功~~~");
} else {
System.out.println("余额不足~~~");
}
} catch (Exception e) {
e.printStackTrace();
System.out.println("转账失败~~~");
try {
JDBCUtile.rollbackTransaction();
} catch (Exception ex) {
ex.printStackTrace();
}
} finally {
try {
JDBCUtile.close();
} catch (Exception e) {
e.printStackTrace();
}
}
//结束事务(正常,失败)
}
}
- 测试
package cn.kz.test;
import cn.kz.service.AccountService;
import cn.kz.service.AccountServiceImpl;
public class Test {
public static void main(String[] args) {
// 尝试转账
AccountService service = new AccountServiceImpl();
service.transfer(1,2,2000);
}
}
原来的数据
代码执行后