DButils

严格来说,DButils不能算作框架,最多算一个小工具。

框架:框架就是一些脚手架,或者是一些模板。框架里面提供了一部分现成的代码供开发者来使用,但是要成为一个完整的产品,还需要开发者在框架的基础之上去补充一些代码。

框架不是万能的,一个框架只能在一个特定的领域起作用,不能去干所有的事情。

例如:Mybatis,在数据的持久化这一块很有优势,但是不能去做别的事情,例如去进行网络通信啊之类的。

那么框架是怎么来的呢?

举个例子,假如现在有很多人都去参与开发一些软件,这些软件都需要去保存数据到数据库。他们一开始都会使用原始的JDBC去把这些数据保存到数据库,后面发现JDBC的功能不够强大,JDBC不够灵活,所以有的人就想把JDBC抽离出来,把这部分处理数据保存的逻辑抽离出来,变成一些接口(你给他传一个sql语句,他自动给你完成后续的事情),这些接口具有独立的功能,并且有通用性。那么我们就说这个抽离出来的逻辑就是一个框架。

DBUtils能干什么

DBUtils引擎 python dbutils还有人用吗_sql

主要是帮助我们去解析返回结果,并且封装为对象或者是集合

核心类

导包

<!--DBUtils-->
<!-- https://mvnrepository.com/artifact/commons-dbutils/commons-dbutils -->
<dependency>
    <groupId>commons-dbutils</groupId>
    <artifactId>commons-dbutils</artifactId>
    <version>1.7</version>
</dependency>

DBUtils

// DBUtils这个工具类提供了几个方法
// 例如以下
public static void close(Connection conn);
public static void close(ResultSet ret);
public static void close(Statement statement);
public static void closeQuietly(Connection conn);
public static void commitAndClose(Connection conn);
……

QueryRunner

提供 了两个构造方法,一个需要传递数据源进去,还有一个不需要传递数据源进去

// 不传递数据源
public QueryRunner() {
}    
// 传递数据源
public QueryRunner(DataSource ds) {
    super(ds);
}

QueryRunner还提供了一些方法用来帮助我们去执行sql语句

// 查询
QueryRunner queryRunner = new QueryRunner(dataSource);
queryRunner.query(sql,resultSetHandler);  // 这种方式DBUtils会帮助我们关闭连接

queryRunner.query(sql,resultSetHandler,param...);  // 这种方式DBUtils会帮助我们关闭连接

// 查询-传递进去连接

QueryRunner queryRunner = new QueryRunner();  // 这个地方就不传递数据源
queryRunner.query(connection,sql,resultSetHandler);  // 这种方式DBUtils不会帮助我们关闭连接

queryRunner.query(connection,sql,resultSetHandler,param...);

// 修改、删除、增加都是使用update方法
QueryRunner queryRunner = new QueryRunner(dataSource);
queryRunner.update(sql,params...);

queryRunner.update(connection,sql,params...);

注意:在我们调用QueryRunner的方法的时候,假如我们执行queryRunner.query或者是queryRunner.update的时候,假如我们传递进去了connection对象,那么DBUtils就会不管这个connection是不是要关闭,假如没有传递,那么DButils在执行完sql相关的逻辑之后,会帮助我们关闭这个连接。

至于这个连接调用connection.close();是真的关闭连接还是放回连接池,则取决于我们连接本身,要看这个连接对象实例是否重写了close()方法

ResultSetHandler

这个接口是定义了帮助我们去解析ResultSet 这个对象的,已经实现好了几个类型帮助我们去干这个活

BeanListHandler

这个是帮助我们把查询的结果转化为一个List< Bean>

@Test
public void testQueryBeanListHandler() throws SQLException {

    // 创建QueryRunner对象
    QueryRunner queryRunner = new QueryRunner(DruidUtils.getDataSource());

    // 我们要执行sql
    String sql = "select * from user";

    List<User> userList = queryRunner.query(sql, new BeanListHandler<User>(User.class));

    System.out.println(userList);

}

BeanHandler

这个是帮助我们把查询的结果转化为一个Bean,注意这里是指我们查询结果的第一条记录

@Test
public void testSelectOne() throws SQLException {

    QueryRunner queryRunner = new QueryRunner(DruidUtils.getDataSource());

    // 我们要执行sql
    String sql = "select * from user where id = ?";

    User user = queryRunner.query(sql, new BeanHandler<User>(User.class),1);

    System.out.println(user);

}

ScalarHandler

这个是直接返回我们查询的结果(AVG、SUM、COUNT、MAX、MIN)

@Test
public void testSelectCount() throws SQLException {

    QueryRunner queryRunner = new QueryRunner(DruidUtils.getDataSource());

    String sql = "select count(1) from user";
    Object query = queryRunner.query(sql, new ScalarHandler<>());

    System.out.println(query);

}

ColunmnListHandler

这个是查询单列的一个结果,然后封装到一个List里面去

// 查询单列 ColumnListHandler
@Test
public void testSelectNameList() throws SQLException {

    QueryRunner queryRunner = new QueryRunner(DruidUtils.getDataSource());

    String sql = "select username from user";
    List<String> strList = queryRunner.query(sql, new ColumnListHandler<String>());

    System.out.println(strList);

}

这个ResultSetHandler底层是怎么做的呢?

这个是我们自己实现的解析的代码

class MyResultSetHandler implements ResultSetHandler<List<User>> {


    @Override
    public List handle(ResultSet resultSet) throws SQLException {
        ArrayList<User> users = new ArrayList<>();
        while (resultSet.next()) {

            User user = new User();
            user.setId(resultSet.getInt("id"));
            user.setUsername(resultSet.getString("username"));
            user.setPassword(resultSet.getString("password"));
            user.setGender(resultSet.getString("gender"));
            users.add(user);
        }
        return users;
    }
}
@Test
public void testQueryBeanListHandler() throws SQLException {

    // 创建QueryRunner对象
    QueryRunner queryRunner = new QueryRunner(DruidUtils.getDataSource());

    // 我们要执行sql
    String sql = "select * from user";

    List<User> userList = queryRunner.query(sql, new BeanListHandler<User>(User.class));

    System.out.println(userList);

}

结论,我们自己实现的ResultSetHandler里面有一些东西是写死的,但是这个DBUtils帮我们实现的这个BeanListHandler是通用的,为什么可以做到通用,因为我们在创建BeanListHandler的时候,需要传递一个.class对象进去,那么BeanListHandler就能通过这个字节码对象,通过反射获取到我们需要查询的列名以及类型

假如我们的Bean的成员变量名字和数据库表的列名对应不上怎么办呢?

那这个时候我们的DBUtils的局限性就显示出来了,这个工具没有办法帮助我们解决这个问题。

DBUtils批处理

// 批处理
@Test
public void testBatch() throws SQLException {

    QueryRunner queryRunner = new QueryRunner(DruidUtils.getDataSource());

    String sql = "insert into user values (?,?,?,?)";
    Object[][] params = new Object[3][];
    params[0] = new Object[]{4,"金角大王","我叫你一声你敢答应吗","male"};
    params[1] = new Object[]{5,"铁扇公主","牛夫人","female"};
    params[2] = new Object[]{6,"女儿国国王","唐长老,留下来","female"};

    // 这个一位数组是什么呢? 这个batchRet对应的是每一条sql语句影响的行数
    int[] batchRet = queryRunner.batch(sql, params);

    for (int affectedRows : batchRet) {
        System.out.println("影响的行数:" + affectedRows);
    }
}

转账案例(事务)

案例一

这个案例可以正常转账,但是有问题

@Test
    public void testTransfer1() {

        QueryRunner queryRunner = new QueryRunner(DruidUtils.getDataSource());
        Connection connection = DruidUtils.getConnection();

        try {
            connection.setAutoCommit(false);


            String sql = "update account set money = money + ? where name = ?";
            // 扣钱 -200
            int affectedRows = queryRunner.update(sql, -200, "ll");
            if (affectedRows < 1) {
                System.out.println("扣钱失败!");
                return;
            }
            // 加钱
            int affectedRows2 = queryRunner.update(sql, 200, "oo");
            if (affectedRows2 < 1) {
                System.out.println("加钱失败!!");
                connection.rollback();
            }

            connection.commit();
        }catch (Exception ex) {
            ex.printStackTrace();
            try {
                connection.rollback();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

    }
案例二

演示出问题的情况

@Test
    public void testTransfer2() {

        QueryRunner queryRunner = new QueryRunner(DruidUtils.getDataSource());
        Connection connection = DruidUtils.getConnection();

        try {
            connection.setAutoCommit(false);


            String sql = "update account set money = money + ? where name = ?";
            // 扣钱 -200
            int affectedRows = queryRunner.update(sql, -200, "ll");
            if (affectedRows < 1) {
                System.out.println("扣钱失败!");
                return;
            }

            int i = 1/0;
            // 加钱
            int affectedRows2 = queryRunner.update(sql, 200, "oo");
            if (affectedRows2 < 1) {
                System.out.println("加钱失败!!");
                connection.rollback();
            }

            connection.commit();
        }catch (Exception ex) {
            ex.printStackTrace();
            try {
                connection.rollback();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
案例三

正确的姿势

@Test
    public void testTransfer3() {

        QueryRunner queryRunner = new QueryRunner();
        Connection connection = DruidUtils.getConnection();

        try {
            connection.setAutoCommit(false);


            String sql = "update account set money = money + ? where name = ?";
            // 扣钱 -200
            int affectedRows = queryRunner.update(connection,sql, -200, "ll");
            if (affectedRows < 1) {
                System.out.println("扣钱失败!");
                return;
            }

            int i = 1/0;
            // 加钱
            int affectedRows2 = queryRunner.update(connection,sql, 200, "oo");
            if (affectedRows2 < 1) {
                System.out.println("加钱失败!!");
                connection.rollback();
            }

            connection.commit();
        }catch (Exception ex) {
            ex.printStackTrace();
            try {
                connection.rollback();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }finally {
            // 关闭连接
            DbUtils.closeQuietly(connection);
        }
    }

总结

DBUtils大家觉得好不好?

优点:

  • 对比JDBC,代码要简洁很多
  • 批处理操作更加方便
  • 去解析结果的时候更加通用

缺点:

  • 功能不够强大(例如:如果我们Bean的成员变量名字和数据库对应不上,那么就可能封装的时候出问题)
  • 在代码里面写sql语句不够优雅,耦合度比较高

所以在工作以后,大家用DBUtils用的不多,那么在工作里面,我们都使用Mybatis