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("st的size:"+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应该是不错的选择。
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执行出错,则前面事务也会被回滚。说明分布式事务控制成功。