事务处理

事务:就是保证操作的一致性,所有的操作要么全部成功,要么全部失败。

事务处理

事务处理在数据库开发中有着非常重要的作用,所谓的事务就是所有的操作要么一起成功,要么一起失败,事务本身具有:原子性(Atomicity)、一致性(Consistency)、隔离性或独立性(Isolation)、持久性(Durability)四个特征,以上的四个特征,也被称为ACID特征。


原子性

原子性是事务最小的单元,是不可再分割的单元,相当于一个个小的数据库操作,这些操作必须同时完成,如果有一个失败了,则一切的操作将全部失败。例如:A转账和B转账分别是两个不可再分的操作,但是如果A的转账失败,则B的操作也肯定无法成功。


一致性

指的是在数据库操作的前后是完全一致的,保证数据的有效性,如果事务正常操作则系统会维持有效性,如果事务出现了错误,则回到最原始状态,也要维持其有效性,这样保证事务开始时和结束时系统处于一致状态。例如:如果A和B转账成功,则保持其一致性,如果现在A和B的转账失败,则保持操作之前的一致性,即:A的钱不会减少,B的钱不会增加。


隔离性

多个事务可以同时进行且彼此之间无法访问,只有当事务完成最终操作的时候,才可以看见结果。


持久性

当一个系统崩溃时,一个事务依然可以坚持提交,当一个事务完成后,操作的结果保存在磁盘中,永远不会被回滚。例如,所有的资金数都是保存在磁盘中,所以,即使系统发生了错误,用户的资金也不会减少。

Java事务失效的场景 java事务处理_java

如果要想操作事务的话,则必须按照以下的步骤完成:

1、取消掉自动提交:每次执行数据库更新的时候实际上发出SQL命令之后就已经提交上去。

2、开始事务

3、进行一系列的操作

4、如果操作全部合格,则提交事务

5、如果发现有一个地方有问题,则可以进行回滚

6、或者设置一个SAVEPOINT保存事务的提交点。


在正常情况下我们插入一条记录的语句如下:

INSERT INTO user(name,password,age,sex,birthday) VALUES(‘月亮’,'northwind',23,'男',’1990-10-18’);

执行完毕后,结果会立即在数据库中显示:

Java事务失效的场景 java事务处理_数据库_02

如果提交之后发现数据是错误的,则此时就没法操作了。因为它是自动提交上去的。


1.现在我们在命令行中输入:

Java事务失效的场景 java事务处理_Java事务失效的场景_03

表示关闭自动提交,准备开启事务。

Java事务失效的场景 java事务处理_sql_04

表示启动事务。

Java事务失效的场景 java事务处理_sql_05

我们再次插入一条语句。

Java事务失效的场景 java事务处理_JDBC事务处理_06

这个时候,我们发现数据已经有了。但是,如果我们现在不需要了,我们可以通过rollback命令进行回退。

Java事务失效的场景 java事务处理_JDBC事务处理_07


再次查询数据库,我们发现,刚刚插入的数据已经不存在了。

Java事务失效的场景 java事务处理_数据库_08


但是,如果我们再次插入此条数据,并执行commit命令,那么数据将会被永久保存。Rollback命令已经不产生任何作用了。

Java事务失效的场景 java事务处理_数据库_09

查询数据库:

Java事务失效的场景 java事务处理_JDBC事务处理_10

我们发现,已经不能再回滚回来了。


在JDBC中同样支持事务的处理操作。

1.不使用事务处理的情况。

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
public class TranDemo01{
    //定义MySQL的数据库驱动程序
    public static final String DBDRIVER = "org.gjt.mm.mysql.Driver";
    //定义MySQL数据库的连接地址
    public static final String DBURL = "jdbc:mysql://localhost:3306/skewrain";
    //MySQL数据库的连接用户名
    public static final String DBUSER = "root";
    //MySQL数据库的连接密码
    public static final String DBPASS = "mysqladmin";
    public static void main(String args[]) throws Exception{
    Connection conn = null;    //数据库连接
    Statement stmt = null;     //定义数据库操作
    Class.forName(DBDRIVER);  //加载驱动程序
    conn = DriverManager.getConnection(DBURL,DBUSER,DBPASS);
    stmt = conn.createStatement();
    stmt.addBatch("INSERT INTO user(name,password,age,sex,birthday)" +
    " VALUES ('skewrain-1','super-1',18,'男','1990-10-18')");
    stmt.addBatch("INSERT INTO user(name,password,age,sex,birthday)" +
    " VALUES ('skewrain-2','super-2',19,'女','1990-10-19')");
    //加入“’”之后,此SQL语句语法就出现了错误,所以,肯定执行到此语句的时候会出现代码错误。
    stmt.addBatch("INSERT INTO user(name,password,age,sex,birthday)" +
    " VALUES ('skewrain-'3','super-3',20,'男','1990-10-20')");
    stmt.addBatch("INSERT INTO user(name,password,age,sex,birthday)" +
    " VALUES ('skewrain-4','super-4',21,'女','1990-10-21')");
    stmt.addBatch("INSERT INTO user(name,password,age,sex,birthday)" +
    " VALUES ('skewrain-5','super-5',22,'男','1990-10-22')");
    int temp[] = stmt.executeBatch();
    System.out.println("更新了:" + temp.length + "条数据。");
    stmt.close();
    conn.close();   
}
};

假如要求插入的五条语句有关联,要么同时成功,要么同时失败。

我们现在编译运行的结果如下:

Java事务失效的场景 java事务处理_Java事务失效的场景_11

查询数据库中的结果如下:

Java事务失效的场景 java事务处理_数据库_12

我们发现,除了出错的数据没有插入进去,其他的数据插入正常。那么,如果现在这5条记录有相互的关系,则此时这样的实现,是不符合要求的。


JDBC事务操作步骤

在JDBC中,如果要想进行事务处理,也需要按照指定的步骤完成:

1.取消掉Connection中设置的自动提交方式;【conn.setAutoCommit(false);】

2.如果批处理操作成功,则执行提交事务:conn.commit();

3.如果操作失败,则肯定会引发异常,在异常处理中让事务回滚:conn.rollback();

4.如果需要,可以设置Savepoint:Savepoint sp = conn.setSavepoint();。

 

JDBC事务处理的代码如下所示:【TranDemo02.java】

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
public class TranDemo02{
    //定义MySQL的数据库驱动程序
    public static final String DBDRIVER = "org.gjt.mm.mysql.Driver";
    //定义MySQL数据库的连接地址
    public static final String DBURL = "jdbc:mysql://localhost:3306/skewrain";
    //MySQL数据库的连接用户名
    public static final String DBUSER = "root";
    //MySQL数据库的连接密码
    public static final String DBPASS = "mysqladmin";
    public static void main(String args[]) throws Exception{
    Connection conn = null;    //数据库连接
    Statement stmt = null;     //定义数据库操作
    Class.forName(DBDRIVER);  //加载驱动程序
    conn = DriverManager.getConnection(DBURL,DBUSER,DBPASS);
conn.setAutoCommit(false); //取消掉自动提交
    stmt = conn.createStatement();
    stmt.addBatch("INSERT INTO user(name,password,age,sex,birthday)" +
        " VALUES ('skewrain-1','super-1',18,'男','1990-10-18')");
    stmt.addBatch("INSERT INTO user(name,password,age,sex,birthday)" +
        " VALUES ('skewrain-2','super-2',19,'女','1990-10-19')");
    //加入“’”之后,此SQL语句语法就出现了错误,所以,肯定执行到此语句的时候会出现代码错误。
    stmt.addBatch("INSERT INTO user(name,password,age,sex,birthday)" +
        " VALUES ('skewrain-'3','super-3',20,'男','1990-10-20')");
    stmt.addBatch("INSERT INTO user(name,password,age,sex,birthday)" +
        " VALUES ('skewrain-4','super-4',21,'女','1990-10-21')");
    stmt.addBatch("INSERT INTO user(name,password,age,sex,birthday)" +
        " VALUES ('skewrain-5','super-5',22,'男','1990-10-22')");
    try{
        int temp[] = stmt.executeBatch();
        System.out.println("更新了:" + temp.length + "条数据。");
        conn.commit();  //所有的操作成功了。
    }catch(Exception e){
        try{
             conn.rollback();
        }catch(Exception e1){}
    }
    stmt.close();
    conn.close();   
}
};

我们先将原有的user表删除,再创建一个新的user表。

Java事务失效的场景 java事务处理_java_13

编译运行程序:

Java事务失效的场景 java事务处理_JDBC事务处理_14

我们查询数据库的内容显示的结果如下:

Java事务失效的场景 java事务处理_Java事务失效的场景_15

可见五条插入语句,一条都没有执行。


在开发中,以上的操作过程是标准的事务处理。


正常情况下,可以通过SAVEPOINT保存事务的操作点,因为默认情况下所有的回滚,就是将全部的操作取消掉,而通过Savepoint可以设置回滚的位置。

一个session的操作(每一个连接到数据库上的用户都称为一个session)

操作1

操作2

SAVEPOINT 记录点1

操作3

操作4

Rollback 记录点1

如果回滚的话,回滚到记录点1,操作1和操作2仍保留。


设置Savepoint的类:setSavepoint();


使用SAVEPOINT进行回滚的操作的代码如下:【TranDemo03.java】

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Savepoint;
public class TranDemo03{
    //定义MySQL的数据库驱动程序
    public static final String DBDRIVER = "org.gjt.mm.mysql.Driver";
    //定义MySQL数据库的连接地址
    public static final String DBURL = "jdbc:mysql://localhost:3306/skewrain";
    //MySQL数据库的连接用户名
    public static final String DBUSER = "root";
    //MySQL数据库的连接密码
    public static final String DBPASS = "mysqladmin";
    public static void main(String args[]) throws Exception{
    Connection conn = null;    //数据库连接
    Statement stmt = null;     //定义数据库操作
    Class.forName(DBDRIVER);  //加载驱动程序
    conn = DriverManager.getConnection(DBURL,DBUSER,DBPASS);
conn.setAutoCommit(false); //取消掉自动提交
    stmt = conn.createStatement();
    stmt.executeUpdate("INSERT INTO user(name,password,age,sex,birthday)" +
        " VALUES ('skewrain-1','super-1',18,'男','1990-10-18')");
    stmt.executeUpdate("INSERT INTO user(name,password,age,sex,birthday)" +
        " VALUES ('skewrain-2','super-2',19,'女','1990-10-19')");
    Savepoint sp = conn.setSavepoint(); //设置保存点
    stmt.executeUpdate("INSERT INTO user(name,password,age,sex,birthday)" +
        " VALUES ('skewrain-4','super-4',21,'女','1990-10-21')");
    stmt.executeUpdate("INSERT INTO user(name,password,age,sex,birthday)" +
        " VALUES ('skewrain-5','super-5',22,'男','1990-10-22')");
    try{
        conn.rollback(sp); //回滚到保存点
        conn.commit();  //所有的操作成功了。
    }catch(Exception e){
    }
    stmt.close();
    conn.close();   
}
};

由于事务处理的隔离性,我们需要删除user表重新建立。

编译运行的结果如下所示:

Java事务失效的场景 java事务处理_sql_16

查看数据库的内容如下所示:

Java事务失效的场景 java事务处理_JDBC事务处理_17

我们发现只有回滚点前的插入语句有效。


小结:

JDBC操作事务的步骤:

1.取消掉自动提交;

2.执行多条SQL语句;

3.如果没有异常,则提交事务;

4.否则则进行事务的回滚操作。