1. DAO 事务

// 在 DAO 中处理事务真是"小菜一碟"

public void xxx(){
Connection con = null;
try{
con = JdbcUtils.getConnection();
con.setAutoCommit(false); // 开启事务 QueryRunner qr = new QueryRunner();
String sql = ...;
Object[] params = ...;
qr.update(con,sql,params); sql = ...;
Object[] params = ...;
qr.update(con,sql,params);
con.commit(); // 提交事务
} catch(Exception e){
try{
// 回滚事务
if(con != null) {con.rollback();}
}catch(Exception e){} }finally{
try{
con.close();
}catch(Exception e){}
}
}

2. Service 才是处理事务的地方

  • DAO 中不是处理事务的地方,因为 DAO 中的每个方法都是对数据库的一次操作, 而 Service 中的方法才是

    对应一个业务逻辑,也就是我们需要在 Service 中的一方法中调用 DAO 的多个方法,而这些方法应该在一个

    事务中.
// 事务需要保证为同一个 Connection
// 可以通过向 DAO 中传递 Connection, 来保证 DAO 的多个方法使用相同的 Connection public class XXXService(){
private XXXDao dao = new XXXDao();
public void serviceMethod(){ // 但是 Connection 对象只能出现在 DAO 中, 因为它是 JDBC 的东西,
// JDBC 的东西是用来连接数据库的, 连接数据库是由 DAO负责, 而事务却
// 应该由 Service 负责. Connection con = null;
try{
con = JdbcUtils.getConnection();
con.setAutoCommit(false); // 向 DAO 中传递 Connection
dao.daoMethod2(con,...);
dao.datMethod3(con,...);
con.commit();
}catch(Exception e){
try{
con.rollback();
} catch(Exception e){}
}finally{
try{
con.close();
}catch(Exception e){}
}
}
}

3. 修改 JdbcUtils

  1. 把对事物的开启和关闭放到 JdbcUtils 中,在 Service 中调用 JdbcUtils 的方法来完成事务的处理,

    但在 Service 中就不会再出现 Connection 了.
  2. DAO 中的方法不用再让 Service 来传递 Connection 了, DAO 会主动从 JdbcUtils 中获取 Connection

    对象, 这样, JdbcUtils 成为了 DAO 和 Service 的中介!

// Service 中的代码
public class XXXService(){
private XXXDao dao = new XXXDao();
public void serviceMethod(){
try{
JdbcUtils.beginTransaction();
dao.daoMethod2();
dao.daoMethod3();
JdbcUtils.commitTransaction();
}catch(Exception e){
JdbcUtils.rollbackTransaction();
}
}
} // JdbcUtils 代码 public class JdbcUtils{
private static ComboPooledDataSource dataSource = new ComboPooledDataSource(); // 它是事务专用连接, 并且每个线程分配一个Connection
private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>(); public static Connection getConnection() throws SQLException{ Connection con = tl.get(); // 获取当前线程的 con // 当 con 不等于 null, 说明已经调用过 beginTransaction() 方法了.表示开启了事务.
if(con != null) return con;
return dataSource.getConnection();
} public static DataSource getDataSource(){
return dataSource();
} // 添加开启事务的方法
// 获取一个 Connection, 设置它的 setAutoCommit(false)
// 还要保证 DAO 中使用的连接是我们刚刚创建的!! /*
* 1. 创建一个 Connection, 设置为手动提交
* 2. 把这个 Connection 给 DAO 用!
* 3. 还要让 commitTransaction 或 rollbackTransaction 可以获取到!!
*/
public static void beginTransaction() throws SQLException { Connection con = tl.get(); if(con != null) throw new SQLException("已经开启了事务,就不要重复开启了!"); con = getConnection(); // 给 con 赋值, 表示事务已经开启了.
con.setAutoCommit(false); tl.set(con); // 把当前线程的连接保存起来.
} // 添加提交事务的方法
// 获取 beginTransaction 提供的 Connection, 然后调用 commit 方法
public static void commitTransaction() throws SQLException { Connection con = tl.get(); // 获取当前线程的专用连接 if(con == null) throw new SQLException("还没有开启事务,不能提交!"); con.commit();
con.close();
// 把它设置为 null, 表示事务已经结束了.
// 下次再去调用 getconnection(),返回的就不是 con 了.
// con = null; 因为有了线程, 所以将 con 直接从线程中移除即可 tl.remove(); // 从 tl 中移除连接
} // 添加回滚事务的方法
// 获取 beginTransaction 提供的 Connection, 然后调用 rollback 方法.
public static void rollbackTransaction() throws SQLException { Connection con = tl.get(); // 获取当前线程的专用连接 if(con == null) throw new SQLException("还没有开启事务,不能回滚!"); con.rollback();
con.close(); tl.remove();
} // 释放连接
public static void releaseConnection(Connection con) throws SQLException{ Connection con = tl.get(); // 获取线程中的事务 // 判断 connection 是不是事务专用, 如果是, 就不关闭
// 如果不是事务专用, 那么就要关闭
// con 是事务专用连接, 如果 con 为 null,表示没有事务.
// 那么, connection 肯定不是事务专用的.
if(con == null) connection.close(); // 如果 con != null, 说明有事务,那么需要判断参数连接是否与 con 相等,
// 如果不相等, 说明 connection 不是事务专用连接.
if(con != connection) connection.close(); }
} // DAO 层代码
public class AccountDao{
public void update(String name, double money) throws SQLException{
QueryRunner qr = new QueryRunner();
String sql = "UPDATE account SET balance=balance+? WHERE name=?";
Object[] params = {money,name}; // 我们需要自己来提供连接, 保证在同一事务中, 多次调用使用的是同一个连接!!
Connection con = JdbcUtils.getConnection();
qr.update(con,sql,params); // 关闭连接
JdbcUtils.releaseConnection(connection);
}
}

参考资料:

Service 事务(JdbcUtils 升级)的更多相关文章

  1. 对于在Dao层,一个DML操作一个事务,升级到Service层,一个用户,一个事务

    原先的连接Connection,只能是来一次,新创建一个连接connection.这样如果事务在Dao层已经默认提交,在service层出错时,对于俩张关联会有俩种不同的结果.为了解决这样的问题,我们 ...

  2. Android入门(十二)SQLite事务、升级数据库

    原文链接:http://www.orlion.ga/610/ 一.事务 SQLite支持事务,看一下Android如何使用事务:比如 Book表中的数据都已经很老了,现在准备全部废弃掉替换成新数据,可 ...

  3. 高并发秒杀系统--Service事务管理与继承测试

    [Spring IoC的类型及应用场景]  [Spring事务使用方式] [Spring事务的特性] [Spring事务回滚的理解] [Service声明式事务的配置] 1.配置事务管理器 2.配置基 ...

  4. windows service 2008 R2 升级 sp1遇到的问题

    因为我的程序是以vs2015开发的,所以在在布署windows service 2008 R2 项目的时候报出 红框里的错,说明要安装.net framework4.6. 感觉so easy,下载一个 ...

  5. spring service事务传播

    spring定义的事务行为有以下几种: REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务.这是最常见的选择. SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行. ...

  6. windows service 2008 R2 安装net4.6环境失败,windows service 2008 R2 升级sp1问题

    一.错误 1.因为我的程序是以vs2017开发的,在windows service 2008 R2  IIS部署项目文件报出错误,因此要安装net4.6的环境. 2.windows service 2 ...

  7. sql service 事务与锁

    了解事务和锁 事务:保持逻辑数据一致性与可恢复性,必不可少的利器. 锁:多用户访问同一数据库资源时,对访问的先后次序权限管理的一种机制,没有他事务或许将会一塌糊涂,不能保证数据的安全正确读写. 死锁: ...

  8. ThreadLocal来管理事务

    ThreadLocal (扩展) 1 ThreadLocal API ThreadLocal类只有三个方法: l  void set(T value):保存值: l  T get():获取值: l  ...

  9. 超全面的JavaWeb笔记day19<Service>

    今日内容 l Service事务 l 客户关系管理系统 Service事务 在Service中使用ThreadLocal来完成事务,为将来学习Spring事务打基础! 1 DAO中的事务 在DAO中处 ...

随机推荐

  1. spring-boot-redis-cluster简单整合例子

    代码地址如下:http://www.demodashi.com/demo/13184.html 一.前言 spring-boot项目整合redis很常见,Redis 一般上生产的时候都是以集群模式部署 ...

  2. 【精】iOS GCD 具体解释

    一.介绍 1.什么是GCD? Grand Central Dispatch.是苹果公司开发的一套多核编程的底层API. GCD首次公布在Mac OS X 10.6,iOS4及以上也可用.GCD存在于l ...

  3. gm picture

    console.log("ok") /*var images = require("images");var fs = require("fs&quo ...

  4. MySql(一):linux 安装mysql数据库——yum安装法

    mysql数据库有多种安装方式,本文只介绍在Linux服务器上最实用.最快捷的mysql server安装方法.一.Linux服务器yum安装(CentOS6.3 64位)所有在服务器上执行的命令,都 ...

  5. Creating Dialogbased Windows Application (3) / 创建基于对话框的Windows应用程序(三)Checkbox的应用、窗体置顶、设置图标 / VC++, Windows

    创建基于对话框的Windows应用程序(三) —— Checkbox的应用.窗体置顶.设置图标 上一节创建的窗体应用程序中,我们用到了Button和StaticText这两个控件.这一节中我们将学习使 ...

  6. Introdution to Spring Mobile

    1. In Eclipse, create a new Maven Project using the spring-mvc-jpa-archetype. 2. Add the spring-mobi ...

  7. driver基础

    驱动测试时,linux驱动常以模块方式插入内核.应包含头文件:linux/kernel.h,linux/module.h 设备驱动的一般结构:Soc(主芯片->设备控制器->外设引脚)-- ...

  8. 青蛙的约会 扩展欧几里得 方程ax+by=c的整数解 一个跑道长为周长为L米,两只青蛙初始位置为x,y;(x!=y,同时逆时针运动,每一次运动分别为m,n米;问第几次运动后相遇,即在同一位置。

    /** 题目:青蛙的约会 链接:https://vjudge.net/contest/154246#problem/R 题意:一个跑道长为周长为L米,两只青蛙初始位置为x,y:(x!=y,同时逆时针运 ...

  9. nginx源代码分析--nginx模块解析

    nginx的模块很之多.能够觉得全部代码都是以模块的形式组织.这包含核心模块和功能模块,针对不同的应用场合.并不是全部的功能模块都要被用到,附录A给出的是默认configure(即简单的httpser ...

  10. Hibernate每个子类一张表(使用注释)实例

    在每个子类一张表的情况下,表是根据持久类创建的,但是它们使用主键和外键来重新定义. 所以关系中不会有重复的列. 我们需要在子类中的使用@PrimaryKeyJoinColumn注释和在父类指定@Inh ...