JavaWeb-Jdbc中使用事物
2020-01-06 本文已影响0人
Tian_Peng
原文连接:http://www.cnblogs.com/xdp-gacl/p/3984001.html
创建数据库和测试表
create database if NOT EXISTS jdbcStudy character set utf8 collate utf8_general_ci;
use jdbcStudy;
drop TABLE if exists `account`;
create table account(
id int primary key auto_increment,
name varchar(40),
money float
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into account(name,money) values('A',1000);
insert into account(name,money) values('B',1000);
insert into account(name,money) values('C',1000);
当Jdbc程序向数据库获得一个Connection对象时,默认情况下这个Connection对象会自动向数据库提交在它上面发送的SQL语句。
若想关闭这种默认提交方式,让多条SQL在一个事务中执行,可使用下列的JDBC控制事务语句:
- Connection.setAutoCommit(false); //开启事务(start transaction)
- Connection.rollback(); //回滚事务(rollback)
- Connection.commit(); //提交事务(commit)
JDBC使用事务范例
在JDBC代码中演示银行转帐案例,使如下转帐操作在同一事务中执行:
update account set money=money-100 where name='A'
update account set money=money+100 where name='B'
代码如下:
public class JdbcTransactionTest {
/**
* @Method: testTransaction1
* @Description: 模拟转账成功时的业务场景
* @Anthor:TP
*/
@Test
public void testTransaction1() {
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
conn.setAutoCommit(false);//通知数据库开启事务(start transaction)
String sql1 = "update account set money=money-100 where name='A'";
st = conn.prepareStatement(sql1);
st.executeUpdate();
String sql2 = "update account set money=money+100 where name='B'";
st = conn.prepareStatement(sql2);
st.executeUpdate();
conn.commit();//上面的两条SQL执行Update语句成功之后就通知数据库提交事务(commit)
System.out.println("成功!!!"); //log4j
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtils.release(conn, st, rs);
}
}
/**
* @Method: testTransaction2
* @Description: 模拟转账过程中出现异常导致有一部分SQL执行失败后让数据库自动回滚事务
* @Anthor:TP
*/
@Test
public void testTransaction2() {
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
conn.setAutoCommit(false);//通知数据库开启事务(start transaction)
String sql1 = "update account set money=money-100 where name='A'";
st = conn.prepareStatement(sql1);
st.executeUpdate();
//用这句代码模拟执行完SQL1之后程序出现了异常而导致后面的SQL无法正常执行,事务也无法正常提交,此时数据库会自动执行回滚操作
int x = 1 / 0;
String sql2 = "update account set money=money+100 where name='B'";
st = conn.prepareStatement(sql2);
st.executeUpdate();
conn.commit();//上面的两条SQL执行Update语句成功之后就通知数据库提交事务(commit)
System.out.println("成功!!!");
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtils.release(conn, st, rs);
}
}
/**
* @Method: testTransaction3
* @Description: 模拟转账过程中出现异常导致有一部分SQL执行失败时手动通知数据库回滚事务
* @Anthor:TP
*/
@Test
public void testTransaction3() {
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
conn.setAutoCommit(false);//通知数据库开启事务(start transaction)
String sql1 = "update account set money=money-100 where name='A'";
st = conn.prepareStatement(sql1);
st.executeUpdate();
//用这句代码模拟执行完SQL1之后程序出现了异常而导致后面的SQL无法正常执行,事务也无法正常提交
int x = 1 / 0;
String sql2 = "update account set money=money+100 where name='B'";
st = conn.prepareStatement(sql2);
st.executeUpdate();
conn.commit();//上面的两条SQL执行Update语句成功之后就通知数据库提交事务(commit)
System.out.println("成功!!!");
} catch (Exception e) {
try {
//捕获到异常之后手动通知数据库执行回滚事务的操作
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
} finally {
JdbcUtils.release(conn, st, rs);
}
}
}
设置事务回滚点
在开发中,有时候可能需要手动设置事务的回滚点,在JDBC中使用如下的语句设置事务回滚点
Savepoint sp = conn.setSavepoint();
Conn.rollback(sp);
Conn.commit();//回滚后必须通知数据库提交事务
设置事务回滚点范例:
public class JdbcTransactionRollBackPointTest {
/**
* @Method: testTransaction1
* @Description: 模拟转账成功时的业务场景
* @Anthor:TP
*/
@Test
public void testTransaction1() {
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
Savepoint sp = null;
try {
conn = JdbcUtils.getConnection();
conn.setAutoCommit(false);//通知数据库开启事务(start transaction)
String sql1 = "update account set money=money-100 where name='A'";
st = conn.prepareStatement(sql1);
st.executeUpdate();
//设置事务回滚点
sp = conn.setSavepoint();
String sql2 = "update account set money=money+100 where name='B'";
st = conn.prepareStatement(sql2);
st.executeUpdate();
//程序执行到这里出现异常,后面的sql3语句执行将会中断
int x = 1 / 0;
String sql3 = "update account set money=money+100 where name='C'";
st = conn.prepareStatement(sql3);
st.executeUpdate();
conn.commit();
} catch (Exception e) {
try {
/**
* 我们在上面向数据库发送了3条update语句,
* sql3语句由于程序出现异常导致无法正常执行,数据库事务而已无法正常提交,
* 由于设置的事务回滚点是在sql1语句正常执行完成之后,sql2语句正常执行之前,
* 那么通知数据库回滚事务时,不会回滚sql1执行的update操作
* 只会回滚到sql2执行的update操作,也就是说,上面的三条update语句中,sql1这条语句的修改操作起作用了
* sql2的修改操作由于事务回滚没有起作用,sql3由于程序异常没有机会执行
*/
conn.rollback(sp);//回滚到设置的事务回滚点
conn.commit();//回滚了要记得通知数据库提交事务
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
} finally {
JdbcUtils.release(conn, st, rs);
}
}
}
其中JdbcUtils.java代码如下:
public class JdbcUtils {
private static String driver;
private static String url;
private static String username;
private static String password;
static {
try {
//读取db.properties文件中的数据库连接信息
InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
Properties prop = new Properties();
prop.load(in);
//获取数据库连接驱动
driver = prop.getProperty("driver");
//获取数据库连接URL地址
url = prop.getProperty("url");
//获取数据库连接用户名
username = prop.getProperty("username");
//获取数据库连接密码
password = prop.getProperty("password");
//加载数据库驱动
Class.forName(driver);
} catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
/**
* @return Connection 数据库连接对象
* @throws SQLException
* @Method: getConnection
* @Description: 获取数据库连接对象
* @Anthor:TP
*/
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url, username, password);
}
/**
* @param conn 数据库连接对象
* @param st Statement
* @param rs rs
* @Method: release
* @Description: 释放资源
* 要释放的资源包括Connection数据库连接对象,负责执行SQL命令的Statement对象,存储查询结果的ResultSet对象
* @Anthor:TP
*/
public static void release(Connection conn, Statement st, ResultSet rs) {
if (rs != null) {
try {
//关闭存储查询结果的ResultSet对象
rs.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if (st != null) {
try {
//关闭负责执行SQL命令的Statement对象
st.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
//关闭Connection数据库连接对象
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}