事务的开启是由JDBC驱动或数据库隐式决定的。Connection对象的autoCommit属性决定什么时候结束一个事务。启动自动提交后,会在每个SQL语句执行完毕后自动提交事务,当Connection对象创建时,默认情况下,事务提交是自动开启的。Connection接口中提供setAutoCommit()方法,可以禁用自动提交。此时,需要显示的调用Connection提供的commit()方法提交事务或rollback()回滚事务。
事务隔离级别用于指定事务中对数据的操作对其它事务的“可见性”。不同的事务隔离级别能够解决并发访问数据带来的不同的并发问题,而且会直接影响并发访问效率,数据并发访问可能会导致如下问题:
脏读:这种情况发生在事务中允许读取未提交的数据。例如,A事务修改了一条数据,但是未提交修改,此时A事务对数据的修改对其他事务是可见的,B事务中能够读取A事务未提交的修改。一旦A事务回滚,则B事务中读取的就是不正确的数据。
不可重复读:这种情况发生在如下场景:
1 A事务中读取一行数据
2 B事务修改了该行数据
3 A事务中再次读取该行数据将得到不同的结果。
幻读:这种情况发生在如下场景:
1 A事务中通过where条件读取若干行。
2 B事务中插入了符合条件的若干新记录。
3 A事务中通过相同条件再次读取数据时将会读取到B事务中插入的数据。
JDBC中定义了5种隔离级别:
· TRANSACTION_NONE: 表示不支持事务,这意味着它是不兼容JDBC规范的驱动。
· TRANSACTION_READ_UNCOMMITTED: 允许事务读取未提交更改的数据,这意味着可能会出现脏读、不可重复读和幻读。
· TRANSACTION_READ_COMMITTED: 表示在事务中进行的任何数据更改,在提交事务之前对其它事务是不可见的。可以防止脏读,但是不能解决幻读和不可重复读。
· TRANSACTION_REPEATABLE_READ: 顾名思义,该隔离级别能够解决脏读和不可重复读,但是不能解决幻读问题。
· TRANSACTION_SERIALIZABLE: 所有事务串行执行,能够有效解决脏读、不可重复读和幻读的问题,但是并发效率较低。
try {
connection.setAutoCommit(false);//设置手动提交事务
String sql1="insert into t_log(logMess,logTime) values('好好学习',now())";
String sql2="insert into t_log(logMess,logTime) values('天天向上',now())";
smt= connection.createStatement();
smt.executeUpdate(sql1);//执行第一个sql
Savepoint savepoint = connection.setSavepoint("sp1");//设置事务保存点,事务可以回滚到该保存点
smt.executeUpdate(sql2);//执行第二个sql
connection.rollback(savepoint);//回滚到保存点
connection.commit();//提交事务,此时数据库中只插入的第一条数据
} catch (SQLException e) {
e.printStackTrace();
}
try {
connection.setAutoCommit(false);//设置手动提交事务
Savepoint savepoint = connection.setSavepoint("sp1");//设置事务保存点,事务可以回滚到该保存点
String sql1="insert into t_log(logMess,logTime) values('好好学习',now())";
String sql2="insert into t_log(logMess,logTime) values('天天向上',now())";
smt= connection.createStatement();
int m= smt.executeUpdate(sql1);//执行第一个sql
m+=smt.executeUpdate(sql2);//执行第二个sql
if(m>=2){//如果都成功则,提交事务
connection.commit();//提交事务,此时数据库中只插入的第一条数据
}else {//有任何一个失败,则回滚事务
connection.rollback(savepoint);//回滚到保存点
}
} catch (SQLException e) {
e.printStackTrace();
}