日期:2014-05-16  浏览次数:20468 次

SQLServer数据库笔记(三)
批处理的错误处理
联机丛书中关于批处理的介绍已经很清晰了,摘录如下:
引用
批处理是包含一个或多个 Transact-SQL 语句的组,从应用程序一次性地发送到SQL Server执行。SQL Server 将批处理语句编译成一个可执行单元,此单元称为执行计划。执行计划中的语句每次执行一条。
编译错误(如语法错误)使执行计划无法编译,从而导致批处理中的任何语句均无法执行。
运行时错误(如算术溢出或违反约束)会产生以下两种影响之一:
大多数运行时错误将停止执行批处理中当前语句和它之后的语句。少数运行时错误(如违反约束)仅停止执行当前语句。而继续执行批处理中其它所有语句。
在生成错误的批处理、存储过程或触发器中,@@ERROR 是唯一可用部分。@@ERROR用来标记SQL语句的执行状况。@@ERROR=0,表示执行成功,否则,表示执行错误。由于 @@ERROR 在每一条语句执行后被清除并且重置,应在语句执行后立即检查它。
错误的所有其它部分,都只返回到那些能够用 API 错误处理机制进行错误处理的应用程序中。

关于上述说明的含义如下:
对于SQLServer服务器而言,虽然对于批处理执行过程中的语句错误情况,服务器已返回足够的错误信息。但SQLServer2000的T-SQL语言并没有提供类似Java的Try catch机制来捕获和处理这些错误。T-SQL需要用户在每一条语句执行后通过@@error来判断是否有语句执行错误。或者是在开启事务的情况下,打开XACT_ABORT开关,这样,当批处理中任意语句出现错误时,都将停止所有后续语句的执行并回滚事务。
对于JDBC程序而言,底层的数据库驱动在得到错误消息返回时,会产生并抛出SQLException异常。XACT_ABORT开关的状态不影响事务正确的被提交或者回滚。
联机丛书的说明如下(但还有些疑惑?):
引用
对于大多数 OLE DB 提供程序(包括 SQL Server),隐性或显式事务中的数据修改语句必须将 XACT_ABORT 设置为 ON。唯一不需要该选项的情况是提供程序支持嵌套事务时。
SET XACT_ABORT { ON | OFF }指定当 Transact-SQL 语句产生运行时错误时,SQL Server是否自动回滚当前事务。当 SET XACT_ABORT 为 ON 时,如果 Transact-SQL 语句产生运行时错误,整个事务将终止并回滚。为 OFF 时,只回滚产生错误的 Transact-SQL 语句,而事务将继续进行处理。

比如对于下面的SQL语句:
insert into cat values('11', 'aaa')
insert into cat values(null, 'bbb')//违反约束
insert into cat values('33', 'ccc')
GO

SQL查询分析器返回:
引用
(所影响的行数为 1 行)
服务器: 消息 515,级别 16,状态 2,行 1
无法将 NULL 值插入列 'id',表 'test.dbo.cat';该列不允许空值。INSERT 失败。
语句已终止。
(所影响的行数为 1 行)

而对于JDBC程序,第2条SQL语句将导致抛出异常:
public static void main(String[] args) throws Exception {
	Properties info = new Properties();
	info.put(Messages.get(Driver.BATCHSIZE), 30);
	info.put("USER", "sa");
	info.put("PASSWORD", "");
	Connection conn = null;
	Statement stmt = null;
	try {
		Class.forName("net.sourceforge.jtds.jdbc.Driver");
		conn = DriverManager.getConnection("jdbc:jtds:sqlserver://localhost:1433/test", info);
		conn.setAutoCommit(false);
		stmt = conn.createStatement();
		stmt.addBatch("insert into cat values('11', 'aaa')");
		stmt.addBatch("insert into cat values(null, 'bbb')");
		stmt.addBatch("insert into cat values('33', 'ccc')");
		stmt.executeBatch();
		conn.commit();
	} catch (Exception e) {
		e.printStackTrace();
		if (conn != null) conn.rollback();
	} finally {
		if (stmt != null) stmt.close();
		if (conn != null) conn.close();
	}
}


java.sql.Statement.executeBatch()的API说明如下:
引用
int[] executeBatch() throws SQLException将一批命令提交给数据库来执行,如果全部命令执行成功,则返回更新计数组成的数组。
如果批量更新中的命令之一无法正确执行,则此方法抛出 BatchUpdateException,并且 JDBC 驱动程序可能继续处理批处理中的剩余命令,也可能不执行。无论如何,驱动程序的行为必须与特定的 DBMS 一致,要么始终继续处理命令,要么永远不继续处理命令。