JDBC高级特性(二)事务、并发控制和行集

一、事务

事务是指一个工作单元,它包含了一组添加,删除,修改等数据操作命令,这组命令作为一个整体向系统提交执行,要么都执行成功,要么全部恢复

在JDBC中使用事务

1)con.setAutoCommit(false),取消自动提交

2)对数据库执行一个或多个操作(一个或多个SQL语句)

3)con.commit(),提交事务(上面的第二部的多个操作就作为一个整体提交执行)

4)如果某个操作失败,通过con.rollback()回滚所有操作(撤销以上的操作,将数据恢复为执行前状态)

事务处理依赖于底层的数据库实现,不同的驱动程序对事务处理的支持程度可能不同

语法
Connection con = DriverManger.getConnection(urlString);
con.setAutoCommit(false);
Statement stm = con.createStatement();
stm.executeUpdate(sqlString);
con.transactionEndMethod; //con.commit() or con.rollback();
示例:
try{ con.setAutoCommit(false);
    stm = con.createStatement();
    stm.executeUpdate(
       “insert into t_user(id, name, password) values(1, ‘admin’,’admin’ )”);
    con.commit(); ……数据操作代码……con.commit();}
 catch(SQLException e) {
        try {con.rollback();} catch(Exception e){}
 }


二、数据库并发

数据库并发就是不同的事务对同一部分数据执行操作

事务T1和T2(或多个事务)对同一部分数据进行改,删,查操作:T1  进行修改而T2进行查询。

数据库并发容易导致的问题

1)读脏数据:事务T1修改某一数据,并将其写回数据库,事务T2读取同一数据后,T1由于某种原因被撤消,这时T1已修改过的数据恢复原值,T2读到的数据就与数据库中的数据不一致,我们称T2读到的数据就为"脏"数据,即不正确的数据。

2)不可重复读:不可重复读是指事务T1读取数据后,事务T2执行更新操作,使T1无法再现前一次读取结果。

3)幻读:事务T1按一定条件从数据库中读取某些数据记录后,事务T2插入了一些记录,当T1再次按相同条件读取数据时,发现多了一些记录。


并发控制

1)数据库并发控制就是要用正确的方式调度并发操作,使一个用户事务的执行不受其它事务的干扰,从而避免造成数据的不一致

在JDBC中,通过connection可以设置事务隔离级别来对并发进行控制

2)事务隔离级别就是事务执行时受打扰的程度,不同隔离级别对应不同的干扰程度,隔离级别越高,数据一致性就越好,但并发性越差,效率越低。

Connection中查看和设置隔离级别的方法
getTransactionIsolation():查看隔离级别
setTransactionIsolation(int  level):设置隔离级别
Connection中的事务隔离级别(Connection接口中的常量)
TRANSACTION_NONE:此级别不支持事务
TRANSACTION_READ_UNCOMMITTED:此级别允许某一事务读其他事务还没有更改完的数据。允许发生脏读 、不可重复读和幻读。
TRANSACTION_READ_COMMITTED:此级别要求某一事务只能等别的事务全部更改完才能读。可以防止发生脏读,但不可重复读和幻读有可能发生。
TRANSACTION_REPEATABLE_READ:此级别要求某一事务只能等别的事务全部更改完才能读而且禁止不可重复读。也就是可以防止脏读和不可重复读,但幻读有可能发生。
TRANSACTION_SERIALIZABLE:此级别防止发生脏读、不可重复读和幻读,事务只能一个接着一个地执行,而不能并发执行。

3)语法
con.setTransactionIsolation(Connection.isolationLevel);
示例:
Connection con = DriverManager.getConnection
(“jdbc:mysql://localhost:3306/test”, “root”, “root”);
con.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);


三、行集

1)行集是从表格式数据源中检索出来的一行或多行数据:

与结果集(ResultSet)类似(RowSet接口继承了ResultSet接口)

但是使用结果集时与数据库的连接不能断开,而行集是可以在关闭连接的情况下存在的,行集一般在关闭连接的情况下使用,只有在进行一些特殊操作

时需要才建立连接

2)行集中的数据来源:

使用JDBC驱动程序从数据库检索的数据
从其他数据源获得的数据,如文件数据

行集(Row  Set)的优点:

1)可以断开数据库连接操作数据

2)可以在分布式系统中的不同组件之间传递

3)默认可更新,可滚动,可序列化,可以方便的在网络间传输

4)可以方便的使数据在行集与JavaBean对象之间进行转换

行集中的一行数据可以封装为一个JavaBean对象

JavaBean是一个类,类中有若干私有属性,然后有与属性相关的公有的get和set方法。这样,我们可以将一行数据存入一个JavaBean对象中


行集相关接口:

javax.sql.RowSet:所有行集的父接口
java.sql.rowset.CachedRowSet:数据行容器,可以在内存中缓存各行数据,在对数据进行操作时不需要连接到数据源。可以修改CachedRowSet对象中的数据,这些修改随后可以被更新到数据库。同时,也是一个JavaBean组件,可滚动,可更新,可序列化。
java.sql.rowset.JDBCRowSet:数据行容器,可滚动,可更新。始终连接到数据库。
java.sql.rowset.WebRowSet:被缓存的行集,该行集数据可以保存为xml文件。
java.sql.rowset.JoinRowSet:数据行容器,这些数据取自那些形成SQL JOIN关系的RowSet对象,有连接和无连接的RowSet对象都可成为JOIN的一部分。
java.sql.rowset.FilteredRowSet:数据行容器,可以对数据进行条件过滤。


行集的填充:

1)传统JDBC方式
Class.forName(“com.mysql.jdbc.Driver”); 
String  connectionUrl = 
“jdbc:mysql://localhost:3306/test?user=root&password=root "; 
Connection  connection = DriverManager.getConnection(connectionUrl);
Statement  statement = connection.createStatement();
ResultSet  rs = statement.executeQuery(sql);
CachedRowSetImpl  rowset = new CachedRowSetImpl();
rowset.populate(rs); 
rs.close(); statement.close();connection.close();

2)设置行集属性连接数据库并检索数据
CachedRowSetImpl  rowset = new CachedRowSetImpl();
rowset.setUrl(“jdbc:mysql://127.0.0.1:3306/test”);
rowset.setUsername(“root”);
rowset.setPassword(“test”);
rowset.setCommand(“select  *  from  student”);
rowset.execute();
其他的填充方式:
XML
Excel
……

-------------------------------------------------------------------------------------------------------------------------------

RowSet的使用:

1、以下行集类使用Java的默认实现类
CachedRowSetImpl rowset = new CachedRowSetImpl();  
//CachedRowSetImpl是SUN定义的CachedRow接口默认实现类
Statement statement = connection.createStatement();
ResultSet  rs = statement.executeQuery(“select  *  from  table1”);
rowset.populate(rs);        //填充行集
rs.close(); statement.close();connection.close();    //关闭连接
//显示CachedRow数据,使用从结果集继承的方法
while (rowset.next()) {   
System.out.print(rowset.getString(1) + " : ");    System.out.println(rowset.getString("CompanyName"));   
}

2、//更新CachedRow数据(conn.setAutoCommit(false);)
crs.last();
crs.updateShort(3, 58);
crs.updateInt(4, 150000);
crs.updateRow();                             //更新行集
crs.acceptChanges(conn);      //更新数据库 
注意事项:

使用行集修改数据与行集中的数据填充方式无关。但是要保证acceptChanges()方法执行时有可用的连接对象,如果行集中有可用的连接对象可以调用acceptChanges(),如果行集中没有可用的连接对象,需要调用acceptChanges(Connection)方法。

使用行集对数据进行添加、修改、删除时,必须保证事务提交为非自动提交(acceptChanges()方法会调用commit()方法)。

3、//添加数据
crs.setTableName(“student”);     //添加数据必须指定
crs.moveToInsertRow();                   //标识指针的位置
crs.updateInt(“id”,33);            //添加数据时主键必须指定
crs.updateString(“name", "Shakespeare");
crs.updateShort(“age", 58);
crs.insertRow();                                 //更新行集
crs.moveToCurrentRow();            //让指针回到标识的位置
crs.acceptChanges(conn);                 //更新数据库

//删除数据
crs.first();
crs.deleteRow();                               //删除行集数据
crs.acceptChanges(conn);              //更新数据库

4、//分页1:使用结果集填充行集
rs = stm.executeQuery(“select  *  from  student”);
crs.setPageSize(4);          //设置每页行数
crs.populate(rs, 10);        //从结果集的第十行开始取4行数据填充行集

crs.nextPage();                //获得后续页数据,如果有数据返回true


注意:

此时resultset,statement(preparedstatement),connection不能关闭,否则行集无法取得后续页数据

5、//分页2:行集建立连接从数据库读取数据
CachedRowSetImpl  crs= new CachedRowSetImpl();
crs.setUrl(“jdbc:mysql://127.0.0.1:3306/test”);
crs.setUsername(“root”);
crs.setPassword(“root”);
crs.setCommand(“select  *  from  student”);
crs.setPageSize(4);       //每页行数,一定要在execute()方法执行前设置,否则无效
crs.execute();
……
crs.nextPage();          //获得下一页的数据,与结果集,连接对象无关
……

以下是相关测试代码:

package com;

import java.sql.SQLException;

import com.sun.rowset.CachedRowSetImpl;

public class JDBC4 {
	public static void main(String[] args) {
		try {
			CachedRowSetImpl rowset=new CachedRowSetImpl();
			rowset.setUrl("jdbc:mysql://localhost:3308/test");
			rowset.setUsername("root");
			rowset.setPassword("123456");
			rowset.setCommand("select*from score;");
			rowset.execute();
			//输出行集数据
			System.out.println("id\tChinese\tEnglish\thistory");
			while(rowset.next()){
				System.out.print(rowset.getInt("id")+"\t");
				System.out.print(rowset.getInt("Chinese")+"\t");
				System.out.print(rowset.getInt("English")+"\t");
				System.out.println(rowset.getInt("history"));
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
}

package com;

import java.sql.SQLException;

import com.sun.rowset.CachedRowSetImpl;

public class JDBC6 {
	public static void main(String[] args) {
		try {
			CachedRowSetImpl rowset=new CachedRowSetImpl();
			rowset.setUrl("jdbc:mysql://localhost:3308/test");
			rowset.setUsername("root");
			rowset.setPassword("123456");
			rowset.setCommand("select*from score;");
			//设置每页显示的数据条数
			rowset.setPageSize(3);
			rowset.execute();
			int i=2;
			System.out.println("第1页");
			System.out.println("id\tChinese\tEnglish\thistory");
			
			while(rowset.next()){
				System.out.print(rowset.getInt("id")+"\t");
				System.out.print(rowset.getInt("Chinese")+"\t");
				System.out.print(rowset.getInt("English")+"\t");
				System.out.println(rowset.getInt("history"));
			}
			while(rowset.nextPage()){
				System.out.println("第"+i+"页");
				i++;
				System.out.println("id\tChinese\tEnglish\thistory");
				while(rowset.next()){
					System.out.print(rowset.getInt("id")+"\t");
					System.out.print(rowset.getInt("Chinese")+"\t");
					System.out.print(rowset.getInt("English")+"\t");
					System.out.println(rowset.getInt("history"));
				}
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
		
	}
}

package com;

import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import com.sun.rowset.CachedRowSetImpl;

import util.DBUtil;

public class JDBC3 {
	public static void main(String[] args) {
		Connection conn=null;
		PreparedStatement pst=null;
		ResultSet rs=null;
		DBUtil util=new DBUtil();
		String sql="select*from score";
		try {
			conn=util.getConn();
			pst=conn.prepareStatement(sql);
			//必须设置非自动提交
			conn.setAutoCommit(false);
			rs=pst.executeQuery();
			//创建行集实例
			CachedRowSetImpl rowset=new CachedRowSetImpl();
			//填充
			rowset.populate(rs);
			rs.close();
			pst.close();
			rowset.absolute(5);
			//rowset.updateInt("id", 5);
			rowset.updateInt("English", 55);
			//更新
			rowset.updateRow();
			//提交
			rowset.acceptChanges(conn);
			
			//输出结果集之前,关闭连接
			conn.close();
			
			//输出行集数据
			System.out.println("id\tChinese\tEnglish\thistory");
			while(rowset.next()){
				System.out.print(rowset.getInt("id")+"\t");
				System.out.print(rowset.getInt("Chinese")+"\t");
				System.out.print(rowset.getInt("English")+"\t");
				System.out.println(rowset.getInt("history"));
			}
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值