JDBC事务+工具类+行级锁for update

一、JDBC手动提交事务      

  模拟一个JDBC事务,实现A账户向B账户转账一万。A账户减去一万和B账户加上一万,必须同时成功或者失败。SQL建表语句:

drop table if exists bankAccount;
create table bankAccount(
actno varchar(1),
bank_money double(7,2),
#10和2分别是有效数字和小数的个数
primary key(actno));
insert into bankaccount values ('A',20000.69);
insert into bankaccount values ('B',10000.45);

        然后在A转账和B收钱之间插入一个异常:

package com.fan;

import java.sql.*;
import java.util.ResourceBundle;

public class Test {
    public static void main(String[] args){
        Connection con=null;
        PreparedStatement ps=null;
        ResourceBundle db=ResourceBundle.getBundle("resource/db");
        String driver=db.getString("driver");
        String url=db.getString("url");
        String user=db.getString("user");
        String password=db.getString("password");
        try {
            Class.forName(driver);
            con=DriverManager.getConnection(url,user,password);
            /*令A的bankmoney减去10000
            * */
            String sql="update bankaccount set bank_money=? where actno=?";
            ps=con.prepareStatement(sql);
            ps.setDouble(1,10000);
            ps.setString(2,"A");
            ps.executeUpdate();

            //出现异常,程序直接转入catch语句执行
            String s=null;
            s.toString();

            /*这里再模拟B受到转账
            * */
            sql="update bankaccount set bank_money=? where actno=?";
            ps=con.prepareStatement(sql);
            ps.setDouble(1,20000);
            ps.setString(2,"B");
            } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        }finally {
            if(ps!=null){
                try {
                    ps.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (con!=null){
                try {
                    con.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

        因为JDBC的事务是自动提交的,所以上述事务中A执行了而B没有执行,从而发生错误。因此在实际开发中需要关闭JDBC的自动提交机制,改成手动提交。在获得数据库连接后就要关闭自动提交。

            con=DriverManager.getConnection(url,user,password);
            //关闭自动提交模式
            con.setAutoCommit(false);

        在正常执行结束后,在上述程序try语句块最后设置手动提交。

con.commit()

        出现异常,在try语句块进行回滚。

            catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
            //出现异常进行回滚
            if (con!=null){
                con.rollback();
            }

 二、JDBC工具类封装

        编写一个工具类,便于JDBC的编写。

package com.fan.JDBCtool;

import java.sql.*;
import java.util.ResourceBundle;

public class DButil {
    //工具类中的构造方法一般都是静态的,防止new对象
    private DButil(){

    }

    //从配置文件获取驱动等名称
    private static ResourceBundle rb=ResourceBundle.getBundle("resource/db");

    //封装一个静态代码块加载驱动
    //在调用DButil工具类的时候,就会在类加载时先执行静态代码块
    //由于静态代码块只加载一次,第二次调用工具类里面的方法,不会执行静态代码块
    static{
        try {
            Class.forName(rb.getString("driver"));
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    //获取连接的方法
    public static Connection getConnection() throws SQLException {
        String url=rb.getString("url");
        String user=rb.getString("user");
        String password=rb.getString("password");
        return DriverManager.getConnection(url,user,password);
    }

    //设置关闭连接等对象的方法
    //用Statement,若传入的对象是PreparedStatement,可以使用多态
    public  static  void close(Connection con, Statement sta, ResultSet rs){
        if (rs!=null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (sta!=null){
            try {
                sta.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (con!=null){
            try {
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

        测试工具类:

package com.fan;

import com.fan.JDBCtool.DButil;

import java.sql.*;
import java.util.ResourceBundle;

public class Test {
    public static void main(String[] args) {
        Connection con=null;
        PreparedStatement ps=null;
        ResultSet rs=null;
        try {
            con= DButil.getConnection();
            con.setAutoCommit(false);
            String sql="select sname from student where sid like ?";
            ps=con.prepareStatement(sql);
            ps.setString(1,"0%");
            rs=ps.executeQuery();
            while(rs.next()){
                System.out.println(rs.getString("sname"));
            }
            con.commit();
        } catch (SQLException e) {
            e.printStackTrace();
            if (con!=null){
                try {
                    con.rollback();
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }
            }
        }finally {
            DButil.close(con,ps,rs);
        }
    }
}

三、行级锁for update

        在DQL语句后面加上for update,意思是在查询的过程中,其他任何事务不能对查询的记录进行操作,直到当前事务结束。该锁又称悲观锁。

       行级锁使用索引,当没有索引或者索引失效会锁住整张表。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值