/*
dao层定义AccountDao类,操作数据库
进账方法:
名称: in
参数: 连接对象,进账账户,进账金额
注意:连接对象,进账账户,进账金额由service层传递
实现步骤:
1.创建QueryRunner对象
2.定义sql语句
3.执行sql语句
出账方法:
名称: out
参数: 连接对象,出账账户,出账金额
注意:连接对象,出账账户,出账金额由service层传递
实现步骤:
1.创建QueryRunner对象
2.定义sql语句
3.执行sql语句
注意:
1.dao层不负责异常处理,有异常直接抛出
2.dao层不负责事务管理
*/
public class AccountDAO {
// 进账方法
public void inAccount(Connection con, String inName, double inMoney) throws Exception{
QueryRunner queryRunner = new QueryRunner();
String sql = "UPDATE account SET money = money + ? WHERE name = ?";
// 执行sql语句
queryRunner.update(con, sql, inMoney, inName);
}
//出账方法
public void outAccount(Connection con,String outName,double outMoney) throws SQLException {
//1.创建QueryRunner对象
QueryRunner qr = new QueryRunner();
//2.定义sql语句
String sql = "update account set money = money - ? where name = ?";
//3.执行sql语句
qr.update(con,sql,outMoney,outName);
}
/*
//这种做法是有问题的,因为in和out方法,并没有使用service层传递进来的Connection连接对象
//而是自己获取连接对象,导致service层和dao层使用的不是一个Connection对象,事务管理出现了问题
//进账方法
public void in(Connection con,String inName,double inMoney) throws SQLException {
//1.创建QueryRunner对象
QueryRunner qr = new QueryRunner();
//2.定义sql语句
String sql = "update account set money = money + ? where name = ?";
//3.执行sql语句
qr.update(C3P0Util.getConnection(),sql,inMoney,inName);
}
//出账方法
public void out(Connection con,String outName,double outMoney) throws SQLException {
//1.创建QueryRunner对象
QueryRunner qr = new QueryRunner();
//2.定义sql语句
String sql = "update account set money = money - ? where name = ?";
//3.执行sql语句
qr.update(C3P0Util.getConnection(),sql,outMoney,outName);
}
*/
}
/**
* service层定义AccountService类,操作dao层的AccountDao类
* 转账方法:
* 名称: transfer
* 参数: 进账账户,出账账户,转账金额
* 注意:连接对象,service层自己创建,进账账户,出账账户,转账金额由view层传递
*
* 步骤:
* 1.创建AccountDao类的对象
* 2.获取连接对象
* 3.开启事务
* 4.调用方法(进账/出账的方法)
* 5.方法正常执行,提交事务
* 6.方法执行出现问题,回滚事务
* 7.关闭资源
*
* 注意:
* 1.service层负责异常处理,有异常try-catch-finally
* 2.service层负责事务管理
*/
public class AccountService {
public void transfer(String inName, String outName, double transferMoney) {
Connection con = null;
try {
// 创建AccountDAO对象
AccountDAO accountDAO = new AccountDAO();
con = DruidUtil.getConnection();
// 开启事务
con.setAutoCommit(false);
// 调用方法(进账/出账)
accountDAO.inAccount(con, inName, transferMoney);
// 模拟异常
System.out.println(1 / 0);
// 调用出账方法
accountDAO.outAccount(con, outName, transferMoney);
// 提交事务
con.commit();
} catch (Exception e) {
e.printStackTrace();
// 方法执行出现问题, 回滚事务
if (con != null) {
try {
con.rollback();
} catch (Exception e1) {
e1.printStackTrace();
}
}
} finally {
DruidUtil.close(null, null, con);
}
}
}
/**
* 转账页面:
* 调用service层
*/
public class AccountPage {
public static void main(String[] args) {
String inName = "jerry";
String outName = "tom";
double transferMoney = 1000;
// 调用service层
AccountService service = new AccountService();
service.transfer(inName, outName, transferMoney);
}
}
view -> service -> dao
三层中都使用了同一个线程: main线程, 如果在这三层中, 使用同一个连接对象
就可以解决操作的是同一个Connection连接对象了。
正如上图我们分析的这样, ThreadLocal
内部使用的是Map集合, 一般Map集合有两个泛型 Map
, 但是ThreadLocal
只有一个泛型, 代表的是值的类型, 内部把键的类型已经规定为Thread, 也就是当前线程对象
作为键的值; 所以ThreadLocal
类泛型T就表示的是当前线程对象绑定的值的类型。
java.lang.ThreadLocal<T>
类
作用: 实现线程共享局部变量
内部使用Map集合,Map集合有2个泛型,K: 键的类型,V代表值的类型
ThreadLocal<T>类: 只有一个泛型,代表的是值的类型,内部把键的类型已经规定为了Thread
,使用当前线程对象,作为键的值所以ThreadLocal<T>类,泛型T表示的是给当前线程对象绑定的值的类型。
成员方法:
public void set(T t)
: 给当前线程对象,绑定一个T类型的变量t
相当于Map集合:
map.put(Thread.currentThread,t)
public T get()
: 获取当前线程对象上,绑定的值, 因为键已经在内部确定了(当前线程对象)
相当于Map集合:
map.get(Thread.currentThread)
public T remove()
: 删除当前线程对象上绑定的值
相当于Map集合:
map.remove(Thread.currentThread)
public class ThreadLocalDemo {
public static void main(String[] args) {
// 创建ThreadLocal对象
ThreadLocal<String> tl = new ThreadLocal<>();
// 获取当前线程对象绑定的值
String s = tl.get();
System.out.println(s); // null, 因为当前线程对象还没有设置value值呢,所以为null
// 给当前main线程对象(作为key),绑定值为"hello guizy"
tl.set("hello guizy");
s = tl.get();
System.out.println(s); // hello guizy
// 创建SubThread类的对象
SubThread st = new SubThread(tl);
// 开启线程
st.start();
}
}
public class SubThread extends Thread {
private ThreadLocal<String> tl;
public SubThread(ThreadLocal<String> tl) {
this.tl = tl;
}
@Override
public void run() {
// 获取当前线程对象上绑定的值
String str = tl.get();
System.out.println("run...." + str);
// 给当前线程对象(key),绑定值为 "sub thread"
tl.set("sub thread");
str = tl.get();
System.out.println("run...." + str);
}
}
跳转到目录
/**
* 连接Connection对象管理工具类
*/
public class ConnectionManager {
/**
* 借助ThreadLocal实现线程中局部变量的数据共享
*/
private static ThreadLocal<Connection> tl = new ThreadLocal();
private ConnectionManager() {
}
/**
* 定义静态方法,获取连接对象
* 此方法,保证在同一个线程中获取到的是同一个Connection对象; 当下次再获取连接的时候,此时该线程对象中已经有连接对象了.
*/
public static Connection getConnection() throws SQLException {
// 首先从ThreadLocal对象中获取Connection对象
Connection con = tl.get();
// 判断con是否为null
if (con == null) {
// 先从数据库连接池中获取一个连接对象
con = DruidUtil.getConnection();
// 绑定到ThreadLocal中
tl.set(con);
}
return con;
}
/*
定义开启事务的方法, 使用的都是同一个连接对象来操作事务
*/
public static void setAutoCommit() throws SQLException {
Connection con = getConnection();
con.setAutoCommit(false);
}
/*
定义提交事务的方法
*/
public static void commit() throws SQLException {
Connection con = getConnection();
con.commit();
}
/*
定义回滚事务的方法
*/
public static void rollback() throws SQLException {
Connection con = getConnection();
con.rollback();
//从ThreadLocal对象中移除
tl.remove();
}
}
跳转到目录
/*
dao层定义AccountDao类,操作数据库
进账方法:
名称: in
参数: 进账账户,进账金额
注意:进账账户,进账金额由service层传递
实现步骤:
1.创建QueryRunner对象
2.定义sql语句
3.执行sql语句
出账方法:
名称: out
参数: 出账账户,出账金额
注意: 出账账户,出账金额由service层传递
实现步骤:
1.创建QueryRunner对象
2.定义sql语句
3.执行sql语句
注意:
1.dao层不负责异常处理,有异常直接抛出
2.dao层不负责事务管理
3.连接对象从ConnectionManager工具类中获取,它保证同一个线程中获取到的是同一个连接对象
*/
public class AccountDao {
//进账方法
public void in(String inName,double inMoney) throws SQLException {
//1.创建QueryRunner对象
QueryRunner qr = new QueryRunner();
//2.定义sql语句
String sql = "update account set money = money + ? where name = ?";
//3.执行sql语句
qr.update(ConnectionManager.getConnection(),sql,inMoney,inName);
}
//出账方法
public void out(String outName,double outMoney) throws SQLException {
//1.创建QueryRunner对象
QueryRunner qr = new QueryRunner();
//2.定义sql语句
String sql = "update account set money = money - ? where name = ?";
//3.执行sql语句
qr.update(ConnectionManager.getConnection(),sql,outMoney,outName);
}
}
/*
service层定义AccountService类,操作dao层的AccountDao类
转账方法:
名称: transfer
参数: 进账账户,出账账户,转账金额
注意:进账账户,出账账户,转账金额由view层传递
连接对象,从ConnectionManager工具类中获取,它保证同一个线程中获取到的是同一个连接对象
步骤:
1.创建AccountDao类的对象
2.获取连接对象
3.开启事务
4.调用方法(进账/出账的方法)
5.方法正常执行,提交事务
6.方法执行出现问题,回滚事务
7.关闭资源
注意:
1.service层负责异常处理,有异常try-catch-finally
2.service层负责事务管理
*/
public class AccountService {
public void transfer(String inName,String outName,double transferMoney) {
Connection con = null;
try {
//1.创建AccountDao类的对象
AccountDao accountDao = new AccountDao();
//2.获取连接对象
con = ConnectionManager.getConnection();
//3.开启事务
ConnectionManager.setAutoCommit();
//4.调用方法(进账/出账的方法)
//调用进账方法
accountDao.in(inName,transferMoney);
// System.out.println(1/0);//出现异常
//调用出账方法
accountDao.out(outName,transferMoney);
//5.方法正常执行,提交事务
ConnectionManager.commit();
} catch (Exception e) {
e.printStackTrace();
//6.方法执行出现问题,回滚事务
try {
ConnectionManager.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
} finally {
//7.关闭资源
DruidUtil.close(null, null, con);
}
}
}
public class AccountPage {
public static void main(String[] args) {
String inName = "jerry";
String outName = "tom";
double transferMoney = 1000;
//调用service层
//创建AccountService类的对象
AccountService service = new AccountService();
//AccountService类的对象调用transfer方法,完成转账
service.transfer(inName,outName,transferMoney);
}
}