目录


利用 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();
}
}

(二十四)JDBC应用的事务管理(转账事例)的更多相关文章

  1. 二十四 Redis消息订阅&事务&持久化

    Redis数据类型: Redis控制5种数据类型:String,list,hash,set,sorted-set 添加数据,删除数据,获取数据,查看有多少个元素,判断元素是否存在 key通用操作 JR ...

  2. VMware vSphere 服务器虚拟化之二十四 桌面虚拟化之手动池管理物理机

    VMware vSphere 服务器虚拟化之二十四 桌面虚拟化之手动池管理物理机 VMwareView手动池可以管理物理计算机 说明: 环境基于实验二十三 1.准备一台Windows 7的物理计算机名 ...

  3. [原]Jenkins(十四)---jenkins示例:admin管理所有项目,新建用户只能看部分项目

    /** * lihaibo * 文章内容都是根据自己工作情况实践得出. *如有错误,请指正 * 版权声明:本博客欢迎转发,但请保留原作者信息! http://www.cnblogs.com/horiz ...

  4. (C/C++学习笔记) 二十四. 知识补充

    二十四. 知识补充 ● 子类调用父类构造函数 ※ 为什么子类要调用父类的构造函数? 因为子类继承父类,会继承到父类中的数据,所以子类在进行对象初始化时,先调用父类的构造函数,这就是子类的实例化过程. ...

  5. 第三百二十四节,web爬虫,scrapy模块介绍与使用

    第三百二十四节,web爬虫,scrapy模块介绍与使用 Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架. 其可以应用在数据挖掘,信息处理或存储历史数据等一系列的程序中.其最初是为了 ...

  6. 创建JDBC模板简化代码、JDBC应用的事务管理以及连接池的作用

    一.创建JDBC模板简化代码 一个简单的查询.要做这么一大堆事情,并且还要处理异常,我们不防来梳理一下: 1.获取connection  2.获取statement  3.获取resultset  4 ...

  7. FreeSql (二十四)Linq To Sql 语法使用介绍

    原本不支持 IQueryable 主要出于使用习惯的考虑,如果继承 IQueryable,编写代码的智能总会提示出现一堆你不想使用的方法(对不起,我有强迫症),IQueryable 自身提供了一堆没法 ...

  8. 学习笔记:CentOS7学习之二十四:expect-正则表达式-sed-cut的使用

    目录 学习笔记:CentOS7学习之二十四:expect-正则表达式-sed-cut的使用 24.1 expect实现无交互登录 24.1.1 安装和使用expect 24.2 正则表达式的使用 24 ...

  9. 设计模式学习(二十四):Spring 中使用到的设计模式

    设计模式学习(二十四):Spring 中使用到的设计模式 作者:Grey 原文地址: 博客园:设计模式学习(二十四):Spring 中使用到的设计模式 CSDN:设计模式学习(二十四):Spring ...

  10. Bootstrap<基础二十四> 缩略图

    Bootstrap 缩略图.大多数站点都需要在网格中布局图像.视频.文本等.Bootstrap 通过缩略图为此提供了一种简便的方式.使用 Bootstrap 创建缩略图的步骤如下: 在图像周围添加带有 ...

随机推荐

  1. UOJ269. 【清华集训2016】如何优雅地求和 [生成函数]

    传送门 思路 神仙题.jpg 脑子一抽,想把\(f(x)\)表示成下降幂的形式,也就是 \[ f(x)=\sum_{i=0}^m f_ix_{(i)}\\ x_{(i)}=\prod_{k=0}^{i ...

  2. zabbix4.2升级后中文字体乱码解决方法.

    字体文件目录: zabbix 4.2 /usr/share/zabbix/assets/fonts/ 4.0 /usr/share/zabbix/fonts/ php 脚本文件位置: /usr/sha ...

  3. C语言学习笔记5-程序结构

    本系列文章由jadeshu编写,转载请注明出处.http://blog.csdn.net/jadeshu/article/details/50752148 作者:jadeshu   邮箱: jades ...

  4. ETL定义、四大模块及子系统说明

    ETL定义.四大模块及子系统说明 ——<Pentaho Kettle解决方案>读书笔记 罗小川 目前公司正在进行数据仓库的建设的前期需求整理和项目启动阶段,想简单来谈一下自己对目前公司在用 ...

  5. H5本地存储详解

    H5之前存储数据一般是通过 cookie ,但是 cookie 存的数据容量比较少.H5 中扩充了文件存储能力,可存储多达 5MB 的数据.现在就实际开发经验来对本地存储 ( Storage ) 的使 ...

  6. P5657 格雷码【民间数据】

    P5657 格雷码[民间数据] 题解 其实这题水啊 打表找规律 [1]0   1 [2]00   01  11  10 [3]000   001   011   010   110   111   1 ...

  7. [转][C#].Net反编译利器

    来自:https://www.cnblogs.com/zsuxiong/p/5117465.html 有以下8款非常不错的.Net反编译利器: 1.Reflector Reflector是最为流行的. ...

  8. dnspy使用技巧

    打开dnspy,调试–>附加到进程–>选择相应的进程ID–>附加(支持同时附加多个进程) 调试–>窗口–>模块–>搜索要调试的程序集–>双击(这一步很重要, ...

  9. C++ ++pos vs pos++

    list<char>::iterator pos; 一般使用前置式递增(preincrement),因为它比后置式递增(postincrement)效率高,因为后置式递增内部需要一个临时对 ...

  10. 阶段5 3.微服务项目【学成在线】_day02 CMS前端开发_15-webpack研究-webpack-dev-server-程序调试

    webpack把我们的js文件都打包了.所以不能用chrome的调试工具. 打包生成的js文件比较乱无法跟踪. 配置好了以后就可以让浏览器查看到打包后的源代码 在源代码这里加一个debuuger 这里 ...