目录


利用 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. Android studio 导入项目报 Error:Cause: peer not authenticated 异常

    修改build.gradle文件(project级的) 一.dependencies { classpath 'com.android.tools.build:gradle:1.0.1'}将class ...

  2. 微信小程序之简单记账本开发记录(五)

    样式表和大致布局在昨天已构建好,今天完成页面结构部分 结果如下图所示

  3. 动手动脑(ppt中6个问题)

    问题一:仔细阅读示例: EnumTest.java,运行它,分析运行结果? public class EnumTest { public static void main(String[] args) ...

  4. 重读APUE(7)-link/unlink与mkdir/rmdir

    link–用于创建一个现有文件的链接:实际上是新建一个目录项,指向当前文件的i节点: unlink–用于删除一个现有文件的连接:实际上是对引用i节点的目录项进行删除,并且对链接计数-1:系统会检查文件 ...

  5. fastjson序列化和反序列化报com.alibaba.fastjson.JSONException: autoType is not support异常问题,解决方案整合

    1.问题起因 2017年3月15日,fastjson官方发布安全升级公告,该公告介绍fastjson在1.2.24及之前的版本存在代码执行漏洞,当恶意攻击者提交一个精心构造的序列化数据到服务端时,由于 ...

  6. 微信小程序倒计时的方法

    timeOut: function(time) { var that = this; var end = new Date(time).getTime(); var Interval = setInt ...

  7. SQL-W3School-高级:SQL IN 操作符

    ylbtech-SQL-W3School-高级:SQL IN 操作符 1.返回顶部 1. IN 操作符 IN 操作符允许我们在 WHERE 子句中规定多个值. SQL IN 语法 SELECT col ...

  8. shell生成指定范围随即整数

    #!/bin/bash function rand(){ min=$ max=$(($-$min+)) num=$( | cksum | awk -F ' ' '{print $1}') echo $ ...

  9. smarty {for}{forelse}

    {for} {for}{forelse}用于创建一个简单的循环. 下面的几种方式都是支持的: {for $var=$start to $end}步长1的简单循环. {for $var=$start t ...

  10. UC浏览器打开百度显示 丰臣吉秀.网址