一、定义
- 事务:一个最小的不可再分的工作单元;通常一个事务对应一个完整的业务(例如银行账户转账业务,该业务就是一个最小的工作单元)
- 一个完整的业务需要批量的DML(insert、update、delete)语句共同联合完成
- 事务只和DML语句有关,或者说DML语句才有事务。这个和业务逻辑有关,业务逻辑不同,DML语句的个数不同
二、应用场景
设想网上购物的一次交易,其付款过程至少包括以下几步数据库操作:
- 更新客户所购商品的库存信息
- 保存客户付款信息–可能包括与银行系统的交互
- 生成订单并且保存到数据库中
- 更新用户相关信息,例如购物数量等等
正常的情况下,这些操作将顺利进行,最终交易成功,与交易相关的所有数据库信息也成功地更新。但是,如果在这一系列过程中任何一个环节出了差错,例如在更新商品库存信息时发生异常、该顾客银行帐户存款不足等,都将导致交易失败。一旦交易失败,数据库中所有信息都必须保持交易前的状态不变,比如最后一步更新用户信息时失败而导致交易失败,那么必须保证这笔失败的交易不影响数据库的状态–库存信息没有被更新、用户也没有付款,订单也没有生成。否则,数据库的信息将会一片混乱而不可预测。
数据库事务正是用来保证这种情况下交易的平稳性和可预测性的技术
三、代码形式表示
1、没有事务
向数据库插入三条数据,其中第二条语句错误
/**
* 没有事务
*
* setAutoCommit(boolean b) :
* 设置是否自动提交
* 参数为false则不提交,需要手动提交
* 参数为true则提交
*/
public class NoTransaction {
public static void main(String[] args) {
Connection connection = null;
Statement statement = null;
try {
// 获取链接对象
connection = DBUtil.getConnection();
// 获取执行SQL语句的对象
statement = connection.createStatement();
/**
* 一次性添加三条记录
*/
statement.addBatch("insert into test(id,name) values(101,'无事务测试1')");
// 故意写错字段,就是想让它出意外
statement.addBatch("insert into test(id,nam) values(102,'无事务测试2')");
statement.addBatch("insert into test(id,name) values(103,'无事务测试3')");
/**
* 这种条件下,没有关闭自动提交,除了第二条错误的语句没有添加
* 另外两条成功添加
* 如果是转账业务的话,这种情况就会出错
*/
statement.executeBatch();
System.out.println("执行成功");
} catch (Exception e) {
e.printStackTrace();
} finally {
DBUtil.close(statement);
DBUtil.close(connection);
}
}
}
2、有事务
/**
* 关闭自动提交事务,手动提交,如果有错误,回滚。
*
* setAutoCommit(boolean b) :
* 设置是否自动提交
* 参数为false则不提交,需要手动提交
* 参数为true则提交
*/
public class Transaction {
public static void main(String[] args) {
Connection connection = null;
Statement statement = null;
try {
// 链接对象
connection = DBUtil.getConnection();
/**
* 关闭自动提交,即使SQL语句正确,数据库表也不会发生变化
*/
connection.setAutoCommit(false);
// 获取执行SQL语句的对象
statement = connection.createStatement();
/**
* 执行多条语句
*/
statement.addBatch("insert into test(id,name) values(201,'有事务测试1')");
/**
* 错误语句,字段 nam 不正确
*/
statement.addBatch("insert into test(id,nam) values(202,'有事务测试2')");
statement.addBatch("insert into test(id,name) values(203,'有事务测试3')");
// 执行
statement.executeBatch();
System.out.println("执行成功");
/**
* 手动提交事务
*/
connection.commit();
// 打开自动提交
connection.setAutoCommit(true);
} catch (Exception e) {
e.printStackTrace();
try {
/**
到这里说明有错误,需要回滚
如果不回滚,直接打开自动提交的话,那么201和203还是会被添加进去
因为没有提交之前的操作被保存到数据库缓存区,执行commit命令的时候,才会强制刷新到数据库中
如果不回滚,等于没有使用事务一样,回滚就会跳转到执行操作之前
所以 回滚之后会把对应的缓存区清空,然后再开启自动提交,就不会出现问题了
*/
connection.rollback();
/**
* 再次把自动提交打开
*/
connection.setAutoCommit(true);
} catch (SQLException e1) {
e1.printStackTrace();
}
} finally {
DBUtil.close(statement);
DBUtil.close(connection);
}
}
}