public static void updateUser(User user) {
SQLProcessor sqlProcessor = new SQLProcessor();
sqlProcessor.executeUpdate(SQL_UPDATE_USER,
new Object[] {user.getUserName(),
user.getEmail(),
new Integer(user.getId())},
new MandatoryUpdateProcessor());
}
事务模型
在数据库中,事务和独立的SQL语句的区别在于事务在生命期内使用一个数据库连接,并且AutoCommit属性必须被设为False。因此我们需要指定事务何时开始,何时结束,并且在事务结束时提交事务。我们可以重用SQLProcessor中的大部分代码来处理事务。也许在最开始读者会问为什么要把执行更新和处理更新的工作放在 executeUpdate()和handleUpdate()两个函数中完成--实际上它们是可以被合并到同一个函数中的。这样做的原因是把处理数据库连接的代码和处理SQL操作的代码分离开来。对于需要在多个SQL操作间共用数据库连接的事务模型来说,这种方案便于编码。
在事务中,我们需要保存事务的状态,特别是数据库连接的状态。前面的SQLProcessor中没有保存状态的属性,为了保证对SQLProcessor类的重用,我们设计了一个包装类,该类包装了SQLProcessor类,并且可以维护事务在生命周期内的状态。
public class SQLTransaction {
private SQLProcessor sqlProcessor;
private Connection conn;
// 缺省构造方法,该方法初始化数据库连接,并将AutoCommit设定为False
...
public void executeUpdate(String sql, Object[] pStmntValues,
UpdateProcessor processor) {
// 获得结果。如果更新操作失败,回滚到事务起点并抛出异常
try {
sqlProcessor.handleUpdate(sql, pStmntValues, processor, conn);
} catch(DatabaseUpdateException e) {
rollbackTransaction();
throw e;
}
}
public void commitTransaction() {
// 事务结束,提交更新并回收资源
try {
conn.commit();
sqlProcessor.closeConn(conn);
// 如果发生异常,回滚到事务起点并回收资源
} catch(Exception e) {
rollbackTransaction();
throw new DatabaseUpdateException("无法提交当前事务");
}
}
private void rollbackTransaction() {
// 回滚到事务起点并回收资源
try {
conn.rollback();
conn.setAutoCommit(true);
sqlProcessor.closeConn(conn);
// 如果在回滚过程中发生异常,忽略该异常
} catch(SQLException e) {
sqlProcessor.closeConn(conn);
}
}
}
SQLTransaction中出现了一些新方法,这些方法主要是用来处理数据库连接和进行事务管理的。当一个事务开始时,SQLTransaction对象获得一个新的数据库连接,并将连接的AutoCommit设定为False,随后的所有SQL语句都是用同一个连接。
只有当commitTransaction()被调用时,事务才会被提交。如果执行SQL语句的过程中发生了异常,程序会自动发出一个回滚申请,以恢复程序对数据库所作的改变。对于开发人员来说,不需要担心在出现异常后处理回滚或关闭连接的工作。下面是一个使用事务模型的例子。
public static void updateUsers(User[] users) {
// 开始事务
SQLTransaction trans = sqlProcessor.startTransaction();
// 更新数据
User user = null;
for(int i = 0; i < users.length; i++) {
user = users[i];
trans.executeUpdate(SQL_UPDATE_USER,
new Object[] {user.getUserName(),
user.getFirstName(),
user.getLastName(),
user.getEmail(),
new Integer(user.getId())},
new MandatoryUpdateProcessor());
}
// 提交事务
trans.commitTransaction();
}
在例子中我们只使用了更新语句(在大多数情况下事务都是由更新操作构成的),查询语句的实现方法和更新语句类似。
问题
在实际使用上面提到的这些模型时,我遇到了一些问题,下面是这些问题的小结,希望对大家有所帮助。
自定义数据库连接
在事务处理的时候,有可能发生在多个事务并存的情况下,它们使用的数据库连接不同的情况。ConnectionManager需要知道它应该从数据库连接池中取出哪一个连接。你可以简单修改一下模型来满足上面的要求。例如在executeQuery()和executeUpdate()方法中,你可以把数据库连接作为参数,然后将它们传送给ConnectionManager对象。请记住所有的连接管理都应该放在executeXXX()方法中。另外一种解决方案,也是一种更面向对象化的解决方案,是将一个连接工厂作为参数传递给SQLProcessor的构造函数。对于不同的连接工厂类型,我们需要不同的SQLProcessor对象。
ResultProcessor类的返回值:对象数组还是List?
为什么ResultProcessor接口中的process()方法返回的是对象数组呢?怎么不使用List类呢?这是由于在很多实际的应用中,SQL查询在大多数情况下值返回一行数据,在这种情况下,使用List对象会有些多余了。但是如果你确信SQL查询将返回多行结果,你可以使用List对象。
数据库操作异常
我们可以用多个自定义的数据库操作异常类来替代运行时发生的SQLException异常。最好在这些自定义的异常类时继承RuntimeException类,这样可以将这些异常进行集中处理。也许你会认为因该将异常处理放在发生异常的地方。但是我们设计这个的模型的目的之一是在JDBC应用程序开发中去掉或弱化异常处理的部分,只有使用RuntimeException我们才可能达到这个目的。
上一页 [1] [2]
[办公软件]在sybase中插入图片、PDF、文本文件 [办公软件]安装Sybase ASE
[办公软件]linux指令大全(完整篇) [办公软件]Linux新手入门常用命令大全
[办公软件]在RedHat Linux 9里安装gaim0.80 [办公软件]浅谈Linux 下Java 1.5 汉字方块问题解决方法
[办公软件]Linux程序员必读:中文化与GB18030标准 [办公软件]linux指令大全
[办公软件]制作Linux启动盘的四种方法 [办公软件]Linux文件系统的反删除方法