在获取数据库连接对象connection时使用ThreadLocal从而保证仅有一个不变的connection进行数据库事务的操作

在使用封装的通用操作数据库方法里,存在获取数据库连接对象的操作,从而导致每次有sql语句调用此通用方法时都会获得一个不同的数据库连接对象,导致这些sql语句不能组成事务(因为Connection的setAutoCommit()方法是针对Connection对象生效的)所以会导致下面的情况:两条sql”组成了表面上的事务“,但其中一条出错后也不能回滚,一个账户减了500元,另一个账户没有加上。

public class GetDifferentConnection {
    public static Connection getConnection() throws Exception {
        Properties properties = new Properties();
        properties.load(new FileInputStream(new File("D:\\develop\\IdeaProject\\SGG\\day28\\src\\BasicDAO\\druid.properties")));
        DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
        Connection connection = dataSource.getConnection();
        return connection;
    }
    public static void update(String sql, Object... args) throws Exception {
        Connection conn = getConnection();
        System.out.println(conn);
        PreparedStatement pst = conn.prepareStatement(sql);
        if (args != null && args.length > 0) {
            for (int i = 0; i < args.length; i++) {
                pst.setObject(i + 1, args[i]);
            }
        }
        //开始进行事务处理
        conn.setAutoCommit(false);
        try {
            pst.executeUpdate();
            conn.commit();
        } catch (Exception e) {
            System.out.println(e.getMessage());
            conn.rollback();
        } finally {
            pst.close();
        }
    }
}
class Demo2 {
    public static void main(String[] args) throws Exception {
        String sql1 = "UPDATE acccount SET balance = balance -500 WHERE id=1;";
        String sql2 = "UPDATE acccount SET balance = balance +500 WHARE id=2;";
        GetDifferentConnection.update(sql1);//com.mysql.jdbc.JDBC4Connection@6385cb26
        GetDifferentConnection.update(sql2);//com.mysql.jdbc.JDBC4Connection@c8e4bb0
        //每执行一次update()方法,都会执行一次getConnection()方法,导致两次的Connection对象是两个不同的,
        // 并且事务处理时的setAutoCommit(false)方法,只针对调用此方法的Connection对象,
        //所以,com.mysql.jdbc.JDBC4Connection@6385cb26执行成功,com.mysql.jdbc.JDBC4Connection@c8e4bb0执行失败,
        //失败的这个确实会回滚,但由于成功的com.mysql.jdbc.JDBC4Connection@6385cb26与失败的com.mysql.jdbc.JDBC4Connection@c8e4bb0不是同一个对象,
        //所以这两个不同的连接对象不能构成一个事务,所以失败的回滚自己不会影响成功的修改数据
    }
}

数据库原始状态
执行了错误的事务代码之后的状态
执行了错误的事务代码之后的控制台输出

首先使用静态成员数组的方式进行修改,以方便更了解后面使用ThreadLocal的方式。因为这两个方式类似。

原理:每次调用getConnection()获取数据库连接对象时都要判断静态数组里是不是有了一个了,如果有了就不再创建新的,保证了多次执行update()方法时仅有一个数据库连接对象

public class GetSameConnection {
    //创建一个保存一个Connection对象的静态数组
    static Connection[] connections = new Connection[1];

    public static void getConnection() throws Exception {
        Properties properties = new Properties();
        properties.load(new FileInputStream(new File("D:\\develop\\IdeaProject\\SGG\\day28\\src\\BasicDAO\\druid.properties")));
        DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
        //每次调用getConnection()获取数据库连接对象时都要判断静态数组里是不是有了一个了,如果有了就不再创建新的,保证了多次执行update()方法时仅有一个数据库连接对象
        if (connections[0] == null) {
            connections[0] = dataSource.getConnection();
        }
    }

    public static void update(String sql, Object... args) throws Exception {
        getConnection();
        Connection conn = connections[0];
        System.out.println(conn);
        PreparedStatement pst = conn.prepareStatement(sql);
        if (args != null && args.length > 0) {
            for (int i = 0; i < args.length; i++) {
                pst.setObject(i + 1, args[i]);
            }
        }
        conn.setAutoCommit(false);
        try {
            pst.executeUpdate();
            conn.commit();
        } catch (Exception e) {
            System.out.println(e.getMessage());
            conn.rollback();
        } finally {
            pst.close();
        }
    }
}

class Demo3 {
    public static void main(String[] args) throws Exception {
        String sql1 = "UPDATE acccount SET balance = balance -500 WHERE id=1;";//com.mysql.jdbc.JDBC4Connection@6385cb26
        String sql2 = "UPDATE acccount SET balance = balance +500 WHARE id=2;";//com.mysql.jdbc.JDBC4Connection@6385cb26
        GetSameConnection.update(sql1);
        GetSameConnection.update(sql2);
    }
}

静态数组方式的控制台

接下来就是使用ThreadLocal的方式

使用ThreadLocal可以存储每个线程的备份资源,因为都是main线程去执行update方法,也当然是只有一个main线程进入到了getConnection方法,所以该类里的ThreadLocal对象也是唯一不变的。

第一次执行getConnection时,main线程的ThreadLocal中没有Connection对象,则会使用set方法给装入一个Connection对象。之后每次执行getConnection时,由于main线程的ThreadLocal中存在Connection对象了,就不会再set了,保证了多次执行getConnection方法都只有一个不变的数据库连接对象

public class GetConnectionThreadLocal {
    static ThreadLocal<Connection> threadLocal = new ThreadLocal<>();
    //使用ThreadLocal可以存储每个线程的备份资源,因为都是main线程去执行update方法,
    //也当然是只有一个main线程进入到了getConnection方法,所以该类里的ThreadLocal对象也是唯一不变的
    //第一次执行getConnection时,main线程的ThreadLocal中没有Connection对象,则会使用set方法给装入一个Connection对象
    //之后每次执行getConnection时,由于main线程的ThreadLocal中存在Connection对象了,就不会再set了,保证了多次执行getConnection方法都只有一个不变的数据库连接对象
    public static void getConnection() throws Exception {
        Properties properties = new Properties();
        properties.load(new FileInputStream(new File("D:\\develop\\IdeaProject\\SGG\\day28\\src\\BasicDAO\\druid.properties")));
        DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
        if (threadLocal.get() == null) {
            Connection connection = dataSource.getConnection();
            threadLocal.set(connection);
        }
    }
    public static void update(String sql, Object... args) throws Exception {
        getConnection();
        Connection conn = threadLocal.get();
        System.out.println(conn);
        PreparedStatement pst = conn.prepareStatement(sql);
        if (args != null && args.length > 0) {
            for (int i = 0; i < args.length; i++) {
                pst.setObject(i + 1, args[i]);
            }
        }
        conn.setAutoCommit(false);
        try {
            pst.executeUpdate();
            conn.commit();
        } catch (Exception e) {
            System.out.println(e.getMessage());
            conn.rollback();
        } finally {
            pst.close();
        }
    }
}
class Demo {
    public static void main(String[] args) throws Exception {
        String sql1 = "UPDATE acccount SET balance = balance -500 WHERE id=1;";
        String sql2 = "UPDATE acccount SET balance = balance +500 WHERE id=2;";
        GetSameConnection.update(sql1);
        GetSameConnection.update(sql2);
    }
}

ThreadLocal方式的控制台

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我的Y同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值