`
cenphoenix
  • 浏览: 160547 次
  • 性别: 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的使用

    在Spring事务管理中,连接池与ThreadLocal结合,确保每个线程在事务内使用的是同一连接,避免了事务间的干扰。例如,`HikariCP`和`C3P0`等流行的连接池实现都支持与Spring事务管理的无缝集成。 在实际应用中,理解...

    java事务 - threadlocal

    当Java事务与ThreadLocal结合使用时,可以在不同的线程中维护各自的事务状态,比如在Spring框架中,每个线程的ThreadLocal可以存储一个TransactionStatus对象,这样就可以在线程内部管理当前事务的状态,而不会影响...

    javaweb 通过threadlocal 手动提交事务

    在Java Web开发中,事务管理是一项关键任务,它确保数据的一致性和完整性。ThreadLocal是Java提供的一种线程绑定变量的工具类...然而,考虑到代码的可维护性和复杂性,通常推荐使用Spring等框架提供的声明式事务管理。

    Spring事务介绍,分23类进行梳理

    Spring事务介绍,分23类进行梳理 ...Spring事务的同步机制使用ThreadLocal变量来存储当前事务的上下文,实现事务的同步。Spring事务的传播机制使用传播行为来控制事务的传播,例如REQUIRED、REQUIRES_NEW、SUPPORTS等。

    spring事务精髓

    总的来说,Spring事务管理通过AOP和对Session的智能管理,为开发者提供了一种高效且灵活的事务处理方式,无论是使用JDBC还是ORM框架如Hibernate,都能够轻松地处理事务。理解Spring事务管理的工作原理,对于优化和...

    spring+druid+AtomikosDataSource实现多数据源切换及分布式事务控制

    Spring的`@Transactional`注解可以用来开启和管理这些分布式事务,确保事务的ACID特性。 7. **代码示例** 创建多数据源的配置文件,包括AtomikosDataSourceBean的定义、TransactionManager的配置以及...

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

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

    理解ThreadLocal

    此外,ThreadLocal 还有很多其他的应用场景,例如在事务管理、日志记录、会话管理等方面都可以使用 ThreadLocal 来实现线程局部变量的管理。 ThreadLocal 的应用非常广泛, Java 开发者应该掌握它来提高开发效率和...

    java 简单的ThreadLocal示例

    ThreadLocal常用于保存线程上下文信息,如用户Session、数据库连接、事务ID等,确保这些信息只在当前线程内有效。 2. **避免同步:** 如果多个线程需要访问相同的数据,但是每个线程都需要自己的副本,...

    ThreadLocal原理及在多层架构中的应用

    - **线程安全的配置对象**:在多层架构中,如Spring框架中,可以使用ThreadLocal来存储线程相关的配置信息,如数据库连接、事务管理等,确保这些对象不会被其他线程访问。 - **HTTP请求上下文**:在Web应用中,可以...

    ThreadLocal原理及在多层架构中的应用.pdf

    Spring框架使用ThreadLocal技术来处理有状态的Bean,使它们在多线程环境下也表现得如同无状态Bean一样。例如,Spring框架中的TransactionSynchronizationManager、RequestContextHolder、AopContext、...

    jBPM4与Spring整合的2种方式

    3. **启用Spring事务管理**:这种方式下,jBPM4不再创建自己的事务管理器,而是依赖于Spring的事务管理。Spring能够根据配置来决定事务的边界,实现事务的声明式管理。 这两种方式的区别主要在于事务管理和...

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

    - Spring事务管理基于事务的ACID属性,异常发生时根据回滚规则决定是否回滚事务。 12. **抽象类与接口** - 抽象类可以包含方法实现,适用于多个类共享部分实现的情况;接口只定义行为,适用于定义规范,多实现多...

    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包结构...

    Springcloud 多数库 多数据源整合,查询动态切换数据库

    在IT行业中,Spring Cloud是一个广泛使用的微服务框架,它提供了许多工具和服务,帮助开发者构建分布式系统。本主题聚焦于在Spring Cloud环境中实现多数据库和多数据源的整合,并且能够动态切换查询的数据库。这是一...

    javaWeb实现事务1

    在上述的描述和代码中,展示了一种使用ThreadLocal实现的简单事务管理策略,它主要用于处理数据库操作时的一致性。以下是对这个实现的详细解析: 1. **ThreadLocal的使用**: ThreadLocal是Java中的一个类,用于在...

    Spring Boot + Druid + Mybatis + Atomikos 配置多数据源 并支持分布式事务

    3. 配置事务管理器:使用Atomikos的UserTransactionManager和JtaTransactionManager,注册到Spring Boot的配置中。 4. 配置Mybatis:设置多个MapperFactoryBean,指定对应的数据源。 5. 编写业务代码:在服务层,...

Global site tag (gtag.js) - Google Analytics