`
shitou_pengju
  • 浏览: 585 次
  • 性别: Icon_minigender_1
  • 来自: 天津
最近访客 更多访客>>
文章分类
社区版块
存档分类
最新评论

jdbc 事务jdbc之事务篇

    博客分类:
  • jdbc
阅读更多
jdbc 事务jdbc之事务篇
*
事务:一组作为一个原子整体的操作,ALL OR NOTHING语义:要么全部,要么一个都不。就是说这组操作中的任何一个操作失败,代表这组操作失败,仅当所有操作都成功时,才算这个“事务”成功。当事务失败时,其影响应该撤销/回滚。当事务成功时其影响应该持久/提交/保存。用电路图形象示意:一个串连图,任何一个节点断路,两端都不会“通”。关于事务的ACID属性在很多资料中都有介绍这里不再涉及。

一个操作是否应归为某个事务,这完全是根据应用/需求来决定的,理论上你可以把任何数目操作,任何操作归为一个事务中。并且当你认为事务也是一个操作时,就出现了“事务的嵌套”,就是说一个事务中可能含有其他事务!就像电路图:复杂电路图可由简单电路图通过并联或串连的形式组合而成。再服务器上,常常并行运行多个事务(我们可用线程来模拟)。关于并行事务处理这里不讨论它(如何串行化事务,并发执行事务等都是较复杂的问题)。

在程序中出现的事务一般都比较隐晦,什么操作归为一个事务由业务逻辑决定,但我们在实现事务时可能有多种方式。这由我们决定,我把业务性事务称为应用事务(仅个人称呼),我们知道程序在运行时使用内存来模拟现实的“微世界”的,一旦断电或程序结束一切都化为泡影,你将又回到原点,为使你的操作确实产生影响,那么相关状态的更改应该持久化,就是说要把程序的(全部或部分)某个瞬时状态保存到持久性介质上。所以我们要进行IO操作,这里就是将其持久到数据库。
*
**
这里来看看如何实现一个应用事务,
现在认为整个系统是一个对象(为了简化讨论),这个对象内部可能是个很复杂的对象图,任何对象上发生的操作都会改变这个巨对象的状态(对象的状态由其内部所有子对象的状态组合来表示),我们认为某些操作应该归为一个事务,那么就需要持久由这些操作引起的状态变化,即应用事务的影响要保存到数据库去!。
事务都具有一个边界:即什么操作代表事务的开始,什么操作代表事务的结束,当然在应用程序中没有这样的边界,但是数据库中却是存在这样的东东:默认情况下数据库中的更改是永久性的:就是说操作不存在回滚,每一次写数据的操作都会“提交”到数据库。这可认为每个dml操作都是一个事务,但我们可以通过编程来改变这种情况,比如mysql中:set autocommit=0; 就表示关闭自动提交,由我们自己来控制提交和回滚时机。jdbc是封装各种数据库差异的API所有对事务控制的操作在java中都是:connection.setAutoCommit(false);//这在任何数据库系统中都管用。
这里有两个事务我们需要区分:一个是应用事务,一个是数据库事务(数据库事务是一组sql操作)。我们就是要将应用事务的结果通过数据库事务来表现出来,换句话说就是应用事务需要依赖数据库事务API。
迄今我所知道的事务实现可以是这样的:
1.当应用事务开始时即刻开启数据库事务,当应用事务回滚时即刻回滚数据库事务,当应用事务提交时即刻提交数据库事务,即二者总是如影随形。
2.应用事务独立完成,仅当应用事务提交时再完成数据库事务的开始和提交。我在以下的示例中会给出这种实现思路的。
**
***
jdbc中控制事务的操作:
Connection接口的:setAutoCommit(boolean autoCommit);commit();和rollback()总共就三个事务控制的接口操作。当连接第一次获取时,将其自动提交模式设定为假表示我们自己控制事务,每一个事务的提交或回滚是下一次事务的开始!!
当一个事务特别大时我们可能要进行细粒度的控制,不希望是ALL或Nothing,可以步步为营,这样数据库系统现在都引入了保存点的概念,就像游戏通关中的保存点,允许我们选择性的回滚到某个最近或指定的保存点上Connection的setSavepoint([String name])方法就可以完成这个功能,当然可以回退到指定的保存点上:connection.rollback(Savepoint point)。另外connection.releaseSavepoint(Savepoint point)可以释放掉已保存的保存点。要注意并非所有的DBMS都支持保存点,通过查询数据库元数据对象来看它是否支持保存点,如果不支持那么关于保存点的操作会抛出异常的:
if(databaseMetaData.supportsSavepoints()){
}else{
//不支持
.....

}
jdbc是一个接口集,各个数据库厂商实现他们,有些操作可能厂商不支持,所以在使用比较偏或比较高级的jdbc操作时最好确定一下他们是否支持,通过查询元数据就可以知道(datavaseMetaData.supportsXXX())!!

Satement的addBatch(),executeBatch()会将一些非查询性质的语句(包括ddl)作为一个单位来执行的。并非所有DBMS都支持批量更新。注意addBatch方法中不可加入select性质的语句。我们可以看出它这里共享了statement对象,我们知道一般事务不能够跨连接(不考虑分布式事务),所以要使一组sql语句归为一个事务的基本条件是“共享同一个连接”。使用批量更新也不失为事务实现的好方法:
try{
conn.setAutocommit(false);
Statement stmt=conn.createStatement();
stmt.addBatch("update .......");
stmt.addBatch("delete.......");
stmt.addBatch("insert........");
int[] updateCounts=stmt.executeBatch();
conn.commit();
}catch(Exception ex){

conn.rollback();
}finally{
     try{
         conn.close();
      }catch{
   }
}

在并行处理事务过程中,事务间可能操作同一个表或数据项,这时就需要同步,或锁机制,数据库一般提供四种事务隔离级别来防止事务间的相互干扰:
隔离级别:a.串行化     b. 可重复读。 c. 读已提交数据    。d. 读未提交数据。

级别的严格程度是递减的,关于这个话题请参考其他 资料。
connection.setTransactionIsolation(int LEVEL);//设定事务的隔离级别 是连接类型的常量值。
***
****
事务操作示例:
/*
事务管理一般使用try/catch/finally块来处理:首先记录自动提交的当前状态(true/false)——因为使用事务时会将连接的自动提交状态改变为false,如果我们要共享一个连接对象那么,使用事务前连接的提交状态需要被记录;然后在Try块中置连接的提交状态为false(即手动提交),之后进行相关的sql语句操作数据库,更新或查询;如果发生故障在catch块中回滚事务:conn.rollback();如果一切正常在try块末尾处提交事务:conn.commit();总在finally块中恢复执行事务前连接的提交状态;
一下我给出一个模板:
*/
package psn.czw.dbo;

import java.sql.Connection;
import java.sql.SQLException;

/**
* @author yiqing95
* 事务操作演示!
*/

public class TranscactionDemo {

/**
* @param args
*/
public static void main(String[] args) {
   // TODO Auto-generated method stub
   transDemo1();
}

public static void transDemo1(){
   /*首先获取连接对象*/
   Connection conn=DBUtil.buildConnection(
     "E://programPractice//Java6//src//psn//czw//dbo//dbConfig.xml");
 
   /*上面的连接是我重新获取的,所以其提交状态总是true,
   * 但当连接是共享其他代码时,为了不干扰后续操作我们需要进行
   * 类似进栈,出栈行为,这就像使用java.awt.Graphics对象
   * 自己绘图时需要做的。总之共享的对象,当你在使用前需要保存
   * 它的状态,使用完毕后恢复它的状态,不要影响他人!*/
   boolean autoCommit=true;//用来不保存连接的初始提交状态
   try {
    autoCommit=conn.getAutoCommit();//获取执行事务前的提交状态
  
    /*其次关闭掉连接对象的默认的自动提交方式/模式   */
    conn.setAutoCommit(false);
  
    DBUtil.printResultSet(
      DBUtil.execQuery(conn,"select * from t"));
  
    /*使用这个连接对象来执行sql操作*/
    DBUtil.execInsert(conn, "insert t(col2,col3) values('a1','b1'),('sdf','sdfs')");
    
    DBUtil.execUpdate(conn,"update t set col2='c3' where id=3");
  
    int a=1/0;
  
    conn.commit();/*提交事务,如果代码能执行到这一步表明一切操作顺利*/
  
   } catch (SQLException e) {
    e.printStackTrace();
    try {
     conn.rollback();//发生了异常,应回滚事务
    } catch (SQLException e1) {
     e1.printStackTrace();
    }
   }catch(Exception ex){
    //处理除零异常
                        /*我故意在正常的sql操作中加入了一个非数据库操作
                        该操作会导致除零异常,这里是为了说明有时的事务并非
                        全部是数据库操作相关,可能还混有业务相关的操作*/
    System.out.println(ex.getMessage());
    try {
     conn.rollback();//发生了异常,应回滚事务
    } catch (SQLException e1) {
     e1.printStackTrace();
    }
   }
   finally{
    /*
    这里可能需要关闭语句对象,关闭连接对象。
    在一些情况下连接对象可能被复用,当我们
    从别处得到一个连接时,如果他是共享/复用的我们
    不应该随便改变他的状态。同样注意一点如果我们从
    别处得到了一个连接对象,当使用这个连接对象时
    那么我们有可能已经参与了一个事务,(假设我们的
    代码不用任何事物相关的API)
    */
    try {

     DBUtil.printResultSet(
       DBUtil.execQuery(conn,"select * from t"));//打印表中数据
   
     conn.setAutoCommit(autoCommit);//重置连接提交状态
     conn.close();//可以不关闭,如果你想共享的话
    } catch (SQLException e) {
     e.printStackTrace();
    }
   }
 
}

}
//以上的代码提取一下可以作为一个事务操作模板。
****
*****
上面看到的好像直接操纵DBMS中的事务,但是应用事务可以跨系统,跨领域操作,比如我们可以认为一次文件的写操作也属于一个事务,我们甚至可以把文件操作,数据库操作,网络操作都归为一个事务中,这个时候为了保证事务的ACID属性,实现就复杂多了,关于分布式事务,两阶段提交协议等话题以后有机会在学习吧,现在先讨论另一个事务的实现范例:
按照分层模式,用户启动一个事务操作(比如点击一个按钮)操作流先经过UI层,再经过业务逻辑层,最后传至数据层,接着到数据库系统。因为内存的天然可丢失性,我们不得不把状态保持的持久性的硬盘中,其实设想如果内存足够大,并且不是断电丢失的话那么硬盘是完全可避免使用的。所以我们其实可以在内存中完成整个事务,然后再持久化状态到硬盘。为此我写了一个模拟这种思路的类:


package psn.czw.dbo;

import java.util.Hashtable;

/**
* @author yiqing95
* 该接口代表一个应用事务
* 拥有的操作是begin,commit,rollback
*
*/

public interface IAppTrans {
public void begin();
public void commit();
public void rollback();
}


class MyTrans implements IAppTrans{
   private String state;//表示状态

private String duplicate;//副本

public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}

public MyTrans(){
/*
初始化自身的状态,我这里只是示意,实际状态可能非常复杂*/
this.state="I am state of MyTrans";
}


   @Override
public void begin() {
         /*开始事务时并不直接操纵原始状态
     而是先复制一个副本,并在副本上操作
    */
    this.duplicate=this.state;
           使用副本进行操作(没有提供操作副本的接口呵!)..............略
}
@Override
public void commit() {
       /*
        提交时是将副本赋给原来的状态 */
   this.state=this.duplicate;
}
@Override
public void rollback() {
     /*回滚不做任何事情,因为是在副本上操作的并不影响原始状态*/
}

}
这只是个示意类,其实我们在提交的时候可以操作数据库,在commit方法中来持久化状态,比如这样:
public void commit() {
       /*
        提交时是将副本赋给原来的状态 */
   this.state=this.duplicate;
            //以下是数据库的事务操作:
           conn.setCommit(false);
           Satement stmt=conn.createSatement();
           stmt.executeUpdate("insert tbl(state) values("+this.state+")");
           //或者: stmt.executeUpdate("update tbl set state="+this.state+"
                     where id=XXXXXX
                     ");
           conn.commit();
           //失败再回滚等等...
}
这样的设计使得数据库事务的时间是最短的,所以有这样的最佳实践:将应用设计为只有当它准备好与数据库通讯时才开始事务,一旦数据库相关的操作完成后立即结束数据库的事务,在事务开始和结束之间,除了数据库通信以外不做其他工作。

*****
******
因为事务的处理流程大都一致所以可以使用模板设计模式来处理,这里其实不是:
package psn.czw.dbo;

import java.sql.Connection;
import java.sql.SQLException;
/*
* 事务模板类
*/

public class TransTemplate {
     Connection conn;
public TransTemplate(){
this.conn=null;/*实际使用时可获取一个连接,或由别处传递*/
}
public TransTemplate(Connection conn){
   this.conn=conn;//从外部传入一个连接对象
}

public void execTrans(TransCallback transCB){
   boolean autoCommit=true;//用来保存连接的初始提交状态
   try {
    autoCommit=conn.getAutoCommit();//获取执行事务前的提交状态
  
    /*其次关闭掉连接对象的默认的自动提交方式/模式   */
    conn.setAutoCommit(false);
    //回调
    transCB.doInTrans(this.conn);

    conn.commit();/*提交事务,如果代码能执行到这一步表明一切操作顺利*/
    System.out.println("事务顺利完成 !");
  
   } catch (SQLException e) {
    e.printStackTrace();
    try {
     conn.rollback();//发生了异常,应回滚事务
    } catch (SQLException e1) {
     e1.printStackTrace();
    }
   }
   finally{
     try {
     conn.setAutoCommit(autoCommit);//重置连接提交状态
     //conn.close();
    } catch (SQLException e) {
     e.printStackTrace();
    }
   }    
}
}
interface TransCallback{
public void doInTrans(Connection conn)throws SQLException;

}

在我机子上的使用是成功的:
Connection conn=DBUtil.buildConnection(
   "E://programPractice//Java6//src//psn//czw//dbo//dbConfig.xml");
 
   TransTemplate transTemplate=new TransTemplate(conn);
   transTemplate.execTrans(new
     TransCallback(){

      @Override
      public void doInTrans(Connection conn) throws SQLException {
       Statement stmt=conn.createStatement();
       stmt.executeUpdate(
         "insert t(col2,col3) values('a1','b1'),('sdf','sdfs')");
       stmt.executeUpdate("update t set col2='hello' where id=11");
     
      }
  
   });    //这里用了匿名类,只要把所有与事务相关的操作放在doInTrans方法中即可,这里的设计模仿spring中的模板类思想,但那里的那个功能很牛的,我这里写着自己玩的,这个类很脆弱,不是线程安全的!!

至此关于事务,就练习到这里!

******
分享到:
评论

相关推荐

    JDBC事务管理的简单实现

    "JDBC事务管理的简单实现" 在JDBC编程中,事务管理是非常重要的一部分。为了实现数据一致性,需要在代码中显示的调用Connection方法的事务相关API来完成工作。下面是关于JDBC事务管理的简单实现的知识点。 一、...

    JDBC的事务初探

    **JDBC事务初探** Java Database Connectivity (JDBC) 是Java平台中用于访问数据库的标准API。在处理数据库操作时,事务管理是确保数据一致性、完整性和持久性的重要机制。本篇将深入探讨JDBC中的事务处理,以及...

    JDBC事务 JTA事务 传播特性 隔离级别

    **JDBC事务**主要针对单个数据库连接,由数据库管理系统(DBMS)自身管理事务。JDBC提供了`Connection`对象的`commit()`和`rollback()`方法用于提交和回滚事务。这是简单的单阶段提交,适用于单一数据源的情况。 **...

    JDBC事务管理.docx

    JDBC事务管理是数据库操作的重要组成部分,特别是在多用户并发访问数据库的环境中,保证数据的一致性和完整性至关重要。本文主要探讨了事务的基本概念、ACID原则、数据库并发问题以及解决方案。 首先,事务是数据库...

    spring JDBC事务管理

    标题中的“Spring JDBC事务管理”是指在Spring框架中如何利用JDBC进行数据库操作时的事务控制。Spring提供了多种方式来管理事务,使得开发者能够在复杂的业务逻辑中更好地控制数据的持久化过程,确保数据的一致性和...

    JDBC事务操作例子所需jar包

    在Java中,JDBC事务处理主要涉及以下几个关键概念: 1. **Connection对象**:它是与数据库的会话,通过该对象可以开启、提交和回滚事务。在连接池中获取到的Connection通常已经自动开启了事务,但如果没有,可以...

    JDBC事务控制--讲述如何控制JDBC事务

    ### JDBC事务控制详解 在数据库应用开发中,事务管理是一项重要的功能,它确保了一系列操作能够作为一个整体被正确地执行或回滚。Java Database Connectivity (JDBC) 是 Java 平台中用于连接数据库的标准 API,提供...

    JDBC事务和JTA(XA)事务

    JDBC事务和JTA(XA)事务 JDBC事务和JTA(XA)事务是两种常见的数据库事务类型,它们都可以用来管理数据库事务,确保数据库的一致性和可靠性。 JDBC事务 JDBC事务是Java Database Connectivity(JDBC)的事务机制,它...

    JDBC事务处理机制探秘

    总结起来,JDBC事务适用于简单的单库操作,而JTA事务则适合处理跨多个数据源的复杂事务场景。在J2EE环境中,容器管理的事务(如SessionBean中的JTA事务)能够提供更高的可伸缩性和可靠性。在实际应用中,根据需求...

    JDBC事务管理项目

    本项目“JDBC事务管理项目”旨在教你如何在Java应用中使用JDBC来处理数据库事务,确保数据的一致性和完整性。 **事务的概念** 事务是数据库操作的基本单元,它包含了对数据库的一系列操作,这些操作要么全部成功,...

    Sharding-JDBC分布式事务应用

    《Sharding-JDBC分布式事务应用》是一篇关于Java领域中如何利用Sharding-JDBC实现分布式事务的专题讨论。Sharding-JDBC是阿里巴巴开源的一款轻量级数据库中间件,它为Java开发者提供了一种透明化的数据分片解决方案...

    Java(JDBC)事务处理

    Java(JDBC)事务处理

    jdbc——内嵌事务

    jdbc——内嵌事务 class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> ...

    Spring JDBC与事务管理

    javaEE 实验三 Spring JDBC与事务管理, 一、实验目的 1、掌握Spring JDBC的配置; 2、掌握JdbcTemplae类中增删改查方法的使用; 3、了解Spring事务管理的3个核心接口; 4、了解Spring事务管理的两种方式; 5、掌握...

    JavaBean中使用JDBC方式进行事务处理

    ### JavaBean中使用JDBC方式进行事务处理 #### 一、引言 在现代软件开发过程中,数据一致性是非常关键的一个方面,特别是在涉及到多个数据库操作时。本文将详细介绍如何在JavaBean中利用JDBC(Java Database ...

    jdbc事务及分页

    ### JDBC事务管理与分页策略详解 在Java开发中,JDBC(Java Database Connectivity)作为连接数据库的标准API,被广泛应用于数据操作。本文将深入探讨JDBC中的事务处理机制及其在实际应用中的示例,同时对比分析两...

    实例详解Spring JDBC事务管理.doc

    Spring JDBC事务管理 Spring框架提供了两种事务管理方式:编程式的事务管理(Programmatic transaction management)和声明式的事务管理(Declarative transaction management)。在本文中,我们将以JDBC事务管理...

    spring jdbc 事务

    在描述中提到的博客链接(由于实际链接无法访问,这里无法提供具体细节),可能详细讲述了如何在Spring中配置和使用JDBC事务,包括示例代码和常见问题解决。在实际项目中,理解并正确应用这些事务管理机制对于保证...

    JDBC事务管理及SavePoint示例Java开发Jav

    这篇12页的PDF文档,"JDBC事务管理及SavePoint示例Java开发Java经验技巧",很可能是详细讲解了这两个主题,并提供了一些实际的编程示例。 首先,让我们来深入理解JDBC事务管理。在Java中,JDBC提供了四种事务隔离...

Global site tag (gtag.js) - Google Analytics