版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_34928644/article/details/82798131
事务transaction
- 原子性(atomicity):组成事务处理的语句形成了一个逻辑单元,不能只执行其中的一部分。
- 一致性(consistency):在事务处理执行前后,数据库是一致的(数据库数据完整性约束)。
- 隔离性(isolcation):一个事务处理对另一个事务处理的影响。
- 持续性(durability):事务处理的效果能够被永久保存下来 。
- 一个事务只会有一个结果:要么成功、要么失败。
MySQL事务:开启、提交、回滚
Start transaction;开始一个事务。
Commit;提交所做的修改。
Rollback;回滚所做的修改。如果在操作时出错,应该从新开始一个事务。
数据库表结构:
CREATE TABLE `tb_user` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`username` VARCHAR(10) DEFAULT NULL,
`password` VARCHAR(10) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
没有事务下执行sql语句
演示没有事务下执行 多条SQL语句
从上往下执行,遇到非法的sql语句就会抛出异常,由于出现异常,接下来的sql语句将不会执行。
@Test
public void noTransaction() throws Exception {
Connection con = ConnUtil.getConnection();
Statement st = con.createStatement();
//SQL 语句正确
String sql = "insert into tb_user(username,password) value('Rose','123')";
st.executeUpdate(sql); //正常执行
//SQL 语句错误:id是INT型,并非 VARCHAR
sql = "insert into tb_uservalue('u123','Rose','123')";
st.executeUpdate(sql); //这里会出现异常
//SQL 语句正确
sql = "insert into tb_user(username,password) value('Java','1234')";
st.executeUpdate(sql); //由于上一句出现异常,这一句就不会执行。
con.close();
}
演示没有事务下执行 批量处理
由于是批量处理,java与mysql之间的只有在executeBatch()时才进行通讯,而且把所有sql语句发送过去,当mysql执行出现错误时,会把错误信息封装起来,等这次批量sql语句都执行完毕才把所有语句的执行结果反馈给java。
@Test
public void noTransaction2() throws Exception {
Connection con = ConnUtil.getConnection();
Statement st = con.createStatement();
//SQL 语句正确
String sql = "insert into tb_user(username,password) value('Rose','123')";
st.addBatch(sql);
//SQL 语句错误:id是INT型,并非 VARCHAR
sql = "insert into tb_uservalue('u123','Rose','123')";
st.addBatch(sql);
//SQL 语句正确
sql = "insert into tb_user(username,password) value('Java','1234')";
st.addBatch(sql);
/* 运行时下面这一句会出现异常,因为批量处理是只有有一条语句出现问题,就会抛出异常
* 但是语法正确的sql语句会照样执行,不同于上面演示!!!
* 因为上面演示是 每条sql语句都是要进行一次通讯,在出现异常后,与mysql的连接就断开了,
* 而 批量处理 则是一次性把所有sql语句发送过去,即使异常也只是影响下一次的通讯。
*/
st.executeBatch();
con.close();
}
有事务的情况下执行 sql 语句
演示有事务下执行 多条SQL语句
因为开启了事务,所以当第二条sql语句执行时,mysql反馈过来执行错误的信息,excute()方法就抛出一个异常,这时该异常被捕捉到,进入了catch块中,通过 con.rollback()方法进行事务回滚。
@Test
public void transaction(){
Connection con = ConnUtil.getConnection();
try {
con.setAutoCommit( false ); //开启事务
Statement st = con.createStatement();
//SQL 语句正确
String sql = "insert into tb_user(username,password) value('Rose','123')";
st.executeUpdate(sql); //正常执行
//SQL 语句错误:id是INT型,并非 VARCHAR
// sql = "insert into tb_uservalue('u123','Rose','123')";
// st.executeUpdate(sql); //这里会出现异常
//SQL 语句正确
sql = "insert into tb_user(username,password) value('Java','1234')";
st.executeUpdate(sql);
con.commit(); //提交事务
System.out.println("事务完成...");
} catch (SQLException e) {
e.printStackTrace();
try {
con.rollback();
System.out.println("事务回滚...");
} catch (SQLException e1) {
throw new RuntimeException( e1.getMessage(), e1);
}
} finally {
if( con != null ) {
try {
//如果后序还有其他业务需要访问数据库的话,应该还原成事务自动提交
//con.setAutoCommit( true );
//如果没有后序操作应该关闭连接
con.close();
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
}
}
演示有事务多线程下执行多条sql语句
由于获取connection的工具是做成单例的形式,所以多线程下不同线程共享同一个connection连接,当一个线程的事务执行完毕后如果出现问题应该就回滚当前事务,而不影响其他线程,但是因为单例就会影响到其他线程的事务。解决方案:见连接池篇
@Test
public void demo1(){
Connection con = ConnUtil.getConnection();
try {
con.setAutoCommit( false ); //开启事务
Statement st = con.createStatement();
String sql = "insert into tb_user(username,password) value('Rose','123')";
st.executeUpdate(sql);
//处理其他业务
new MyThread(1).start();
new MyThread(2).start();
con.commit(); //提交事务
System.out.println("主线程:事务完成...");
} catch (SQLException e) {
//e.printStackTrace();
try {
con.rollback(); //事务回滚
System.out.println("主线程:事务回滚...");
} catch (SQLException e1) {
throw new RuntimeException( e1.getMessage(), e1);
}
} finally {
if( con != null ) {
try {
//con.setAutoCommit( true ); //还原设置
con.close();
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
}
}
class MyThread extends Thread{
int num ;
public MyThread(int num) {
this.num = num;
}
@Override
public void run() {
Connection con = ConnUtil.getConnection();
try {
con.setAutoCommit( false ); //开启事务
Statement st = con.createStatement();
String sql = "insert into tb_user(username,password) value('Rose"+num+"','123')";
st.executeUpdate(sql);
con.commit(); //提交事务
System.out.println( "子线程"+num+":事务完成...");
} catch (SQLException e) {
//e.printStackTrace();
try {
con.rollback(); //事务回滚
System.out.println("子线程"+num+":事务回滚...");
} catch (SQLException e1) {
throw new RuntimeException( e1.getMessage(), e1);
}
} finally {
if( con != null ) {
try {
//con.setAutoCommit( true ); //还原设置
con.close();
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
}
}
}
}