DBUtils 学习使用

commons-dbutils简介

commons-dbutils是Apache组织提供的一个开源JDBC工具类库,它是对JDBC的简单封装,学习成本极低,并且使用dbutils能极大简化jdbc编码的工作量,同时也不会影响程序的性能。因此dbutils成为很多不喜欢hibernate的公司的首选。 dbutils的官方网站http://commons.apache.org/proper/commons-dbutils/download_dbutils.cgi

dbutils 优点

  1. 对于数据表的读操作,他可以把结果转换成List,Array,Set等java集合,便于程序员操作;
  2. 对于数据表的写操作,也变得很简单(只需写sql语句)
  3. 可以使用数据源,使用JNDI,数据库连接池(dbcp,c3p0)等技术来优化性能--重用已经构建好的数据库连接对象,而不像php,asp那样,费时费力的不断重复的构建和析构这样的对象。

commons-dbutils API介绍

  • org.apache.commons.dbutils.QueryRunner QueryRunner (insert、update、delete使用)
  • org.apache.commons.dbutils.ResultSetHandler (用于查询结果处理使用)
  • org.apache.commons.dbutils.DbUtils (工具类)

QueryRunner 类的主要方法

  • public Object query(Connection conn, String sql, Object[] params, ResultSetHandler rsh) throws SQLException

执行一个查询操作,在这个查询中,对象数组中的每个元素值被用来作为查询语句的置换参数。该方法会自行处理PreparedStatement和ResultSet的创建和关闭。

  • public Object query(String sql, Object[] params, ResultSetHandler rsh) throws SQLException

几乎与第一种方法一样;唯一的不同在于它不将数据库连接提供给方法,并且它是从提供给构造方法的数据源(DataSource)或使用的setDataSource方法中重新获得Connection。

  • public Object query(Connection conn, String sql, ResultSetHandler rsh) throws SQLException

执行一个不需要置换参数的查询操作。

  • public int update(Connection conn, String sql, Object[] params) throws SQLException

用来执行一个更新(插入、更新或删除)操作。

  • public int update(Connection conn, String sql) throws SQLException

用来执行一个不需要置换参数的更新操作。

  • public int[] batch(Connection conn, String sql, Object[][] params) throws SQLException

这个方法对应着批处理,经常用于在同一个表中批量插入数据,或批量更新表的数据。 该方法为何会接收二维数组Object[][] params呢? 答:例如现在要想在同一个表中批量插入数据,编写的SQL语句为:

  String sql = "insert into users(id,name) values(?,?)";

该方法接收二维数组Object[][] params,那么调用其的时候就要传递一个诸如这样的实参[[1,aa],[2,bb],[3,cc]],即用二维数组里面的每一个一维数组生成一条sql语句。 那为何又会返回int[]呢? 答:该方法的返回值是int[],所以会返回诸如这样的结果:[1,1,1],意思是生成的第一条sql语句影响数据库几行、生成的第二条sql语句影响数据库几行、生成的第三条sql语句影响数据库几行。

ResultSetHandler接口使用讲解

该接口用于处理java.sql.ResultSet,将数据按要求转换为另一种形式。ResultSetHandler接口提供了一个单独的方法:Object handle (java.sql.ResultSet .rs)

ResultSetHandler接口的实现类

  • ArrayHandler:把结果集中的第一行数据转成对象数组。
  • ArrayListHandler:把结果集中的每一行数据都转成一个数组,再存放到List中。
  • BeanHandler:将结果集中的第一行数据封装到一个对应的JavaBean实例中。
  • BeanListHandler:将结果集中的每一行数据都封装到一个对应的JavaBean实例中,存放到List里。
  • ColumnListHandler:将结果集中某一列的数据存放到List中。
  • KeyedHandler(name):将结果集中的每一行数据都封装到一个Map里,再把这些map再存到一个map里,其key为指定的key。
  • MapHandler:将结果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值。
  • MapListHandler:将结果集中的每一行数据都封装到一个Map里,然后再存放到List。

DbUtils类使用讲解

DbUtils:提供如关闭连接、装载JDBC驱动程序等常规工作的工具类,里面的所有方法都是静态的。主要方法如下:

  • public static void close(…) throws java.sql.SQLException DbUtils类提供了三个重载的关闭方法。这些方法检查所提供的参数是不是NULL,如果不是的话,它们就关闭Connection、Statement和ResultSet。
  • public static void closeQuietly(…) 这一类方法不仅能在Connection、Statement和ResultSet为NULL情况下避免关闭,还能隐藏一些在程序中抛出的SQLException。
  • public static void commitAndCloseQuietly(Connection conn) 用来提交连接,然后关闭连接,并且在关闭连接时不抛出SQL异常。
  • public static boolean loadDriver(java.lang.String driverClassName) 这一方法装载并注册JDBC驱动程序,如果成功就返回true。使用该方法,你不需要捕捉这个异常ClassNotFoundException。

使用DBUtils完成数据库的CRUD

在使用DBUtils完成数据库的CRUD之前,我们先编写测试用的SQL脚本:

CREATE TABLE `user` (
  `id` int(11) NOT NULL auto_increment,
  `name` varchar(50) NOT NULL,
  `age` tinyint(10) NOT NULL,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

使用dbutils遵从以下步骤

  1. 加载JDBC驱动程序类,并用DriverManager来得到一个数据库连接conn。
  2. 实例化 QueryRunner,得到实例化对象qRunner。
  3. qRunner.update()方法,执行增改删的sql命令,qRunner.query()方法,得到结果集。

获取连接对象

private static final String url = "jdbc:mysql://192.168.1.15:3306/test?useUnicode=true&characterEncoding=utf8";
private static final String driver = "com.mysql.jdbc.Driver";
private static final String user = "znsd_test";
private static final String password = "123456";

public static Connection getConnect() {
    Connection conn = null;
    try {
        Class.forName(driver);
        conn = DriverManager.getConnection(url, user, password);
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (SQLException e) {
        e.printStackTrace();
    }
    return conn;
}

创建bean对象关联数据库

public class User implements Serializable {
    private Integer id; // 用户ID
    private String name; // 用户姓名
    private Integer age;  // 用户年龄
    // ...忽略set、get方法
}

添加

public Integer add(User user) {
        QueryRunner queryRunner = new QueryRunner();
        Connection connect = ConnectionUtil.getConnect();
        try {
            return queryRunner.update(connect, "insert into user(name, age) values(?, ?)", user.getName(), user.getAge());
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DbUtils.closeQuietly(connect);
        }
        return 0;
}

修改

public Integer update(User user) {

        QueryRunner queryRunner = new QueryRunner();
        Connection conn = ConnectionUtil.getConnect();
        try {
            return queryRunner.update(conn , "update user set name = ?, age = ? where id = ?", user.getName(), user.getAge(), user.getId());
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DbUtils.closeQuietly(conn);
        }
        return 0;
}

删除

public Integer delete(Integer id) {

        QueryRunner queryRunner = new QueryRunner();
        Connection conn = ConnectionUtil.getConnect();
        try {
            return queryRunner.update(conn , "delete from user where id = ?", id);
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DbUtils.closeQuietly(conn);
        }
        return 0;
}

批量添加

public int[] batch(Object[][] values) {

        QueryRunner queryRunner = new QueryRunner();
        Connection conn = ConnectionUtil.getConnect();
        try {
            return queryRunner.batch(conn, "insert into user(name, age) values(?, ?)", values);
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DbUtils.closeQuietly(conn);
        }
        return null;
}

查询所有

public List<User> select() {

        QueryRunner queryRunner = new QueryRunner();
        Connection conn = ConnectionUtil.getConnect();
        try {
            return queryRunner.query(conn, "select id id1, name, age from user", new UserBeanListHandler(User.class));
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DbUtils.closeQuietly(conn);
        }
        return null;
}

查询返回map

public List<Map<String, Object>> selectAsMap() {

        QueryRunner queryRunner = new QueryRunner();
        Connection conn = ConnectionUtil.getConnect();
        try {
            List<Map<String, Object>> userMaps = queryRunner.query(conn, "select id id1, name, age from user", new MapListHandler());
            return userMaps;
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DbUtils.closeQuietly(conn);
        }
        return null;
}

查询单个对象

public User selectOne(Integer id) {

        QueryRunner queryRunner = new QueryRunner();
        Connection conn = ConnectionUtil.getConnect();
        try {
            return queryRunner.query(conn, "select id id1, name, age from user where id = ? limit 1", new BeanHandler<User>(User.class) {
                @Override
                public User handle(ResultSet rs) throws SQLException {
                    if (rs.next()) {
                        return new User(rs.getInt("id1"), rs.getString("name"), rs.getInt("age"));
                    }
                    return null;
                }
            }, id);
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DbUtils.closeQuietly(conn);
        }
        return null;
}

结合c3p0数据源使用dbutils

c3p0简介

C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate,Spring等

数据库连接池简介

数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;连接池允许多个客户端使用缓存起来的连接对象,这些对象可以连接数据库,它们是共享的、可被重复使用的。

打开/关闭数据库连接开销很大,连接池技术允许我们在连接池里维护连接对象,这样可以提高数据库的执行命令的性能。多个客户端请求可以重复使用相同的连接对象,当每次收到一个客户端请求时,就会搜索连接池,看看有没有闲置的连接对象。如果没有闲置对象的话,要么所有的客户端请求都进入队列排队,要么在池中创建一个新的连接对象(这取决于池里已经有多少个连接存在以及配置支持多少连接)。一旦某个请求使用完连接对象之后,这个对象会被重新放入池中,然后会被重新分派给排队等待的请求(分派给哪个请求要看使用什么调度算法)。因为大部分请求都是使用现存的连接对象,所以连接池技术大大减少了等待创建数据库连接的时间,从而减少了平均连接时间。

使用连接池的优点

  • 资源重用:

由于数据库连接得以重用,避免了频繁创建,释放连接引起的大量性能开销。在减少系统消耗的基础上,另一方面也增加了系统运行环境的平稳性。

  • 更快的系统反应速度:

数据库连接池在初始化过程中,往往已经创建了若干数据库连接置于连接池中备用。此时连接的初始化工作均已完成。对于业务请求处理而言,直接利用现有可用连接,避免了数据库连接初始化和释放过程的时间开销,从而减少了系统的响应时间。

  • 统一的连接管理,避免数据库连接泄露:

在较为完善的数据库连接池实现中,可根据预先的占用超时设定,强制回收被占用连接,从而避免了常规数据库连接操作中可能出现的资源泄露。

使用步骤

  1. 首先要导入c3p0和dbutils以及mysql的jar包
  • c3p0-0.9.5.2.jar
  • commons-dbutils-1.7.jar
  • mchange-commons-java-0.2.11.jar
  • mysql-connector-java-5.1.22.jar
  1. 编写c3p0的配置文件c3p0-config.xml

在src目录下存放c3p0的配置文件,配置文件是c3p0自己去识别并读入的,我们不需要在代码中做任何的操作,但是配置文件一定要命名为c3p0-config.xml

   <?xml version="1.0" encoding="UTF-8"?>
   <c3p0-config>
    <!-- 这是默认配置信息 -->
    <default-config>
        <!-- jdbc连接四大参数配置 -->
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://192.168.1.15:3306/test</property>
        <property name="user">znsd_test</property>
        <property name="password">123456</property>

        <!-- 池参数配置 -->
        <!--JDBC的标准参数,用以控制数据源内加载的PreparedStatements数量。但由于预缓存的statements
            属于单个connection而不是整个连接池。所以设置这个参数需要考虑到多方面的因素。
            如果maxStatements与maxStatementsPerConnection均为0,则缓存被关闭。Default: 0
        -->
        <property name="acquireIncrement">3</property>
        <!--初始化时获取的连接数,取值应在minPoolSize与maxPoolSize之间。Default: 3 -->
        <property name="initialPoolSize">10</property>
        <!--连接池中保留的最小连接数。-->
        <property name="minPoolSize">2</property>
        <!--连接池中保留的最大连接数。Default: 15 -->
        <property name="maxPoolSize">10</property>
    </default-config>
   </c3p0-config>
  1. 使用c3p0获取获取数据库连接对象
   public class C3p0ConnectionUtil {

    // 配置文件的默认配置!要求你必须给出c3p0-config.xml
    private static ComboPooledDataSource c3p0DataSource = new ComboPooledDataSource();

    /**
     * 获取连接对象
     * @return
     */
    public static Connection getConnection() {
        try {
            // 得到连接器
            return c3p0DataSource.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取数据源
     * 
     * @return
     */
    public static DataSource getDataSource() {
        return c3p0DataSource;
    }
   }
  1. dbutils 使用c3p0
   public Integer add(User user) {
        QueryRunner queryRunner = new QueryRunner(C3p0ConnectionUtil.getDataSource());
        try {
            return queryRunner.update("insert into user(name, age) values(?, ?)", user.getName(), user.getAge());
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return 0;
   }
  1. 返回主键例子
   public Integer add(User user) {
        DataSource dataSource = C3p0ConnectionUtil.getDataSource();
        QueryRunner queryRunner = new QueryRunner(dataSource);
        try {
            String sql = "insert into user(name, age) values(?, ?)";
                // 需要条用insert方法,指定MapHandler参数
            Map<String, Object> idMap = queryRunner.insert(sql, new MapHandler() {

                @Override
                public Map<String, Object> handle(ResultSet rs) throws SQLException {

                    //rs = rs.getStatement().getGeneratedKeys();
                        // 获取主键
                    int id = rs.next() ? rs.getInt(1) : -1;
                    // 将数据库返回主键放入map中
                    Map<String, Object> idMap = new HashMap<String, Object>();
                    idMap.put("id", id);

                    return idMap;
                }
            }, user.getName(), user.getAge());

            return (Integer) idMap.get("id");
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return 0;
   }