`
cenphoenix
  • 浏览: 159016 次
  • 性别: Icon_minigender_1
  • 来自: 大连
社区版块
存档分类
最新评论

从ThreadLocal的使用到Spring的事务管理

    博客分类:
  • Java
阅读更多

 我写这篇文章的目的,为了使大家更好的理解和摸清事务的规律,希望对新手学习事务这块内容时有所帮助。 

   在我们开发一个应用时,很多时候我们的一个业务操作会对数据库进行多次操作,有时候我们需要保证这么一系列的操作要么全部成功,要么全部失败,其实这个这个概念就是我们今天要谈论的事务。 
   
   现在我们开发应用一般都采用三层结构,如果我们控制事务的代码都放在DAO(DataAccessObject)对象中,在DAO对象的每个方法当中去打开事务和关闭事务,当Service对象在调用DAO时,如果只调用一个DAO,那我们这样实现则效果不错,但往往我们的Service会调用一系列的DAO对数据库进行多次操作,那么,这个时候我们就无法控制事务的边界了,因为实际应用当中,我们的Service调用的DAO的个数是不确定的,可根据需求而变化,而且还可能出现Service调用Service的情况,看来手工来控制事务对于一个稍微严谨一点的系统来说完全是不现实的。 

   那么现在我们有什么好的解决办法吗?还记得EJB引以为傲的声明式事务吗,虽然它现在已经慢慢没落,但是它的思想被后人所吸取,我们的Spring框架是一个轻量级框架,它同样的实现了声明式事务的支持,使我们能够通过配置及可插拔的方式的完成整个应用的事务的管理。 

   
   谈到Sping事务,我们今天要说到的一个东东是ThreadLocal,早在JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。简单的说,ThreadLocal是为每个线程保存一份变量,各个线程访问自己对应的变量,所以我们就可以不使用synchronized关键字同样可以实现线程同步,要了解关于ThreadLocal的详细信息,请参看http://hi.baidu.com/cjjic02/blog/item/1ba41813aabde8886438dbe5.html 


为了简单明了,今天我们先抛开AOP,还是先用手工的方式通过ThreadLocal来管理连接,废话不多说,先来看代码 
TransactionHelper 

package com.hwadee.demo;  
  
import java.io.IOException;  
import java.io.InputStream;  
import java.sql.Connection;  
import java.sql.DriverManager;  
import java.sql.SQLException;  
import java.util.Properties;  
  
public final class TransactionHelper {  
      
    //使用ThreadLocal持有当前线程的数据库连接  
    private final static ThreadLocal<Connection> connection_holder = new ThreadLocal<Connection>();  
      
    //连接配置,来自connection.properties  
    private final static Properties connectionProp = new Properties();  
      
    static{       
        //加载配置文件  
        InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("connection.properties");  
        try {  
              
            connectionProp.load(is);  
            is.close();  
            //加载驱动程序  
            Class.forName(connectionProp.getProperty("driverClassName"));  
        } catch (IOException e) {  
             throw new RuntimeException(e.getMessage(),e);  
        }catch(ClassNotFoundException e){  
            throw new RuntimeException("驱动未找到",e);  
        }  
    }  
      
    //获取当前线程中的数据库连接  
    private static Connection getCurrentConnection()  
    {  
        Connection conn = connection_holder.get();  
        if(conn == null){  
            conn =  createNotAutoCommitConnection();              
            connection_holder.set(conn);  
        }  
        return conn;  
    }  
      
    //执行SQL语句  
    public static int executeNonQuery(String sql) throws SQLException{  
          
        Connection conn = getCurrentConnection();  
           
        return conn.createStatement().executeUpdate(sql);  
  
    }  
      
    //提交事务  
    public static void commit(){  
        Connection conn = getCurrentConnection();  
        try {  
            conn.commit();  
            conn.close();  
            connection_holder.set(null);  
        } catch (SQLException e) {  
            throw new RuntimeException(e.getMessage(),e);  
        }  
    }  
      
      
    //回滚事务  
    public static void rollback(){  
        Connection conn = getCurrentConnection();  
        try {  
            conn.rollback();  
            conn.close();  
            connection_holder.set(null);  
        } catch (SQLException e) {  
            throw new RuntimeException(e.getMessage(),e);  
        }  
    }  
      
    //创建一个不自动Commit的数据库连接  
    private static Connection createNotAutoCommitConnection() {  
        try {  
              
            Connection conn = DriverManager.getConnection(connectionProp.getProperty("url")+";databaseName="+ connectionProp.getProperty("databaseName")  
                    ,connectionProp.getProperty("username")  
                    ,connectionProp.getProperty("password"));  
            conn.setAutoCommit(false);  
            return conn;  
        } catch (SQLException e) {  
             throw new RuntimeException(e.getMessage(),e);  
        }  
    }     
}  

 

这个类实现了基本的连接管理与执行SQL语句的方法,可以在多线程环境下运行 


程序入口 

package com.hwadee.demo;  
  
import java.sql.SQLException;  
  
public class MainModule {  
      
    public static void main(String[] args) {          
        try{  
              
            insert1();  
              
            insert2();  
              
            //方法1和2都无异常,提交事务,任何一个方法出现异常都将导致事务回滚。  
            TransactionHelper.commit();  
        }catch(SQLException e){           
            TransactionHelper.rollback();  
            throw new RuntimeException(e.getMessage(),e);  
        }catch(RuntimeException e){            
            TransactionHelper.rollback();  
            throw new RuntimeException(e.getMessage(),e);  
        }  
    }  
      
      
    static void insert1() throws SQLException{        
        String sql = "insert into department values(1,'市场部')";  
          
        TransactionHelper.executeNonQuery(sql);        
    }  
      
    static void insert2() throws SQLException{        
        String sql = "insert into department values(2,'研发部')";  
          
        TransactionHelper.executeNonQuery(sql);   
          
        //throw new RuntimeException("回滚");       
    }  
}  

 连接字符串配置,请将此文件放入classpath根目录中 
connection.properties 

url=jdbc:sqlserver://localhost:1433  
databaseName=pubs  
username=sa  
password=password  
driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver  

 建表语句 

USE [pubs]  
go  
CREATE TABLE [Department](  
    [DEPT_ID] [int] primary key,  
    [DEPT_NAME] [varchar](50)  
)  
GO  

 好了现在运行这个应用,可以正常的插入两条数据,接下来,取消insert2方法里面的注释,再运行看看效果。 

static void insert2() throws SQLException{        
        String sql = "insert into department values(2,'研发部')";  
          
        TransactionHelper.executeNonQuery(sql);   
          
        throw new RuntimeException("回滚");         
    }  

 很重要的一点是要想实现事务,我们必须用同一个数据库连接执行这些语句,最终才能做到统一的提交和回滚。 
我们可以这样假设 
insert1和insert2为不同DAO的方法 
仔细观察,我们的insert1和insert2并没有负责打开连接和关闭连接。而是间接的调用TransactionHelper.executeNonQuery(sql); 
这样使我们执行的所有方法都是使用同一个连接进行数据库操作。 

   其实这个例子只是想告诉大家要实现声明式事务的一部分内容,这个例子只能实现简单的单事务模型,要实现更复杂的事务传播模型如嵌套等,还需要我们使用更多的技术,如AOP等等。先写到这里,希望对大家有所帮助!

 

 

 

分享到:
评论

相关推荐

    Spring事务处理-ThreadLocal的使用

    NULL 博文链接:https://yizhenn.iteye.com/blog/2293339

    Spring基于ThreadLocal的“资源-事务”线程绑定设计的缘起

    题目起的有些拗口了,简单说,这篇文章想要解释Spring为什么会选择使用ThreadLocal将资源和事务绑定到线程上,这背后有着什么样的起因和设计动机,通过分析帮助大家更清晰地认识Spring的线程绑定机制。访问任何带有...

    Spring 2.0 开发参考手册

    9.9.1. 对一个特定的 DataSource 使用错误的事务管理器 9.10. 更多的资源 10. DAO支持 10.1. 简介 10.2. 一致的异常层次 10.3. 一致的DAO支持抽象类 11. 使用JDBC进行数据访问 11.1. 简介 11.1.1. Spring ...

    Spring-Reference_zh_CN(Spring中文参考手册)

    9.9.1. 对一个特定的 DataSource 使用错误的事务管理器 9.10. 更多的资源 10. DAO支持 10.1. 简介 10.2. 一致的异常层次 10.3. 一致的DAO支持抽象类 11. 使用JDBC进行数据访问 11.1. 简介 11.1.1. Spring JDBC包结构...

    高级开发spring面试题和答案.pdf

    spring 三种注入(就是从spring容器中将bean放入对象属性值中) Spring下描述依赖关系@Resource, @Autowired和@Inject的区别与联系 Spring中BeanFactory和ApplicationContext的区别 谈谈Spring IOC的理解,原理与...

    Spring.3.x企业应用开发实战(完整版).part2

    10.5.1 Spring事务管理器的应对 10.5.2 Hibernate+Spring JDBC混合框架的事务管理 10.6 特殊方法成漏网之鱼 10.6.1 哪些方法不能实施Spring AOP事务 10.6.2 事务增强遗漏实例 10.7 数据连接泄漏 10.7.1 底层连接资源...

    Spring中文帮助文档

    9.9.1. 对一个特定的 DataSource 使用了错误的事务管理器 9.10. 更多的资源 10. DAO支持 10.1. 简介 10.2. 一致的异常层次 10.3. 一致的DAO支持抽象类 11. 使用JDBC进行数据访问 11.1. 简介 11.1.1. 选择一...

    spring chm文档

    9.9.1. 对一个特定的 DataSource 使用错误的事务管理器 9.10. 更多的资源 10. DAO支持 10.1. 简介 10.2. 一致的异常层次 10.3. 一致的DAO支持抽象类 11. 使用JDBC进行数据访问 11.1. 简介 11.1.1. Spring ...

    Spring API

    9.9.1. 对一个特定的 DataSource 使用了错误的事务管理器 9.10. 更多的资源 10. DAO支持 10.1. 简介 10.2. 一致的异常层次 10.3. 一致的DAO支持抽象类 11. 使用JDBC进行数据访问 11.1. 简介 11.1.1. 选择一种...

    Spring3.x企业应用开发实战(完整版) part1

    10.5.1 Spring事务管理器的应对 10.5.2 Hibernate+Spring JDBC混合框架的事务管理 10.6 特殊方法成漏网之鱼 10.6.1 哪些方法不能实施Spring AOP事务 10.6.2 事务增强遗漏实例 10.7 数据连接泄漏 10.7.1 底层连接资源...

    fat:基于springboot,zookeeper,redis分布式事务强一致性方案

    脂肪FAT,基于springboot,使用zookeeper,redis,spring异步,spring transactionManager的强一致性分布式事务解决方案框架介绍纯编码方式,强一致性。使用redis / zookeeper作为注册中心,代理事务的执行,使用...

    架构探险 从零开始写javaweb框架.pdf

    为了使框架具备 AOP 特性,从代理技术讲到 AOP 技术,从 ThreadLocal 技术讲到事务控制技术。*后对框架进行优化与扩展,通过对现有框架的优化,使其可以提供更加完备的功能,并以扩展 Web 服务插件与安全控制插件为...

    JAVA面试常见问题整理

    JAVA面试常见问题整理 本文主要概述了Java面试中常见...同时,还涉及了事务处理的并发问题、Spring事务的实现方式和隔离级别等相关知识。 总之,本文为读者提供了一个关于Java面试常见问题及内容的概述,帮助读者更好

    J2EE开发全程实录(JAVA项目开发)

    9.3.9 改造Spring事务配置方式... 172 9.4 会话服务的生命周期管理... 175 9.5 IValueObject接口... 178 第10章 层间数据传输.... 180 10.1 什么是DTO.. 180 10.2 域DTO.. 181 10.3 定制DTO.. 186 10.4 数据传送哈希...

    leetcode下载-JavaTopic:Java面试题总结

    spring事务7种传播特性和隔离级别的理解? spring boot的启动过程 spring事务实现的原理 aop切面 如何解决spring循环依赖问题? Redis篇: redis持久化的原理(RDB、AOF); redis缓存穿透、缓存雪崩,有没有在实际...

    经典JAVA.EE企业应用实战.基于WEBLOGIC_JBOSS的JSF_EJB3_JPA整合开发.pdf

    第4章 利用JDBC和JTA访问 数据库和管理全局事务 178 4.1 JDBC和容器管理的数据源 179 4.1.1 JDBC概述 179 4.1.2 使用JDBC执行数据库访问 180 4.1.3 使用WebLogic服务器管理的 数据源 182 4.1.4 使用JBoss服务器管理...

    免费分享 Java面试笔记 面试八股文 计算机网络基础

    本内容属于免费分享,如有积分变动请评论联系;...Spring:IOC、AOP、声明式事务、MVC等;Redis:持久化过程、高可用实现、缓存设计、应用场境等。 所有文档都是md格式,方便阅读,图文并茂便于理解。

    Java常见面试题208道.docx

    面试题包括以下十九部分:Java 基础、容器、多线程、反射、对象拷贝、Java Web 模块、异常、网络、设计模式、Spring/Spring MVC、Spring Boot/Spring Cloud、Hibernate、Mybatis、RabbitMQ、Kafka、Zookeeper、MySql...

    史上最全java面试,103项重点知识,带目录

    98. spring 事务实现方式有哪些? 59 99. 说一下 spring 的事务隔离? 59 100. 说一下 spring mvc 运行流程? 60 101. spring mvc 有哪些组件? 61 102. @RequestMapping 的作用是什么? 62 103. @Autowired 的作用...

Global site tag (gtag.js) - Google Analytics