JDBC分布式管理

来源:互联网 发布:arduino单片机简介 编辑:程序博客网 时间:2024/06/07 07:02

1、java.sql.ResultSet的各种特性

       JDBC1.0、JDBC2.0 、JDBC3.0 中分别用以下方法创建Statement 。

JDBC1.0 : createStatement()

JDBC2.0 : createStatement(resultSetType,resultSetConcurrency)

JDBC3.0 : createStatement(resultSetType,resultSetConcurrency, resultSetHoldability)

下面依次分析resultSetType 、resultSetConcurrency 、resultSetHoldability 这几个参数的含义。

1.1、ResultSetType

     resultSetType 的可选值有: ResultSet.TYPE_FORWARD_ONLY 、ResultSet.TYPE_SCROLL_INSENSITIVE 、ResultSet.TYPE_SCROLL_SENSITIVE 。

 

1 :ResultSet.TYPE_FORWARD_ONLY

默认的cursor 类型,仅仅支持结果集forward ,不支持backforward ,random ,last ,first 等操作。 

2 :ResultSet.TYPE_SCROLL_INSENSITIVE

支持结果集backforward ,random ,last ,first 等操作,对其它session对数据库中数据做出的更改是不敏感的。

实现方法:从数据库取出数据后,会把全部数据缓存到cache 中,对结果集的后续操作,是操作的cache 中的数据,数据库中记录发生变化后,不影响cache 中的数据,所以ResultSet 对结果集中的数据是INSENSITIVE 的。

3 :ResultSet.TYPE_SCROLL_SENSITIVE

支持结果集backforward ,random ,last ,first 等操作,对其它session对数据库中数据做出的更改是敏感的,即其他session 修改了数据库中的数据,会反应到本结果集中。

实现方法:从数据库取出数据后,不是把全部数据缓存到cache 中,而是把每条数据的rowid 缓存到cache 中,对结果集后续操作时,是根据rowid 再去数据库中取数据。所以数据库中记录发生变化后,通过ResultSet 取出的记录是最新的,即ResultSet 是SENSITIVE 的。insert delete操作不会影响到ResultSet,因为insert 数据的rowid 不在ResultSet 取出的rowid 中,所以insert 的数据对ResultSet 是不可见的,而delete 数据的rowid 依旧在ResultSet 中,所以ResultSet 仍可以取出被删除的记录( 因为一般数据库的删除是标记删除,不是真正在数据库文件中删除)。

 

做个试验,验证一下SENSITIVE 特性。数据库为oracle10g ,驱动为ojdbc6.jar 。

表中数据如下:

程序如下:

   @Test

       public void testSensitive() throws Exception{

              Scanner sc = new Scanner(System.in);

              System.err.println(con.getTransactionIsolation());

              Statement st = con.createStatement(

                            ResultSet.TYPE_SCROLL_SENSITIVE,

                            //修改成update类型

                            ResultSet.CONCUR_READ_ONLY

                     );

              //必须显式的使用字段名称

              ResultSet rs = st.executeQuery("select id,name from stud");

              //设置Oracle一次获取一条记录

             

              while(rs.next()){

                     String id = rs.getString("id");

                     String name = rs.getString("name");

                     System.err.print(id+"\t"+name);

                     //设置阻塞,然后去修改表中的数据

                     sc.nextLine();

              }

}

定义ResultSet 为 ResultSet. TYPE_SCROLL_SENSITIVE 类型,首先执行 sql 访问数据库,然后执行 rs.next() 移动游标取数据。在循环里面加上nextLine(),目的是为了我们有时间在后台把数据库里的数据改了。比如当在循环里打印出第一行的数据后,我们在后台,将最后一行修改成:Oracle。但最终的结果却是如下:

      

数据没变呀,ResultSet 不敏感啊!于是去查阅资料,找了n 久,还是在英文文档上找到了答案。原来是fetchsize 的问题。调用ResultSet 的next 方法取数据时,并不是每调用一次方法就去数据库里查一次,而是有个fetchSize, 一次取fetchSize 条数据。Oracle 默认的fetchsize 等于10 ,所以上面的代码在第一次调用rs.next()时,就已经把3 条数据都取出来了,所以才会有上面的结果。

      

第二次实验,在ResultSet rs =stmt.executeQuery(sql); 前面加上 stmt.setFetchSize(1); 将fetchSize 设置为1 。然后重新第一次实验的步骤,发现最 终结果为:

             

       代码:

       @Test

       public void testSensitive()throws Exception{

              Scanner sc = new Scanner(System.in);

              System.err.println(con.getTransactionIsolation());

              Statement st = con.createStatement(

                            ResultSet.TYPE_SCROLL_SENSITIVE,

                            //修改成update类型

                            ResultSet.CONCUR_READ_ONLY

                     );

              //Oracle默认的一次获取10条记录

              System.err.println("stsize:"+st.getFetchSize());

st.setFetchSize(1);

              //必须显式的使用字段名称

              ResultSet rs = st.executeQuery("select id,name from stud");

              //设置Oracle一次获取一条记录

             

              while(rs.next()){

                     String id = rs.getString("id");

                     String name = rs.getString("name");

                     System.err.print(id+"\t"+name);

                     //设置阻塞,然后去修改表中的数据

                     sc.nextLine();

              }

       }

原因就是 fetchsize 设置为 1 时,每次 next 取数时都会重新用 rowid取数据库里取数据,当然取到的是最新的数据了。

1.2、ResultSetConcurrency

     ResultSetConcurrency的可选值有2个:

     ResultSet.CONCUR_READ_ONLY 在ResultSet中的数据记录是只读的,不可以修改

     ResultSet.CONCUR_UPDATABLE 在ResultSet中的数据记录可以任意修改,然后更新到数据库,可以插入,删除,修改。

1.3、ResultSetHoldability

  ResultSetHoldability 的可选值有2 个 :

    HOLD_CURSORS_OVER_COMMIT: 在事务commit 或rollback 后,ResultSet 仍然可用。

    CLOSE_CURSORS_AT_COMMIT: 在事务commit 或rollback 后,ResultSet 被关闭。

      public void testRs() throws Exception{

              Statement st = con.createStatement();

              ResultSet rs = st.executeQuery("select * from stud");

              //当使得同一个st打开第二个ResultSet时第一个即被自动关闭

              ResultSet rs2 = st.executeQuery("select * from stud");

              System.err.println(rs.isClosed());

       }

     需要注意的地方:

       1 :Oracle 只支持HOLD_CURSORS_OVER_COMMIT 。

2 :当Statement 执行下一个查询,生成第二个ResultSet 时,第一个ResultSet 会被关闭,这和是否支持支持HOLD_CURSORS_OVER_COMMIT无关。

 

       以下是代码测试是否支持可持续性:

      /**

        * 测试结果集的可持续性

        */

       @Test

       public void testReHold() throws Exception {

              // Oracle不支持在提交后直接关闭结果集

              boolean boo =con.getMetaData().supportsResultSetHoldability(

                            ResultSet.CLOSE_CURSORS_AT_COMMIT);

              System.err.println("是否支持:" + boo);//false

              boo = con.getMetaData().supportsResultSetHoldability(

                            ResultSet.HOLD_CURSORS_OVER_COMMIT);

              System.err.println("只支持提交事务以后保持rs:" + boo);//true-oracle只支持这一种情况

              // 打开一个事务

              con.setAutoCommit(false);

              Statement st = con.createStatement(ResultSet.TYPE_FORWARD_ONLY,

                            ResultSet.CONCUR_READ_ONLY, ResultSet.HOLD_CURSORS_OVER_COMMIT);

              ResultSet rs = st.executeQuery("select id,name from stud");

              con.commit();//提交以后因为没有关闭,可以可以使用rs

              if (!rs.isClosed()) {

                     while (rs.next()) {

                            String name = rs.getString("name");

                            System.err.println(name);

                     }

              }

       }

1.4、验证数据库是否支持ResultSet的各种特性

不同的数据库版本及 JDBC 驱动版本,对ResultSet 的各种高级特性的支持是不一样的,我们可以通过以下方法,来验证具体的数据库及 JDBC 驱动,是否支持ResultSet 的各种特性。

    DatabaseMetaData dbMeta = conn.getMetaData();

     然后调用 DatabaseMetaData 对象的以下方法:

    boolean supportsResultSetType(int resultSetType);

    boolean supportsResultSetConcurrency(int type, int concurrency);

    boolean supportsResultSetHoldability(int holdability);

    参考的2 篇英文文档:

http://cs.felk.cvut.cz/10gr2/java.102/b14355/jdbcvers.htm(JDBCStandards Support)

http://download.oracle.com/docs/cd/B10501_01/java.920/a96654/resltset.htm#1023642(Oracle9i JDBC Developer's Guide and Reference Release 2 (9.2))

http://www.java3z.com/cwbwebhome/article/article8/828.html?id=2244

 

 

2、使用JOTM实现分布式的事务

2.1、JOTM

       JavaOpen Transaction Manager。

       http://jotm.ow2.org/xwiki/bin/view/Main/WebHome

      

2.2、XAPool

        如果需要使用JOTM则应该使用分布式的数据连接池。XAPool应该是不错的选择。

        http://xapool.ow2.org/

        

 

2.3、JTA

       JTA,即Java Transaction API,译为Java事务API。

JTA允许应用程序执行分布式事务处理——在两个或多个网络计算机资源上访问并且更新数据。JDBC驱动程序的JTA支持极大地增强了数据访问能力。

一个分布式事务(distributed transaction)包括:

一个事务管理器(transaction manager),事务管理器(transaction manager)承担着所有事务参与单元者的相互通讯的责任。

一个或多个资源管理器(resource manager)。一个资源管理器(resource manager)是任意类型的持久化数据储。

       所有分布式事务的类,都必须实现JTA中的接口。

 

2.4、相关类:

1、javax.sql.XADataSource –位于Java SDK中。所以分布式的数据源都应该实现此接口:

      

2、javax.sql.XAConnection-位于JavaSDK中,用于实现分布式的数据连接

      

3、javax.sql.UserTransaction – 位于JTA包中。用于管理跨多个数据源的事务。

      

4、javax.sql.TransactionManager – 位于JTA包中,用于管理跨多个数据源的事务

      

 

 

2.5、以下是用JOTM管理分布式事务的基本示例

第一步:创建一个Java项目,并导入以下包

              包括JOTM的包和xapool的包。本处由于要连接oracle数据库,所以导入了ojdbc6.jar包。

第二步:在Oracle数据库中,创建两个用户,模拟两个连接

          本处,在scott和hr用户下,创建一个结构完全相同的表:

         

第三步:代码

package demo;

import java.sql.Connection;

import java.sql.Statement;

import javax.transaction.TransactionManager;

import javax.transaction.UserTransaction;

import org.enhydra.jdbc.standard.StandardXADataSource;

import org.objectweb.jotm.Jotm;

/**

 * JOTM实现分布式的事务管理

 */

public class JOTMJTATxDemo {

       public static void main(String[] args)throws Exception {

              Jotm jotm = new Jotm(true,false);

              System.err.println(jotm);

              // 创建TransactionManager

              TransactionManager transactionManager =jotm.getTransactionManager();[王健1] 

              UserTransaction userTransaction[王健2]  = jotm.getUserTransaction();

              System.err.println("userTx:"+userTransaction);

              // 创建一个分布的数据源

              StandardXADataSource ds1 = newStandardXADataSource();[王健3] 

              ds1.setDriverName("oracle.jdbc.OracleDriver");

              ds1.setUser("scott");

              ds1.setPassword("tiger");

              ds1.setUrl("jdbc:oracle:thin:@192.168.56.103:1521:xe");

              //这一句非常重要,用于说明使用哪一个事务管理者

              ds1.setTransactionManager(transactionManager);[王健4] 

              System.err.println("ds1 is :" + ds1);

              // 创建第二个数据源

              StandardXADataSource ds2 = new StandardXADataSource();

              ds2.setDriverName("oracle.jdbc.OracleDriver");

              ds2.setUser("hr");

              ds2.setPassword("1234");

              ds2.setUrl("jdbc:oracle:thin:@192.168.56.103:1521:xe");

              ds2.setTransactionManager(transactionManager);

              System.err.println("ds2 is :" + ds2);

              // 分别获取两个分布的Connection,必须要通过xadatasource获取分布式的数据连接

              Connection c1 = ds1.getXAConnection[王健5] ().getConnection();

              Connection c2 = ds2.getXAConnection().getConnection();

              System.err.println("c1 is:"+c1);

              //开始事务

              userTransaction.begin();[王健6] 

              System.err.println(userTransaction.getStatus());

              try {

                     // 分别获取两个Statement

                     Statement st1 = c1.createStatement();

                     Statement st2 = c2.createStatement();

                     st1.execute("insert into stud(id,name) values(1,'Jack')");

                     st2.execute("insert into stud(id,name) values(2,'Rose')");

                     System.err.println("成功了。提交数据...");

                     System.err.println(userTransaction.getStatus());

                     userTransaction.commit();

              } catch (Exception e) {

                     System.err.println("错误:"+e.getMessage());

                     userTransaction.rollback();

              }finally{

                     if(c1!=null ||!c1.isClosed()){

                            System.err.println("关闭连接。。");

                            c1.close();

                            c2.close();

                     }

                     //可选的停止JOTM服务

                     jotm.stop();

              }

              System.err.println("代码执行完成。。。");

       }

}

如果上面的两个statament,后面的statement执行出错,则前面事务也会被回滚。说明分布式事务控制成功。

 

 

 

 

 

 

获取事务管理器。

声明事务。后面需要用这个类来开始或是提交事务。

分别声明两个数据源。连接不同的数据源。

设置事务事务器。

通过XAConnection获取连接。

开始事务。

整理自王建老师