JDBC如何处理事务和隔离级别
事务和JDBC相关内容不再做介绍,可以去本人博客中看看:
这里主要说一下在JDBC编程中如何处理事务?
和MySQL一样,JDBC默认不开启事务,每执行一个SQL便提交一次,在MySQL中我们需要set autocommit=0来开启事务功能,然后用begin开始一个事务,用savepoint point1设置名为point1的保存点,用rollback来回滚,用commit来提交。而在JDBC中,我们需要调用connection对象的一些方法:
- connection.setAutoCommit(false);开启事务功能
- connection.commit();提交一个事务
我们先来看看上面两个方法的使用:
import java.sql.*;
public class TranDemo {
public static void select(Connection connection,String sql1) throws SQLException {
PreparedStatement ps= null;
try {
ps = connection.prepareStatement(sql1);//预编译sql1
ps.setString(1,"08");
ResultSet resultSet = ps.executeQuery();
while (resultSet.next()){
System.out.println("SID="+resultSet.getString(1)+
" Sname="+resultSet.getString(2));
}//我这里为了方便只打印SID和Sname
} catch (SQLException e) {
e.printStackTrace();
}finally {
if(ps!=null){
ps.close();//ps是这个方法的局部变量,方法结束需要关闭ps
ps=null;//防止内存泄露
}
}
}
public static void delete(Connection connection,String sql3){
PreparedStatement ps= null;
try {
ps=connection.prepareStatement(sql3);//预编译sql3
ps.setString(1,"08");
ps.execute();
} catch (SQLException e) {
e.printStackTrace();
}finally {
if(ps!=null){
ps.close();//ps是这个方法的局部变量,方法结束需要关闭ps
ps=null;//防止内存泄露
}
}
}
public static void main(String[] args) {
Connection connection=null;
try {
String sql1="select * from Student where SID=?";
String sql3="delete from Student where SID=?";
Class.forName("com.mysql.jdbc.Driver");
connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test_sql", "root", "123456");
//开启事务功能
connection.setAutoCommit(false);//false--不自动提交
//此时事务功能就已经开启
//为了看起来有条理,我把查询、删除SID='08'这条记录的三个SQL语句的执行过程封装起来
select(connection,sql1);//先执行sql1,查询SID='08',打印SID和Sname
delete(connection,sql3);//再执行sql3,删除SID='08'这条记录
select(connection,sql1);//查询SID='08',看还能不能查到
connection.commit();//对比下调用这个方法和不调用的区别
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}finally {
if(connection!=null){
connection.close();
connection=null;
}
}
}
}
当把 connection.commit();注释起来时,执行结果如下:
可以看到两次查询同样的数据只有一次的打印结果,证明删除成功;
去MySQL中确认一下:
还查询的出来,证明删除失败,Java代码中执行的结果并没有提交(commit)给MySQL。
来看看放开connection.commit();的注释后的打印结果:
删除成功,再看看数据库中的数据:
查询不到,证明删除成功。
注意:事务中的缓存机制是将一个事务访问的数据复制一份为缓存(这里说成副本/快照更合适)去操作,当调用commit时才把缓存的内容提交给数据库。
- connection.setSavepoint();设置一个保存点
- connection.rollback()/connection.rollback(point1);回滚到初始状态/point1状态
注意!!!正常写程序时回滚语句要写在抛SQLException异常的catch块中,如果某个SQL语句执行失败抛异常就可以回滚。
这里为了掩饰回滚效果就放在try块中,方便演示:
import java.sql.*;
public class TranDemo {
public static void select(Connection connection,String sql1) throws SQLException {
PreparedStatement ps= null;
try {
ps = connection.prepareStatement(sql1);//预编译sql1
ps.setString(1,"02");
ResultSet resultSet = ps.executeQuery();
while (resultSet.next()){
System.out.println("SID="+resultSet.getString(1)+
" Sname="+resultSet.getString(2));
}//我这里为了方便只打印SID和Sname
} catch (SQLException e) {
e.printStackTrace();
}finally {
if(ps!=null){
ps.close();//ps是这个方法的局部变量,方法结束需要关闭ps
ps=null;//防止内存泄露
}
}
}
public static void delete(Connection connection,String sql3){
PreparedStatement ps= null;
try {
ps=connection.prepareStatement(sql3);//预编译sql3
ps.setString(1,"02");
ps.execute();
} catch (SQLException e) {
e.printStackTrace();
}finally {
if(ps!=null){
ps.close();//ps是这个方法的局部变量,方法结束需要关闭ps
ps=null;//防止内存泄露
}
}
}
public static void main(String[] args) throws SQLException {
Connection connection=null;
Savepoint point1=null;
try {
String sql1="select * from Student where SID=?";
String sql3="delete from Student where SID=?";
Class.forName("com.mysql.jdbc.Driver");
connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test_sql", "root", "123456");
//开启事务功能
connection.setAutoCommit(false);//false--不自动提交
//此时事务功能就已经开启
//为了看起来有条理,我把查询、删除SID='02'这条记录的三个SQL语句的执行过程封装起来
select(connection,sql1);//1.先执行sql1,查询SID='02',打印SID和Sname
point1= connection.setSavepoint();//设置一个保存点point1
delete(connection,sql3);//2.执行sql3,删除SID='02'这条记录
select(connection,sql1);//查询SID='02',不打印则删除成功
connection.rollback(point1);//回滚到point1
select(connection,sql1);//打印出SID='02'的SID和Sname则回滚成功
connection.commit();//对比下调用这个方法和不调用的区别,不能写在rollback操作前
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}finally {
if(point1!=null){
connection.releaseSavepoint(point1);
point1=null;
}
if(connection!=null){
connection.close();
connection=null;
}
}
}
}
运行结果:
打印了两次而不是三次,证明在事务的缓存(副本)中确实删除了一次,最后又回滚到point1,删除掉的数据又回来了。
- connection.releaseSavepoint(point1);删除point1保存点
- connection.setTransactionIsolation(int level);设置事务的隔离级别,这个隔离级别只对当前连接有效,在MySQL中一个连接就是打开的一个窗口(终端);在JDBC中,一个连接就是一个connection。
剖析源码看一下隔离级别对应的int值: