JDBC高级特性事务并发控制和行集

Posted llguanli

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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进行查询。

数据库并发easy导致的问题

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();
		}
	}
}
























































以上是关于JDBC高级特性事务并发控制和行集的主要内容,如果未能解决你的问题,请参考以下文章

11MySQL存储引擎

JDBC事务的相关知识

PHP PHP - 轻量级数据库和行集类,ActiveRecord样式函数

InnoDB存储引擎的高级特性大盘点

Linux九阴真经之九阴白骨爪残卷11(并发访问控制和事务Transactions)

JDBC事务