JDBC第四章知识点总结——JDBC高级特性2--事务,并发控制,行集
知识点预览
事务
并发控制
Row set(行集)
JDBC异常与警告
事务
1. 事务—银行转账问题
a) 张三将1000元钱转到李四的账号,程序处理需要的步骤:
1、张三的账号减掉1000元;
2、李四的账号增加1000元;
3、转账过程记录;
只有三个步骤全部正确完成转账才算成功。
b) 但是程序在实际运行时,执行完步骤1后,程序出现错误导致步骤2和步骤3无法顺利执行:
张三的钱少了1000元;
李四的钱没变;
1000元去了哪里?
2. 事务
a) 数据库:默认情下,JDBC的每个数据操作SQL语句都是自动提交,每个语句分开单独执行
b) 银行转账问题:需要多次数据操作才能完成
c) 可能产生的问题
需要多次数据操作才能完成的业务无法完成,数据不一致
d) 解决办法
银行转账问题:将完成转账功能的所有数据操作放在一起作为一个整体执行,要么全部都执行成功,要么全部恢复
e) 事务是指一个工作单元,它包含了一组添加,删除,修改等数据操作命令,这组命令作为一个整体向系统提交执行,要么都执行成功,要么全部恢复
f) 在JDBC中使用事务
con.setAutoCommit(false),取消自动提交
对数据库执行一个或多个操作(一个或多个SQL语句)
con.commit(),提交事务(上面的第二部的多个操作就作为一个整体提交执行)
如果某个操作失败,通过con.rollback()回滚所有操作(撤销以上的操作,将数据恢复为执行前状态)
g) 事务处理依赖于底层的数据库实现,不同的驱动程序对事务处理的支持程度可能不同
h) 语法
Connection con =DriverManger.getConnection(urlString);
con.setAutoCommit(false);
Statement stm =con.createStatement();
stm.executeUpdate(sqlString);
con.transactionEndMethod;//con.commit() or con.rollback();
i) 示例:
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){}
}
并发控制
1. 数据库并发
a) 数据库并发就是不同的事务对同一部分数据执行操作
事务T1和T2(或多个事务)对同一部分数据进行改,删,查操作:T1 进行修改而T2进行查询。
b) 数据库并发容易导致的问题
i. 读脏数据:事务T1修改某一数据,并将其写回数据库,事务T2读取同一数据后,T1由于某种原因被撤消,这时T1已修改过的数据恢复原值,T2读到的数据就与数据库中的数据不一致,我们称T2读到的数据就为"脏"数据,即不正确的数据。
ii. 不可重复读:不可重复读是指事务T1读取数据后,事务T2执行更新操作,使T1无法再现前一次读取结果。
iii. 幻读:事务T1按一定条件从数据库中读取某些数据记录后,事务T2插入了一些记录,当T1再次按相同条件读取数据时,发现多了一些记录。
2. 并发控制
a) 数据库并发控制就是要用正确的方式调度并发操作,使一个用户事务的执行不受其它事务的干扰,从而避免造成数据的不一致
b) 在JDBC中,通过connection可以设置事务隔离级别来对并发进行控制
事务隔离级别就是事务执行时受打扰的程度,不同隔离级别对应不同的干扰程度,隔离级别越高,数据一致性就越好,但并发性越差,效率越低。
c) Connection中查看和设置隔离级别的方法
getTransactionIsolation():查看隔离级别
setTransactionIsolation(int level):设置隔离级别
d) Connection中的事务隔离级别(Connection接口中的常量)
TRANSACTION_NONE:此级别不支持事务
TRANSACTION_READ_UNCOMMITTED:此级别允许某一事务读其他事务还没有更改完的数据。允许发生脏读、不可重复读和幻读。
TRANSACTION_READ_COMMITTED:此级别要求某一事务只能等别的事务全部更改完才能读。可以防止发生脏读,但不可重复读和幻读有可能发生。
TRANSACTION_REPEATABLE_READ:此级别要求某一事务只能等别的事务全部更改完才能读而且禁止不可重复读。也就是可以防止脏读和不可重复读,但幻读有可能发生。
TRANSACTION_SERIALIZABLE:此级别防止发生脏读、不可重复读和幻读,事务只能一个接着一个地执行,而不能并发执行。
e) 语法
con.setTransactionIsolation(Connection.isolationLevel);
f) 示例:
Connection con =DriverManager.getConnection
(“jdbc:mysql://localhost:3306/test”,“root”, “root”);
con.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
。。。
Rowset(行集)
1. 行集
a) 行集是从表格式数据源中检索出来的一行或多行数据
与结果集(ResultSet)类似(RowSet接口继承了ResultSet接口)
但是使用结果集时与数据库的连接不能断开,而行集是可以在关闭连接的情况下存在的,行集一般在关闭连接的情况下使用,只有在进行一些特殊操作时需要才建立连接
b) 行集中的数据来源
使用JDBC驱动程序从数据库检索的数据
从其他数据源获得的数据,如文件数据
2. 行集(Row Set)的优点
a) 可以断开数据库连接操作数据
b) 可以在分布式系统中的不同组件之间传递
c) 默认可更新,可滚动,可序列化,可以方便的在网络间传输
d) 可以方便的使数据在行集与JavaBean对象之间进行转换
行集中的一行数据可以封装为一个JavaBean对象
JavaBean是一个类,类中有若干私有属性,然后有与属性相关的公有的get和set方法。这样,我们可以将一行数据存入一个JavaBean对象中
3. 行集相关接口
a) javax.sql.RowSet:所有行集的父接口
b) java.sql.rowset.CachedRowSet:数据行容器,可以在内存中缓存各行数据,在对数据进行操作时不需要连接到数据源。可以修改CachedRowSet对象中的数据,这些修改随后可以被更新到数据库。同时,也是一个JavaBean组件,可滚动,可更新,可序列化。
c) java.sql.rowset.JDBCRowSet:数据行容器,可滚动,可更新。始终连接到数据库。
d) java.sql.rowset.WebRowSet:被缓存的行集,该行集数据可以保存为xml文件。
e) java.sql.rowset.JoinRowSet:数据行容器,这些数据取自那些形成SQL JOIN关系的RowSet对象,有连接和无连接的RowSet对象都可成为JOIN的一部分。
f) java.sql.rowset.FilteredRowSet:数据行容器,可以对数据进行条件过滤。
4. 行集的填充
a) 由驱动或厂商实现相关接口得到可以使用的行集类
我们演示中使用的是SUN的默认实现类
b) 传统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 = newCachedRowSetImpl();
rowset.populate(rs);
rs.close(); statement.close();connection.close();
c) 设置行集属性连接数据库并检索数据
CachedRowSetImpl rowset = newCachedRowSetImpl();
rowset.setUrl(“jdbc:mysql://127.0.0.1:3306/test”);
rowset.setUsername(“root”);
rowset.setPassword(“test”);
rowset.setCommand(“select * from student”);
rowset.execute();
d) 其他的填充方式:
XML
Excel
……
5. RowSet的使用
a) RowSet的使用1——展示数据
以下行集类使用Java的默认实现类
CachedRowSetImplrowset = new CachedRowSetImpl();
//CachedRowSetImpl是SUN定义的CachedRow接口默认实现类
Statementstatement = 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"));
}
b) RowSet的使用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()方法)。
c) RowSet的使用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); //更新数据库
d) RowSet的使用4--分页1
//分页1:使用结果集填充行集
rs =stm.executeQuery(“select * from student”);
crs.setPageSize(4); //设置每页行数
crs.populate(rs,10); //从结果集的第十行开始取4行数据填充行集
…
crs.nextPage(); //获得后续页数据,如果有数据返回true
…
注意:
此时resultset,statement(preparedstatement),connection不能关闭,否则行集无法取得后续页数据
e) RowSet的使用5--分页2
//分页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(); //获得下一页的数据,与结果集,连接对象无关
……
6. 行集的事件
a) 行集支持事件,当行集中的数据发生变化时就会触发相关事件,从而可以通知使用行集的对象
b) 当行集数据发生变化时获得通知的对象需要实现RowSetListener接口成为事件侦听器并在行集对象中注册
c) 事件侦听器注册使用addRowSetListener()方法
d) crs.addRowSetListener(事件侦听器对象);
e) 行集可以生成三种类型的事件
游标移动事件
一行数据改变事件
数据全部改变事件
JDBC异常与警告
1. JDBC异常处理
a) 数据库应用程序包含以下部分
应用程序
JDBC代码
数据库
b) 各部分不一致就可能出现异常或警告
c) 通常使用SQLException和SQLWarning表示异常和警告信息
SQLException表示异常,如驱动不匹配,进行了数据库不支持的操作等,会导致应用程序终止
SQLWarning表示警告,如某些操作不规范但仍然能执行,不会导致应用程序终止
2. SQLException类
a) 使用JDBC访问数据库时,下列情况会导致SQL异常的产生
JDBC与数据库服务器之间的通信信息丢失
错误的命令
使用数据库不支持的函数
访问不存在的列
b) SQLException类提供了数据库访问错误或其他错误的信息,主要包括:
getNextException()方法返回下一个SQL异常对象
getErrorCode()方法返回用整数表示的错误代码(由数据库厂商提供)
getMessage()方法返回用字符串表示的错误信息
3. SQLWarning类
a) 是SQLException的子类
b) 因非致命SQL状态而生成
c) 不会导致应用程序中断运行
d) 存储最近产生的SQL警告信息
e) 使用getNextWaring()方法获得下一个警告信息