目录


利用 Dbutils 进行事务操作(以转账为例)

    我们只在dao层进行增删改查操作。切忌在dao层直接进行转账的业务逻辑 ;

    我们在services层开启一个连接,在连接上开启事务,然后在这个链接上进行多条sql操作 ;

·老方说,其实上面那样写,还是不优雅的;优雅的写法:spring事务管理或者使用 ThreadLocal 类 ;

                        (备注:经常听到spring的大名,很好奇spring到底是怎样的存在呢,现在还没学,很少好奇!)

转账实现方式(不优雅的方式)

dao层代码:

public class EmployeeDao1 {

    public void setConnection(Connection connection) {
this.connection = connection;
} private Connection connection; public EmployeeDao1() {
} public EmployeeDao1(Connection connection) {
this.connection = connection;
} public void update(Employee e) throws SQLException {
// 不要将连接池作为参数,这里我们要用事务
QueryRunner runner = new QueryRunner();
String sql = "update employee set money = ? where name = ?";
Object[] params = {e.getMoney(), e.getName()};
runner.update(connection, sql, params);
} public Employee find(int id) throws SQLException { QueryRunner runner = new QueryRunner();
String sql = "select * from employee where id =?";
Employee employee = runner.query(connection,sql,new BeanHandler<Employee>(Employee.class),id);
return employee;
} }

services层进行调用:

/**
* 转账的基础版本(不优雅的实现)
*
* @param sourceId 源账户的id
* @param targetId 目标账户的id
* @param money 转换的金额
*/
public void transaction1(int sourceId, int targetId, int money) throws SQLException {
Connection connection = null;
try {
// 获取连接
connection = JdbcDataSourceUtils.getConnection();
System.out.println(" --- "+connection);
// 开启事务
connection.setAutoCommit(false);
// 获取Dao层对象,将开启事务的连接 传递进去
EmployeeDao1 dao1 = EmployeeFactory.getDao1(connection);
// 查找用户
Employee s = dao1.find(sourceId);
Employee t = dao1.find(targetId);
// 转账逻辑 a减b增
s.setMoney(s.getMoney() - money);
t.setMoney(t.getMoney() + money);
// 进行转换
dao1.update(s);
dao1.update(t);
// 提交事务
connection.commit();
} finally {
connection.close();
}
}

我们可以看到,我们在调用dao层,需要传进去连接,还需要在连接上,开启事务,最后还有提交。每次进行类似的逻辑,都需要 重复 这样的逻辑代码。


ThreadLocal 类

    其实内部就是一个Map集合 ;

    ·ThreadLocal 类在 J2EE 中很重要,它可以在线程内共享数据 ;以后在services、dao层之间传递数据,就不需要再利用方法参数传递了

    利用方法参数,太死板了;我们就可以选择 ThreadLocal 类来完成了。用法:写一个工具类,内部维护着一个 ThreadLocl 对象;

    不要说什么域,存数据,我就问下,dao层有request对象吗,能取到域中数据吗。。。

    ·许多框架使用了这个类 ;

    ·以后学框架,不但要学会用法,也要学习下源码,实现原理。。

    ·现在我们学的时事务,是一个services里面的多个dao操作的事务。

    ·以后还要学习多个services为一个事务,就需要用到过滤器了,可以保证一次请求以内的所有操作,都在一个事务里面。

转账实现方式(优雅的方式)

dao层代码:(很简短)

public class EmployeeDao2 {

    public void update(Employee e) throws SQLException {
// 不要将连接池作为参数,这里我们要用事务
QueryRunner runner = new QueryRunner();
String sql = "update employee set money = ? where name = ?";
Object[] params = {e.getMoney(), e.getName()};
runner.update(JdbcDataSourceUtils.getConnection(), sql, params);
} public Employee find(int id) throws SQLException { QueryRunner runner = new QueryRunner();
String sql = "select * from employee where id =?";
Employee employee = runner.query(JdbcDataSourceUtils.getConnection(),sql,new BeanHandler<Employee>(Employee.class),id);
return employee;
} }

services层代码:

/**
* 转账逻辑 优雅的实习方式
* @param sourceId 源客户id
* @param targetId 目标客户id
* @param money 转账金额
* @throws SQLException
*/
public void transaction2(int sourceId, int targetId, int money) throws SQLException {
try{
// 在线程上绑定已经开启事务的连接
JdbcDataSourceUtils.startTransaction();
// 获取dao层实例
EmployeeDao2 dao2 = EmployeeFactory.getDao2() ;
// 查找用户
Employee s = dao2.find(sourceId) ;
Employee t = dao2.find(targetId) ;
// 转账逻辑 a减b增
s.setMoney(s.getMoney() - money);
t.setMoney(t.getMoney() + money);
// 进行转换
dao2.update(s);
dao2.update(t);
// 提交事务
JdbcDataSourceUtils.commitTransaction();
}finally {
JdbcDataSourceUtils.closeConnetion();
}
}

其中涉及到的工具类代码:(ThreadLoacl类的使用)

/**
* 获取连接,如若线程上已经绑定连接,则返回线程上绑定的连接,否则则从连接池中获取一个连接返回
*
* @return 连接
*/
public static Connection getConnection() {
// 先判断线程上是否有Connection
Connection connection = tl.get();
if (null == connection) {
try {
connection = dataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
return connection;
} /**
* 为当前线程绑定一个开启事务的连接
*/
public static void startTransaction() {
// 先判断线程上是否有Connection
// get 也是由执行这个方法的线程调用
Connection connection = tl.get();
if (null == connection) {
try {
connection = dataSource.getConnection();
tl.set(connection);
// 开启事务
connection.setAutoCommit(false);
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
} /**
* 提交事务
*/
public static void commitTransaction() {
try {
Connection connection = tl.get();
if (connection != null) {
connection.commit();
}
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
} /**
* 关闭连接
*/
public static void closeConnetion() {
try {
Connection connection = tl.get();
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
} finally {
// 哪一个线程调用这个方法,就按照线程为key去移除对应的Connection
tl.remove();
}
}
04-30 00:56