JDBC的事务操作

事务概念

对多个SQL指令进行操作,只有这些指令都成功时,才能认为整个操作是完成的,这样的操作称为”事务操作“。如果一个SQL指令操作失败,之前的各个操作都要取消,这种取消动作称为”回滚 (rollback)“。

JDBC中的事务操作是基于同一个数据连接的,各个连接之间相互独立。当数据连接断开后,一个事务就结束了。事务操作的方法都位于java.sql.Connection接口中。

JDBC事务操作默认是自动提交的,一条对数据库的更新表达式代表一项事务操作,操作成功后,系统会自动调用commit()来提交,否则会调用rollback()来回滚。如果想取消自动提交可以调用setAutoCommit(false),回复自动提交则可以设置其实参为true。取消自动提交可以灵活地将多个表达式作为一个事务,然后使用commit()来提交,如果出现异常可以使用rollback()来回滚。


实例

import java.sql.*;

public class Main {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Connection con = null;
        Statement smt;
        
        try{
            Class.forName("com.mysql.jdbc.Driver");//加载JDBC MYSQL驱动
            con=DriverManager.getConnection("jdbc:mysql://localhost:3306/javatemp","root","root"); //建立数据库连接
            con.setAutoCommit(false);//取消事务的自动提交
            //System.out.println(con.getTransactionIsolation()); //获取事务默认加锁方式
            //con.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);//设置事务加锁方式
            //System.out.println(con.getTransactionIsolation());//获取事务当前加锁方式
            smt=con.createStatement();//创建Statement对象
            smt.executeUpdate("insert into users(username) values('beston5')");//执行SQL命令
            con.rollback();//回滚
            smt.executeUpdate("insert into users(username) values('beston6')");
            con.commit();//提交
            con.setAutoCommit(true);//恢复事务的自动提交            
            PreparedStatement ps=con.prepareStatement("insert into users(username) values(?)");//使用prepareStatement对象操作数据指令
            ps.setString(1,"oseye6");//设置占位符值
            ps.executeUpdate();    //执行SQL命令

            ResultSet res=smt.executeQuery("select * from users");//执行SQL命令并获得结果集
            while(res.next()){//处理结果集
                System.out.println("userid:"+res.getInt("userid")+"\t"+"username:"+res.getString("username"));                
            }
            res.close();//关闭结果集连接的数据并释放JDBC资源
            
        }catch(Exception ex){
            ex.printStackTrace();//打印异常信息        
        }finally{
            if(con!=null){
                try{
                    con.close();//关闭数据库连接
                }catch(Exception ex){                    
                    ex.printStackTrace();
                }
            }
            
        }
    }
}
View Code

上面注释的应该很详细了,"beston5"不会插入到数据库,"beston6"和"oseye6"将会插入到数据中。


JDBC 对数据库的加锁机制

先来看三个概念:

  • 脏数据读写(dirty reads):当一个事务修改了某一数据行的值而未提交时,另一事务读取了此行值。倘若前一事务发生了回滚,则后一事务将得到一个无效的值(脏数据)。
  • 重复读写(repeatable reads):当一个事务在读取某一数据行时,另一事务同时在修改此数据行。则前一事务在重复读取此行时将得到一个不一致的值。
  • 影象读写(phantomreads):当一个事务在某一表中进行数据查询时,另一事务恰好插入了满足了查询条件的数据行。则前一事务在重复读取满足条件的值时,将得到一个额外的“影象“值。

JDBC API支持事务对数据库的加锁,并且提供了5种操作支持:

static int TRANSACTION_NONE= 0; //禁止事务操作和加锁。
static  int TRANSACTION_READ_UNCOMMITTED= 1; //允许脏数据读写、重复读写和影象读写
static  int TRANSACTION_READ_COMMITTED= 2;//禁止脏数据读写,允许重复读写和影象读写
static  int TRANSACTION_REPEATABLE_READ= 4;//禁止脏数据读写和重复读写,允许影象读写
static  int TRANSACTION_SERIALIZABLE= 8;//禁止脏数据读写、重复读写和允许影象读写

上述归类可以分为两种加锁密度,表加锁和行加锁。其中最后一项为表加锁,其余3~4项为行加锁。

如上代码中,取消

//System.out.println(con.getTransactionIsolation()); //获取事务默认加锁方式
//con.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);//设置事务加锁方式
//System.out.println(con.getTransactionIsolation());//获取事务当前加锁方式

的注释,将会看到默认枷锁是为4,设置后的枷锁为2.


总结:

随着加锁方式值增加,其事务的独立性增加,更能有效的防止事务操作之间的冲突;同时也增加了加锁的开销,降低了用户之间访问数据库的并发性,程序的运行效率也回随之降低。因此得平衡程序运行效率和数据一致性之间的冲突。

一般来说:

  • 对于只涉及到数据库的查询操作时,可以采用TRANSACTION_READ_UNCOMMITTED方式;
  • 对于数据查询远多于更新的操作,可以采用TRANSACTION_READ_COMMITTED方式;
  • 对于更新操作较多的,可以采用TRANSACTION_REPEATABLE_READ;
  • 在数据一致性要求更高的场合再考虑最后一项,由于涉及到表加锁,因此会对程序运行效率产生较大的影响;

另外,在oracle中数据库驱动对事务处理的缺省值是TRANSACTION_NONE,即不支持事务操作,所以需要在程序中手动进行设置;而MYSQL是不支持TRANSACTION_NONE的,这点需要注意。

posted @ 2012-08-15 10:44  码农神说  阅读(249)  评论(0编辑  收藏  举报