栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

22-05-12 西安 jdbc(2) 德鲁伊连接池、自己封装的jdbc工具类MyQueryRunner、事务的ACID属性、引发的并发问题,事务的隔离级别、DButils

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

22-05-12 西安 jdbc(2) 德鲁伊连接池、自己封装的jdbc工具类MyQueryRunner、事务的ACID属性、引发的并发问题,事务的隔离级别、DButils

数据库连接池:

连接对象的缓冲区。负责申请,分配管理,释放连接的操作

不用数据库连接池很费事:

因为每一次Java程序要与mysql服务器通信(执行sql),都需要先建立连接,然后才能通信。

建立连接池的好处:
A:事先准备好一些连接的话,用户来了就分配给它,不用现建立连接,更快。
B:连接用完,放回池中,可以重复利用,连接的利用率增加。
C:池可以限制上限,这样服务器就不会轻易崩溃,更安全。

用了连接池,我们面对的是连接池,close()放回到连接池,不再是关闭连接

数据库连接池技术

  1. DBCP 数据库连接池
  2. C3P0 数据库连接池
  3. DRUID 阿里【德鲁伊连接池,用的最多】集DBCP 、C3P0 、Proxool 优点于一身的数据库连接池

数据库连接池:javaX.sql.DataSource接口,具体的实现是开源组织。开源万岁!

创建德鲁伊连接池方式1:不使用properties文件

    @Test
    //创建连接池方式一:
    public void test1() throws SQLException {
//       数据库连接池:javaX.sql.DataSource:接口,具体的实现是开源组织
        DruidDataSource dds = new DruidDataSource();
        dds.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dds.setUrl("jdbc:mysql://127.0.0.1:3306/test?serverTimezone=UTC");
        dds.setUsername("root");
        dds.setPassword("123456");

//        设置初始容量,和最大活动数
        dds.setInitialSize(5);
        dds.setMaxActive(10);
//        从连接池获取连接
        Connection conn = dds.getConnection();
        System.out.println(conn);
//         close()放回到连接池,不是关闭与远程数据库连接
        conn.close();
    }

创建德鲁伊连接池方式2:使用properties文件,druid.properties文件是在src下.

public class ConnPoolTest {
    @Test
    //创建连接池方式二:
    public void test2() throws Exception {
        Properties props = new Properties();
//通过类加载器帮我们加载资源配置文件
   props.load(ConnPoolTest.class.getClassLoader().getResourceAsStream("druid.properties"));
//        调用静态方法直接用druid.properties文件创建德鲁伊连接池
//        工厂模式,通过工厂类的静态方法,创建连接池对象。
        DataSource ds = DruidDataSourceFactory.createDataSource(props);
        Connection conn = ds.getConnection();
        System.out.println(conn);
        conn.close();
    }

}



MyQueryRunner

public class MyQueryRunner {
    
    public int update(Connection conn, String sql, Object ... args){
        PreparedStatement ps = null;
        int row = 0;
        try {
            //创建ps,为后面发送sql语句做准备
            ps = conn.prepareStatement(sql);
            //取可变参数中的数据,填充到占位符
            for (int i = 0; i < args.length; i++) {
                ps.setObject(i+1, args[i]);
            }
            //执行增删改sql,返回受影响的行数
            row = ps.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
             //这里是不能关闭conn的,因为在一个事务中会有多个增删改,即多次调用
             //这个update方法,不能在第一次调用后就关闭这个Connection连接
            JDBCUtils.close(null, ps);
        }
        return row;
    }
}

事务:身为程序员,必须对事务非常清楚

什么是事务:一个commit和一个rollback之间【commit】的一个或多个DML

默认情况:当一个连接对象被创建时,默认情况下是自动提交事务:每次执行一个 SQL 语句时,
如果执行成功,就会向数据库自动提交,而不能回滚

转账举例:

A账户减去100 balance
B账户加100
//转账过程中出现一个问题 int i =10/0;

转账前:

转账过程:

因为俩个不同的连接,没法成为同一个事务,所以要变化一下上一节我们写的通用增删改

    MyQueryRunner mqr = new MyQueryRunner();

    @Test
    public void test2() throws Exception {
            Connection  conn = JDBCUtils.getConnection();
            String sql1 = "update user_table set balance = balance - 100 where user = ?";
            mqr.update(conn, sql1, "AA");
            //模拟故障
            int i = 10 / 0;
            String sql2 = "update user_table set balance = balance + 100 where user = ?";
            //在俩个不同的连接上是没法成为同一个事务的,所以
            mqr.update(conn, sql2, "BB");
            //事务提交
            JDBCUtils.close(conn, null, null);
    }

 

解决思路:这俩个DML操作要么都成功,要么都失败。让俩个dml成为一个事务

1、取消自动提交事务  Connection连接对象.setAutoCommit(false)
2、成功使用commit,失败在异常处理里使用roolback

    MyQueryRunner mqr = new MyQueryRunner();

    @Test
    public void test1() {

        Connection conn = null;
        try {
            conn = JDBCUtils.getConnection();
            conn.setAutoCommit(false);//取消自动提交(开启事务)
            String sql1 = "update user_table set balance = balance - 100 where user = ?";
            mqr.update(conn, sql1, "AA");
            //模拟故障
            int i = 10 / 0;
            String sql2 = "update user_table set balance = balance + 100 where user = ?";
            //在俩个不同的连接上是没法成为同一个事务的,所以
            mqr.update(conn, sql2, "BB");
            //事务提交
            conn.commit();
        } catch (SQLException e) {
            e.printStackTrace();
            try {
                conn.rollback();//事务回滚
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.close(conn, null, null);
        }
    }

有了事务,要是一条不成功,则会回滚。


 事务的ACID属性
1.原子性  要么都成功,要么都失败。
2.一致性 保证总金额2000
3.隔离性 isolation   并发执行的各个事务之间不能互相干扰
4.持久性,一旦commit对数据库中数据的改变就是永久性的,就算数据库出现故障不应该对其有任何影响

事务引发的并发3个问题:

  1. 脏读 : t1读取了t2修改但是没有提交的数据,t2一旦回滚,这个数据对t1属于临时无效的数据
  2. 不可重复度  :针对读了字段值。在t1事务不知情 的情况下t2事务 改了数据。
  3. 幻读:针对增加或删除了表的行数,在t1事务不知情 的情况下t2事务改表

数据库的隔离级别:

解决多个事务访问共享数据的问题.
一个事务与其他事务隔离的程度称为隔离级别。隔离级别越高, 数据一致性就越好, 但并发性越弱.

Mysql 支持 4 种事务隔离级别.

Mysql 默认的事务隔离级别为: REPEATABLE READ(可重复读)

 全局变量 @@tx_isolation, 表示当前的事务隔离级别. 

-- 查看当前的隔离级别:
SELECT @@tx_isolation

-- 设置数据库系统的全局的隔离级别:
set global transaction isolation level read committed

-- 设置当前 mySQL 连接的隔离级别: 
set  transaction isolation level read committed;

DButils

是 Apache 组织提供的一个开源 JDBC工具类库,将常用的操作数据库的JDBC的类和方法集合在一起,它是对JDBC的简单封装。

ResultSetHandler,此接口用于处理数据库查询操作得到的结果集。不同的结果集的情形,由其不同的子类来实现,如下:

BeanHandler:把结果集转为一个 Bean
BeanListHandler:把结果集转为一个 Bean 的集合
MapHandler:把结果集转为一个 Map
MapListHandler:把结果集转为一个 Map 的 List
ScalarHandler:把结果集转为一个类型的数据返回

QueryRunner,提供数据库操作的一系列重载的update()和query()操作

  1. 可以实现增、删、改、查、批处理、
  2. 考虑了事务处理需要共用Connection。
  3. 该类最主要的就是简单化了SQL查询,它与ResultSetHandler组合在一起使用可以完成大部分的数据库操作,能够大大减少编码量。
  4. 不需要手动关闭连接,runner会自动关闭连接,释放到连接池中

public class DButilsTest {    
  private static DataSource ds = null;

    static {
        Properties props = new Properties();
        try {
            //加载文件获取文件中的连接数据库四要素
            props.load(DButilsTest.class.getClassLoader().getResourceAsStream("druid.properties"));
            //创建连接池,并初始化(可以在properties中指定)
            ds = DruidDataSourceFactory.createDataSource(props);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //使用开源组织dbutils中的QueryRunner
    //提供数据库操作的一系列重载的update()和query()操作
    private QueryRunner qr = new QueryRunner(ds); 

   @Test
    public void test1() throws SQLException {
            String sql = "insert into customers(id, name, email, birth) values(?,?,?,?)";
            //使用开源组织提供的DBUtils,进行通用的增删改,代替我们自己写的工具MyQueryRunner
            int row = qr.update(sql, 21, "孙尚香", "ssx@abc.com", "2002-8-19");
            System.out.println("已影响" + row + "行");
    }

}


public class DButilsTest {

    private static DataSource ds = null;

    static {
        Properties props = new Properties();
        try {
            //加载文件获取文件中的连接数据库四要素
            props.load(DButilsTest.class.getClassLoader().getResourceAsStream("druid.properties"));
            //创建连接池,并初始化(可以在properties中指定)
            ds = DruidDataSourceFactory.createDataSource(props);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //使用开源组织dbutils中的QueryRunner
    //提供数据库操作的一系列重载的update()和query()操作
    private QueryRunner qr = new QueryRunner(ds);

    @Test
    public void test5() throws SQLException {
//        String sql = "select count(*) from customers";
        String sql = "select max(birth) from customers";
//        ScalarHandler:把结果集转为一个类型的数据返回
        Object obj = qr.query(sql, new ScalarHandler());
        System.out.println(obj);
    }

    @Test
    public void test4() throws SQLException {
        String sql = "select id, name, email, birth from customers where id <= ?";
//       MapListHandler:把结果集转为一个 Map 的 List
        List> list = qr.query(sql, new MapListHandler(), 22);
        //遍历maplist
        for (Map map : list) {
            Set> entries = map.entrySet();

            for (Map.Entry entry : entries) {
                String key = entry.getKey();
                Object value = entry.getValue();

                System.out.println(key + "===" + value);
            }

            System.out.println("------------------------------------");

        }

    }

    @Test
    public void test3() throws SQLException {
        String sql = "select id, name, email, birth from customers where id <= ?";
//        BeanListHandler:把结果集转为一个 Bean 的集合
        List list = qr.query(sql, new BeanListHandler<>(Customer.class), 22);
        for (Customer cust : list) {
            System.out.println(cust);
        }
    }

    @Test
    public void test2() throws SQLException {
        String sql = "select id, name, email, birth from customers where id = ?";
//      BeanHandler:把结果集转为一个 Bean【插入不同的卡槽】
        Customer cust = qr.query(sql, new BeanHandler<>(Customer.class), "22");
        System.out.println(cust);
    }
}

写一个通用的DAO [从数据库中获取对象]

把数据库中的记录<---->Java的对象对应起来。

DAO就是为了完成增删改查,接口是可以扩展的。

public interface DAO {//Database Access Object

    
    public Object getValue(Connection conn, String sql, Object ... args) throws SQLException;

    
    public List getList(Connection conn, String sql, Object ... args) throws SQLException;

    
    public T get(Connection conn, String sql, Object ... args) throws SQLException;

    
    public int update(Connection conn, String sql, Object ... args) throws SQLException;

}

提供实现类:抽象类【转恶霸用来】
BaseDAO
考虑事务 在new 的时候qr就不需要new的时候放连接了

public abstract class BaseDAO implements DAO {

    private QueryRunner qr = new QueryRunner();
    private Class cl;

    public BaseDAO(){
        

        //要是没有反射工具类,就用上面注释掉的代码
        cl = ReflectionUtils.getSuperGenericType(this.getClass());
    }

    @Override
    public Object getValue(Connection conn, String sql, Object... args) throws SQLException {
        return qr.query(conn, sql, new ScalarHandler(), args);
    }

    @Override
    public List getList(Connection conn,  String sql, Object... args) throws SQLException {
        return qr.query(conn, sql, new BeanListHandler<>(cl), args);
    }

    @Override
    public T get(Connection conn, String sql, Object... args) throws SQLException {
        return qr.query(conn, sql, new BeanHandler<>(cl), args);
    }

    @Override
    public int update(Connection conn, String sql, Object... args) throws SQLException {
        return qr.update(conn, sql, args);
    }
}

创建一个DAO继承BaseDao

public class CustomerDAO extends BaseDAO {


}
    @Test
    public void test1(){
        CustomerDAO cd = new CustomerDAO();

        Connection conn = null;
        try {
            conn = JDBCUtils.getConnection();

            String sql = "select id, name, email, birth from customers where id = ?";
            Customer customer = cd.get(conn, sql, 20);
            System.out.println(customer);
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.close(conn, null, null);
        }

    }

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/884732.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号