JDBC03 利用JDBC实现事务提交与回滚【调用Connection中的方法实现事务管理】

 目录

  1 Connection中的重用方法

  2 JDBC事务管理经典案例

1 Connection类中常用的方法回顾

  1.1 Statement createStatement() throws SQLException;

    创建一个Statement实例(即:创建一个SQL执行对象)

  1.2 PreparedStatement prepareStatement(String sql) throws SQLException;

    创建一个PreparedStatement对象(即:创建一个预编译SQL执行对象)

  1.3 void setAutoCommit(boolean autoCommit) throws SQLException;

    设置事务的自动提交(false为关闭自动提交,true为启动自动提交)

  1.4 void commit() throws SQLException;

    手动提交事务

  1.5 void rollback() throws SQLException;

    手动回滚事务

 

2 需要用到事务回滚的经典案例:银行转账案例

  转出和转入是一个事务,如果转出成功但是转入失败的会就需要进行事务回滚,否则就出出现转出者余额减少但是转入者余额没有增加

  注意:事务的提交与回滚是通过Connection提供的方法来调用的;本质上事务还是依赖数据库的实现;Connection的方法实质上也是调用了数据库事务机制.

  2.1 不使用事务控制的转账业务

    缺点:如果转入成功,但是转入失败的话,会造成转出者余额减少,但是转入者余额不变

    项目结构图

      

 1 package cn.xiangxu.entity;
 2 
 3 import java.sql.Connection;
 4 import java.sql.PreparedStatement;
 5 import java.util.Scanner;
 6 
 7 import cn.xiangxu.tools.DBUtil;
 8 
 9 public class Test {
10     public static void main(String[] args) {
11         Scanner scanner = new Scanner(System.in);
12         System.out.println("请输入转出用户名:");
13         String outName = scanner.nextLine();
14         System.out.println("请输入需要转出的资金额度:");
15         Double money = Double.parseDouble(scanner.nextLine());
16         System.out.println("请输入转入用户名:");
17         String inName = scanner.nextLine();
18         System.out.println("转出账户为:" + outName + "转出金额为:" + money + "转入账户为:" + inName);
19         
20         
21         Connection conn = null;
22         try {
23             conn = DBUtil.getConnection(); // 实例化连接对象
24             
25 //            conn.setAutoCommit(false); // 关闭自动提交事务功能
26             
27             String sql = "UPDATE client "
28                     + "SET account = account - ? " 
29                     + "WHERE name = ? ";
30             PreparedStatement ps = conn.prepareStatement(sql);
31             ps.setDouble(1, money);
32             ps.setString(2, outName);
33             Integer rs = ps.executeUpdate();
34             if(rs > 0) {
35                 System.out.println("转出成功");
36             } else {
37                 System.out.println("转出失败");
38                 return; // 转出失败跳出函数,不再执行下面的语句;但是finally中的语句还是会执行的,因为就算天塌下来finally中的语句都会执行
39             }
40             
41             System.out.println("======分割线=======");
42             
43             String sql_in = "UPDATE client "
44                     + "SET account = account + ? " 
45                     + "WHERE name = ? ";
46             PreparedStatement ps_in = conn.prepareStatement(sql_in);
47             ps_in.setDouble(1, money);
48             ps_in.setString(2, inName);
49             Integer judge_in = ps_in.executeUpdate();
50             if(judge_in > 0) {
51                 System.out.println("转入成功");
52 //                conn.commit(); // 转出、转入都成功就提交事务
53             } else {
54                 System.out.println("转入失败");
55 //                conn.rollback(); // 转出成功、转入失败就回滚事务
56             }
57             
58 //            conn.setAutoCommit(true); // 打开自动提交事务
59             
60         } catch (Exception e) {
61             // TODO Auto-generated catch block
62             e.printStackTrace();
63         } finally {
64             System.out.println("我是finally中的语句哟");
65             try {
66                 DBUtil.closeConnection();
67             } catch (Exception e) {
68                 // TODO Auto-generated catch block
69                 e.printStackTrace();
70             }
71         }
72     }
73 }
转账业务java源代码
1 CREATE TABLE client  (
2     id INT (10)  PRIMARY KEY,
3     name VARCHAR (10),
4     pwd VARCHAR (10),
5     account INT (20)
6 );
SQL语句
 1 package cn.xiangxu.tools;
 2 
 3 import java.io.IOException;
 4 import java.io.InputStream;
 5 import java.sql.Connection;
 6 import java.sql.SQLException;
 7 import java.util.Properties;
 8 
 9 import org.apache.commons.dbcp.BasicDataSource;
10 
11 public class DBUtil {
12     /*
13      * ThreadLocal用于线程跨方法共享数据使用
14      * ThreadLocal内部有一个Map,  key为需要共享数据的线程本身,value就是其需要共享的数据
15      */
16     private static ThreadLocal<Connection> tl; // 声明一个类似于仓库的东西
17     private static BasicDataSource dataSource; // 声明一个数据库连接池对象
18     
19     // 静态代码块,在类加载的时候执行,而且只执行一次
20     static {
21         tl = new ThreadLocal<Connection>(); // 实例化仓库对象
22         dataSource = new BasicDataSource(); // 实例数据库连接池对象
23 
24         Properties prop = new Properties(); // 创建一个Properties对象用(该对象可以用来加载配置文件中的属性列表)
25         InputStream is = DBUtil.class.getClassLoader().getResourceAsStream("config/mysql.properties"); // 读取配置文件信息
26         try {
27             prop.load(is); // 加载配置文件中的属性列表
28             
29             String driverClassName = prop.getProperty("driverClassName"); // 获取属性信息
30             String url = prop.getProperty("url");
31             String username = prop.getProperty("username");
32             String password = prop.getProperty("password");
33             Integer maxActive = Integer.parseInt(prop.getProperty("maxActive"));
34             Integer maxWait = Integer.parseInt(prop.getProperty("maxWait"));
35             
36             dataSource.setDriverClassName(driverClassName); // 初始化数据库连接池(即:配置数据库连接池的先关参数)
37             dataSource.setUrl(url);
38             dataSource.setUsername(username);
39             dataSource.setPassword(password);
40             dataSource.setMaxActive(maxActive);
41             dataSource.setMaxWait(maxWait);
42             
43             is.close(); // 关闭输入流,释放资源
44         } catch (IOException e) {
45             // TODO Auto-generated catch block
46             e.printStackTrace();
47         } 
48         
49     }
50     
51     /**
52      * 创建连接对象(注意:静态方法可以直接通过类名来调用)
53      * @return 连接对象
54      * @throws Exception
55      */
56     public static Connection getConnection() throws Exception { 
57         try {
58             Connection conn = dataSource.getConnection(); // 创建连接对象(利用数据库连接池进行创建)
59             tl.set(conn); // 将连接对象放到仓库中
60             return conn; 
61         } catch (Exception e) {
62             // TODO Auto-generated catch block
63             e.printStackTrace();
64             throw e;
65         }
66     }
67     
68     /**
69      * 关闭连接对象(注意:静态方法可以通过类名直接调用)
70      * @throws Exception
71      */
72     public static void closeConnection() throws Exception {
73         Connection conn = tl.get(); // 从仓库中取出连接对象
74         tl.remove(); // 清空仓库
75         if(conn != null) { // 判断连接对象是否释放资源
76             try {
77                 conn.close();
78             } catch (Exception e) {
79                 // TODO Auto-generated catch block
80                 e.printStackTrace();
81                 throw e;
82             }
83         }
84     }
85 
86 }
数据库连接池的java源代码
1 # zhe shi zhu shi , yi ban bu yong zhong wen 
2 # deng hao liang bian mei you kong ge, mo wei mei you fen hao
3 # hou mian bu neng you kong ge
4 driverClassName=com.mysql.jdbc.Driver
5 url=jdbc:mysql://localhost:3306/test
6 username=root
7 password=182838
8 maxActive=100
9 maxWait=3000
数据库信息文件
 1 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 2   <modelVersion>4.0.0</modelVersion>
 3   <groupId>cn.xiangxu</groupId>
 4   <artifactId>testJDBC</artifactId>
 5   <version>0.0.1-SNAPSHOT</version>
 6   <dependencies>
 7       <dependency>
 8           <groupId>mysql</groupId>
 9           <artifactId>mysql-connector-java</artifactId>
10           <version>5.1.37</version>
11       </dependency>
12       <dependency>
13           <groupId>junit</groupId>
14           <artifactId>junit</artifactId>
15           <version>4.12</version>
16       </dependency>
17       <dependency>
18           <groupId>commons-dbcp</groupId>
19           <artifactId>commons-dbcp</artifactId>
20           <version>1.4</version>
21       </dependency>
22   </dependencies>
23 </project>
maven依赖文件

  2.2 利用事务控制的转账业务

 1 package cn.xiangxu.entity;
 2 
 3 import java.sql.Connection;
 4 import java.sql.PreparedStatement;
 5 import java.sql.SQLException;
 6 import java.util.Scanner;
 7 
 8 import cn.xiangxu.tools.DBUtil;
 9 
10 public class Test {
11     public static void main(String[] args) {
12         Scanner scanner = new Scanner(System.in);
13         System.out.println("请输入转出用户名:");
14         String outName = scanner.nextLine();
15         System.out.println("请输入需要转出的资金额度:");
16         Double money = Double.parseDouble(scanner.nextLine());
17         System.out.println("请输入转入用户名:");
18         String inName = scanner.nextLine();
19         System.out.println("转出账户为:" + outName + "转出金额为:" + money + "转入账户为:" + inName);
20         
21         
22         Connection conn = null;
23         try {
24             conn = DBUtil.getConnection(); // 实例化连接对象
25             
26             conn.setAutoCommit(false); // 关闭自动提交事务功能
27             
28             String sql = "UPDATE client "
29                     + "SET account = account - ? " 
30                     + "WHERE name = ? ";
31             PreparedStatement ps = conn.prepareStatement(sql);
32             ps.setDouble(1, money);
33             ps.setString(2, outName);
34             Integer rs = ps.executeUpdate();
35             if(rs > 0) {
36                 System.out.println("转出成功");
37             } else {
38                 System.out.println("转出失败");
39                 return; // 转出失败跳出函数,不再执行下面的语句;但是finally中的语句还是会执行的,因为就算天塌下来finally中的语句都会执行
40             }
41             
42             System.out.println("======分割线=======");
43             
44             String sql_in = "UPDATE client "
45                     + "SET account = account + ? " 
46                     + "WHERE name = ? ";
47             PreparedStatement ps_in = conn.prepareStatement(sql_in);
48             ps_in.setDouble(1, money);
49             ps_in.setString(2, inName);
50             Integer judge_in = ps_in.executeUpdate();
51             if(judge_in > 0) {
52                 System.out.println("转入成功");
53                 conn.commit(); // 转出、转入都成功就提交事务
54             } else {
55                 System.out.println("转入失败");
56                 conn.rollback(); // 转出成功、转入失败就回滚事务
57             }
58             
59             conn.setAutoCommit(true); // 打开自动提交事务
60             
61         } catch (Exception e) {
62             // TODO Auto-generated catch block
63             try {
64                 conn.rollback(); // 捕获到异常后也需要进行事务回滚
65             } catch (SQLException e1) {
66                 // TODO Auto-generated catch block
67                 e1.printStackTrace();
68             } 
69             e.printStackTrace();
70         } finally {
71             System.out.println("我是finally中的语句哟");
72             try {
73                 DBUtil.closeConnection();
74             } catch (Exception e) {
75                 // TODO Auto-generated catch block
76                 e.printStackTrace();
77             }
78         }
79     }
80 }
转账业务的java源代码

  2.3 将关闭自动提交功能、手动提交功能、手动回滚功能封装到一个类中

  1 package cn.xiangxu.tools;
  2 
  3 import java.io.IOException;
  4 import java.io.InputStream;
  5 import java.sql.Connection;
  6 import java.sql.SQLException;
  7 import java.util.Properties;
  8 
  9 import org.apache.commons.dbcp.BasicDataSource;
 10 
 11 public class DBUtil {
 12     /*
 13      * ThreadLocal用于线程跨方法共享数据使用
 14      * ThreadLocal内部有一个Map,  key为需要共享数据的线程本身,value就是其需要共享的数据
 15      */
 16     private static ThreadLocal<Connection> tl; // 声明一个类似于仓库的东西
 17     private static BasicDataSource dataSource; // 声明一个数据库连接池对象
 18     
 19     // 静态代码块,在类加载的时候执行,而且只执行一次
 20     static {
 21         tl = new ThreadLocal<Connection>(); // 实例化仓库对象
 22         dataSource = new BasicDataSource(); // 实例数据库连接池对象
 23 
 24         Properties prop = new Properties(); // 创建一个Properties对象用(该对象可以用来加载配置文件中的属性列表)
 25         InputStream is = DBUtil.class.getClassLoader().getResourceAsStream("config/mysql.properties"); // 读取配置文件信息
 26         try {
 27             prop.load(is); // 加载配置文件中的属性列表
 28             
 29             String driverClassName = prop.getProperty("driverClassName"); // 获取属性信息
 30             String url = prop.getProperty("url");
 31             String username = prop.getProperty("username");
 32             String password = prop.getProperty("password");
 33             Integer maxActive = Integer.parseInt(prop.getProperty("maxActive"));
 34             Integer maxWait = Integer.parseInt(prop.getProperty("maxWait"));
 35             
 36             dataSource.setDriverClassName(driverClassName); // 初始化数据库连接池(即:配置数据库连接池的先关参数)
 37             dataSource.setUrl(url);
 38             dataSource.setUsername(username);
 39             dataSource.setPassword(password);
 40             dataSource.setMaxActive(maxActive);
 41             dataSource.setMaxWait(maxWait);
 42             
 43             is.close(); // 关闭输入流,释放资源
 44         } catch (IOException e) {
 45             // TODO Auto-generated catch block
 46             e.printStackTrace();
 47         } 
 48         
 49     }
 50     
 51     /**
 52      * 创建连接对象(注意:静态方法可以直接通过类名来调用)
 53      * @return 连接对象
 54      * @throws Exception
 55      */
 56     public static Connection getConnection() throws Exception { 
 57         try {
 58             Connection conn = dataSource.getConnection(); // 创建连接对象(利用数据库连接池进行创建)
 59             tl.set(conn); // 将连接对象放到仓库中
 60             return conn; 
 61         } catch (Exception e) {
 62             // TODO Auto-generated catch block
 63             e.printStackTrace();
 64             throw e;
 65         }
 66     }
 67     
 68     /**
 69      * 关闭连接对象(注意:静态方法可以通过类名直接调用)
 70      * @throws Exception
 71      */
 72     public static void closeConnection() throws Exception {
 73         Connection conn = tl.get(); // 从仓库中取出连接对象
 74         tl.remove(); // 清空仓库
 75         if(conn != null) { // 判断连接对象是否释放资源
 76             try {
 77                 conn.close();
 78             } catch (Exception e) {
 79                 // TODO Auto-generated catch block
 80                 e.printStackTrace();
 81                 throw e;
 82             }
 83         }
 84     }
 85     
 86     /**
 87      * 在执行SQL语句前关闭JDBC的自动提交事务功能
 88      * @throws SQLException
 89      */
 90     public static void tansBegin() throws SQLException {
 91         try {
 92             tl.get().setAutoCommit(false); // 从仓库中获取连接对象并调用setAutoCommit来关闭自动提交事务功能
 93         } catch(SQLException e) {
 94             e.printStackTrace();
 95             throw e;
 96         }
 97     }
 98     
 99     /**
100      * 手动回滚功能
101      * @throws SQLException
102      */
103     public static void transBack() throws SQLException {
104         tl.get().rollback(); // 从仓库中获取连接对象并调用rollback来实现事务回滚操作
105         tl.get().setAutoCommit(true); // 回滚启动事务自动提交功能
106     }
107     
108     /**
109      * 手动提交功能
110      * @throws SQLException
111      */
112     public static void transCommit() throws SQLException {
113         tl.get().commit(); // 从仓库中获取连接对象并调用commit来实现事务提交操作
114         tl.get().setAutoCommit(true); // 提交后启动事务自动提交功能
115     }
116 
117 }
DBUtil
 1 package cn.xiangxu.entity;
 2 
 3 import java.sql.Connection;
 4 import java.sql.PreparedStatement;
 5 import java.sql.SQLException;
 6 import java.util.Scanner;
 7 
 8 import cn.xiangxu.tools.DBUtil;
 9 
10 public class Test {
11     public static void main(String[] args) {
12         Scanner scanner = new Scanner(System.in);
13         System.out.println("请输入转出用户名:");
14         String outName = scanner.nextLine();
15         System.out.println("请输入需要转出的资金额度:");
16         Double money = Double.parseDouble(scanner.nextLine());
17         System.out.println("请输入转入用户名:");
18         String inName = scanner.nextLine();
19         System.out.println("转出账户为:" + outName + "转出金额为:" + money + "转入账户为:" + inName);
20         
21         
22         Connection conn = null;
23         try {
24             conn = DBUtil.getConnection(); // 实例化连接对象
25             
26             DBUtil.tansBegin(); // 关闭自动提交事务功能
27             
28             String sql = "UPDATE client "
29                     + "SET account = account - ? " 
30                     + "WHERE name = ? ";
31             PreparedStatement ps = conn.prepareStatement(sql);
32             ps.setDouble(1, money);
33             ps.setString(2, outName);
34             Integer rs = ps.executeUpdate();
35             if(rs > 0) {
36                 System.out.println("转出成功");
37             } else {
38                 System.out.println("转出失败");
39                 return; // 转出失败跳出函数,不再执行下面的语句;但是finally中的语句还是会执行的,因为就算天塌下来finally中的语句都会执行
40             }
41             
42             System.out.println("======分割线=======");
43             
44             String sql_in = "UPDATE client "
45                     + "SET account = account + ? " 
46                     + "WHERE name = ? ";
47             PreparedStatement ps_in = conn.prepareStatement(sql_in);
48             ps_in.setDouble(1, money);
49             ps_in.setString(2, inName);
50             Integer judge_in = ps_in.executeUpdate();
51             if(judge_in > 0) {
52                 System.out.println("转入成功");
53                 DBUtil.transCommit(); // 转出、转入都成功就提交事务
54             } else {
55                 System.out.println("转入失败");
56                 DBUtil.transBack(); // 转出成功、转入失败就回滚事务
57             }
58             
59         } catch (Exception e) {
60             // TODO Auto-generated catch block
61             try {
62                 DBUtil.transBack();// 捕获到异常后也需要进行事务回滚
63             } catch (SQLException e1) {
64                 // TODO Auto-generated catch block
65                 e1.printStackTrace();
66             } 
67             e.printStackTrace();
68         } finally {
69             System.out.println("我是finally中的语句哟");
70             try {
71                 DBUtil.closeConnection();
72             } catch (Exception e) {
73                 // TODO Auto-generated catch block
74                 e.printStackTrace();
75             }
76         }
77     }
78 }
转账业务java源代码

 

 

    

 

posted @ 2017-07-07 17:04  寻渝记  阅读(661)  评论(0编辑  收藏  举报