JDBC事务之例子篇

上一篇随笔记了一些有关JDBC事务管理的理论知识。这篇来看例子(主要怕一篇随笔装所有东西太长了然后分开呵呵)

一般讲事务管理的,都是拿转钱来当例子的,嗯没错我们这也是。

这个是数据库中的t_account表,装的就是额~可以理解成一个银行账号,就有id,用户名,还有用户的存款。

然后是一个简单的Java实体类:

package com.java.ws.transactionDemo;

/**
 * 数据库的实体对应类
 * 银行账号类
 * @author 85060
 *
 */
public class Account {
    private int id;
    private String accountName;
    private float accountBalance;
    
    public Account(int id, String accountName, float accountBalance) {
        this.id = id;
        this.accountBalance = accountBalance;
        this.accountName = accountName;
    }
    
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getAccountName() {
        return accountName;
    }
    public void setAccountName(String accountName) {
        this.accountName = accountName;
    }
    public float getAccountBalance() {
        return accountBalance;
    }
    public void setAccountBalance(float accountBalance) {
        this.accountBalance = accountBalance;
    }
    
    
}

DbUtil类:是个封装了获取JDBC驱动还有连接代码的类,就不贴了,主要就是提供Connection还有关闭数据库连接和预处理。

然后来看 这个AccountInOutDao,这里我们不是很规范,逻辑代码也写在了这里:

package com.java.ws.transactionDemo;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import com.java.ws.util.DbUtil;

public class AccountInOutDao {
    
    private static DbUtil dbUtil = new DbUtil();
    
    /* new Account(1, "张三", 500);
     new Account(2, "李四", 1000);*/
    
    
    public static int outMoney(Account user, int amount, Connection con) {
        
        String sql = "UPDATE t_account set accountBalance = accountBalance - ? where id = ?";
        PreparedStatement pstmt = null;
        int result = 0;
        try {
            
            
            
            pstmt = con.prepareStatement(sql);
            pstmt.setInt(1, amount);
            pstmt.setInt(2, user.getId());
            result = pstmt.executeUpdate();//返回执行的条数
            
            
            
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            System.out.println("在借钱出去的数据库操作里面出错");
            e.printStackTrace();
        } finally {
            dbUtil.close(pstmt, con);
        }
        
        return result;
    }
    
    
    public static int inMoney(Account user, int amount, Connection con) {
        
        String sql = "UPDATE t_account set accountBalance = accountBalance + ? where id = ?";
        PreparedStatement pstmt = null;
        int result = 0;
        try {
            
            
            
            pstmt = con.prepareStatement(sql);
            pstmt.setInt(1, amount);
            pstmt.setInt(2, user.getId());
            result = pstmt.executeUpdate();//返回执行的条数
            
            
            
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            System.out.println("在借钱出去的数据库操作里面出错");
            e.printStackTrace();
        } finally {
            dbUtil.close(pstmt, con);
        }
        
        
        return result;
    }
    
    
    public static void main(String[] args) {
     Connection con = dbUtil.getCon();
        System.out.println("张三要转钱200块了");
        if(outMoney(new Account(1, "张三", 500), 200, con) != 0) {
            System.out.println("成功转钱");
        }
        
        
        System.out.println("张三要转钱200块了");
        if(inMoney(new Account(2, "李四", 1000), 200, con) != 0) {
            System.out.println("成功收钱");
        }
        
    }
    
    
}

操作后结果:

 

这里先是一个普通且正常运行的情况,就看拿钱也成功,取钱也成功。但如果我们现在人为地在转钱和收钱中间制造一个异常的话呢?

public static void main(String[] args) {
        
        System.out.println("张三要转钱200块了");
        if(outMoney(new Account(1, "张三", 500), 200) != 0) {
            System.out.println("成功转钱");
        }
        
        System.out.println(1/0);  //制造了一个异常
        
        System.out.println("张三要转钱200块了");
        if(inMoney(new Account(2, "李四", 1000), 200) != 0) {
            System.out.println("成功收钱");
        }
        
}

结果就是,显而易见:

 

张三是转了两百块钱,然而李四啥都没收到。这要是在现实的银行系统中还得了hhh。

所以我们这里采用事务管理的方式,给个try catch块,如果抓到错误,就回滚。

    public static void main(String[] args) {
        
        Connection con = null;
        
        try {
            
            
            
            
            con = dbUtil.getCon();
            con.setAutoCommit(false);
            
            
            System.out.println("张三要转钱200块了");
            if(outMoney(new Account(1, "张三", 500), 200, con) != 0) {
                System.out.println("成功转钱");
            }
            
            System.out.println(1/0);  //制造异常
            
            System.out.println("张三要转钱200块了");
            if(inMoney(new Account(2, "李四", 1000), 200, con) != 0) {
                System.out.println("成功收钱");
            }
            
            
            
            
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            System.out.println("转账失败!");
            try {
                
                
                con.rollback();//有错误,回滚
                System.out.println("刚刚走了回滚那一步");
                
            } catch (SQLException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
            
        } finally {
            try {
                
                
                con.commit();  //最后提交,如果是没异常,则正常提交,如果有异常,就提交回滚那个位置前面的那些操作
            
            
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            dbUtil.close(con);
        }
        
        
        
  }
    

结果:

数据库信息没变化:

刚刚我们采用全部回滚,也可以定点回滚:

Savepoint savePoint = null;

savePoint = con.setSavepoint(); //记忆点

                  
con.rollback(savePoint);//有错误,回滚到记忆点

附:

一开始我是在InMoney和OutMoney里面分别建立Connection,然后在main方法里面又搞了一个Connection用来回滚,想着说应该是可以吧,回滚到之前main这个con的初始情况。结果不行,要改成一个Connection才行。 后面百度相关信息,有看到当多个线程使用一个Connection的时候,会引起事务的混乱。。我想这也是为什么要有连接池这种技术的原因吧。

原文地址:https://www.cnblogs.com/wangshen31/p/8856575.html