事务(操作数据库,判断SQL语句是提交还是回滚)


事务

要么都成功,要么都失败。

在一个事件中的事:从通知开启事务,到提交事务之间,任何一句语句有错误,则哪一句语句都不应该提交。

1、事务的几个状态

1、开启事务

2、事务提交 commit()。在做事务提交和回滚前,关键的头尾是:(1)通知开启事务,false是开启(connection.setAutoCommit(false);) (2)提交或回滚(提交写在try部分,回滚写在catch中)

3、事务回滚 rollback()。事务一旦提交(commit)了,就无法回滚(rollback)了。所以,应该在提交之前判断,如果崩了就不要提交,直接回滚。

4、关闭事务

转账:

A:1千块

B:1千块

A --> B: A(900)、B(1100)

不能出现A转给了B100块钱,A成为900,但是系统崩溃了,B还没收到100块钱,出现了A900块钱,B1000块钱的情况。

1、搭建环境,进行测试

1 Create TABLE account(
2     id INT PRIMARY KEY auto_increment,
3     `name` VARCHAR(40),
4     money FLOAT
5 );
6 INSERT INTO account(`name`,money) VALUES ('A',1000);
7 INSERT INTO account(`name`,money) VALUES ('B',1000);
8 INSERT INTO account(`name`,money) VALUES ('C',1000);
View Code

2、项目构成

1、建的普通Maven项目(不用模板)

2、在java下建con.wang.utils文件夹,并在该文件夹下建两个class文件。

3、两个class文件中,一个是JDBC的工具类(JdbcUtils),公共的资源配置放在这;另一个是具体的操作数据库的类(Test_jdbc)

4、连接数据库的用户名,密码,数据库驱动和url都放在resources下的db.properties文件夹中。

 

driver = com.mysql.jdbc.Driver
url = jdbc:mysql://localhost:3306/jdbc_example?useUnicode=true&characterEncoding=utf8&useSSL=true
username = root
password = 123456
View Code

5、导入mysql依赖。此外,为实现上一节提到的junit单元测试,还要导入junit依赖

    <dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>
View Code

3、写JDBC的工具类(JdbcUtils)

package com.wang.utils;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
public class JdbcUtils {
    private static String driver = null;
    private static String url = null;
    private static String username = null;
    private static String password = null;
    static {
        try {
            InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
            Properties properties = new Properties();
            properties.load(in);

            driver = properties.getProperty("driver");
            url = properties.getProperty("url");
            username = properties.getProperty("username");
            password = properties.getProperty("password");

            //驱动只用加载一次
            Class.forName(driver);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    //获取连接
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(url, username, password);
    }
    //释放连接资源
    public static void release(Connection conn, Statement st, ResultSet rs){
        if(rs!=null){
            try {
                rs.close();
            }catch (SQLException e){
                e.printStackTrace();
            }
        }
        if(st!=null){
            try {
                st.close();
            }catch (SQLException e){
                e.printStackTrace();
            }
        }
        if(conn!=null){
            try {
                conn.close();
            }catch (SQLException e){
                e.printStackTrace();
            }
        }

    }

}
View Code

4、写具体的操作数据库的类(Test_jdbc)

      —— 模拟转账,并测试失败情况下的回滚和成功情况下的事务提交

情况1:当写第4步,故意制造错误的时候,看看第3步的SQL语句,提交还是回滚

 1 package com.wang.utils;
 2 import org.junit.Test;
 3 
 4 import java.sql.Connection;
 5 import java.sql.DriverManager;
 6 import java.sql.SQLException;
 7 public class Test_jdbc {
 8     @Test
 9     public void test() throws Exception {
10         //配置信息
11         //String driver = "com.mysql.jdbc.Driver";
12         String url = "jdbc:mysql://localhost:3306/jdbc_example?useUnicode=true&characterEncoding=utf8&useSSL=true";
13         String username = "root";
14         String password = "123456";
15         Connection connection = null;
16         try {
17             //第一步,加载驱动
18             Class.forName("com.mysql.jdbc.Driver");
19             //第二步,连接数据库
20             connection = DriverManager.getConnection(url, username, password);
21             //第三步,通知开启事务。false是开启
22             connection.setAutoCommit(false);
23             //第四步,执行SQL语句
24             String sql = "update account set money = money - 100 where name = 'A'";
25             connection.prepareStatement(sql).executeUpdate();
26             //第五步,制造错误
27             int i = 1/0;
28             //第六步,写第二句SQL语句。理论上,第五步出现错误,所以这一句应该也不执行,不应该提交,应该回滚
29             String sql2 = "update account set money = money + 100 where name = 'B'";
30             connection.prepareStatement(sql2).executeUpdate();
31             //第七步,提交
32             connection.commit();
33             //第八步,若上面两天SQl都提交成功,则输出“提交成功”
34             System.out.println("提交成功!");
35         } catch (Exception e) {
36             try {
37                 //如果try中的方法有错误,就走catch,通知数据库执行回滚事务
38                 connection.rollback();
39             } catch (SQLException e1) {
40                 e1.printStackTrace();
41             }
42             e.printStackTrace();
43         } finally {
44             //释放资源
45             connection.close();
46         }
47     }
48 }
View Code

2、当注释掉第五步,代码没有错误,则提交事务

package com.wang.utils;
import org.junit.Test;
import java.sql.*;
public class Test_jdbc {
    @Test
    public void test() throws Exception {
        Connection connection = null;
        Statement st = null;
        ResultSet rs = null;
        try {
            //第一步,连接数据库
            connection = JdbcUtils.getConnection();
            //第二步,通知开启事务。false是开启
            connection.setAutoCommit(false);
            //第三步,执行SQL语句
            String sql = "update account set money = money - 100 where name = 'A'";
            connection.prepareStatement(sql).executeUpdate();
            //第四步,故意制造错误
            //int i = 1/0;
            //第五步,写第二句SQL语句。
            //注释掉第四步错误,第三步和第五步的sql和sql2语句都提交
            String sql2 = "update account set money = money + 100 where name = 'B'";
            connection.prepareStatement(sql2).executeUpdate();
            //第六步,提交
            connection.commit();
            //第七步,若上面两条SQl都提交成功,则输出“提交成功”
            System.out.println("提交成功!");
        } catch (Exception e) {
            try {
                //如果try中的方法有错误,就走catch,通知数据库执行回滚事务
                connection.rollback();
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
            e.printStackTrace();
        } finally {
            //释放资源
            JdbcUtils.release(connection,st,rs);
        }
    }
}
View Code

5、上述的关键在第3步,通知开启事务:connection.setAutoCommit(false);    false是开启

若不通知开启事务,即使有错误,也不会回滚,直接就提交了。

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM