#Java# 文章列表 Java 安装 Java:第一个程序 Hello World Java:建议使用 UTF-8 编写 Java 代码 Java:package 包命名规范 使用 Intellij IDEA 创建 Java 项目 Java 布尔类型 Java 处理日期和时间 Java 正则表达式 Java finalize 方法 Java:空值 null Java 如何触发垃圾回收 Java ThreadLocal Java InheritableThreadLocal Java Integer之间的比较 Java 动态代理 Java 匿名类 Java 枚举 Java 如何静态导入 import static println Java 引用级别:强引用、软引用、弱引用、幽灵引用 Java try finally return 解惑 Java WeakHashMap Java ReferenceQueue 怎么写 Java 示例代码? Java 匿名类双大括号初始化 什么是 Java Bean Java 多行字符串 Java 快速生成 List Java 快速生成 Map Java 将异常堆栈转换为 String JDK SPI 的使用和源码分析 Java Map 中的 key 和 value 能否为 null ? Java List 和 数组的互相转换 Java 获取环境变量 Java 获取和设置系统属性 Java:如何获取当前进程的 PID ? Java 字符串左侧 右侧补充空格或者其他字符 Java 线程 Java:如何获取文本文件内容 Java:读取资源文件内容 Java:使用 JavaFx 构建 GUI Java:Class 类 Java:使用 instanceof 判断对象类型 一个自定义的 Java 工具类 Java:获取当前函数所属类的类名 Java:获取当前执行的函数名 Java:使用 String 的 split 函数拆分字符串 Java:获取字符的 Unicode 编号(代码点) Java:获取当前工作目录 Java:使用 Class 对象的 isArray 方法判断对象是否为数组 使用 Java 生成 CSV 文件 Java Mockito 测试框架快速入门 JUnit 入门 JUnit 单测隔离 Java JOOR 反射库 Java alibaba transmittable-thread-local 库:让 ThreadLocal 跨线程传播 Java 日志组件 slf4j 的使用和源码分析 Java Lombok 库:为你减少样板代码 Java:使用 cglib 实现动态代理 Java Hibernate validator 校验框架 Java 使用 Hessian2 序列化和反序列化 H2 数据库快速入门 Java:使用 Gson 库处理 JSON 数据 Java 集成 groovy 构建规则引擎 Java 13:安装 Java 13 新特性:文本块(多行字符串) 卸载 MacOS 上安装的 Java Java:执行 sql 文件 Java JDK 有哪些发行版 ? java拾遗:String和数组 java拾遗:由反转数组想到System.out的实现机制 java拾遗:如何读取properties文件内容 Java并发概念汇总 java拾遗:System.out.println()是什么? java拾遗:通过示例理解位运算 使用“庖丁解牛”进行中文分词 DBUtils简明教程 试用velocity模板引擎 Java:将字符串哈希为数字 kafka SnappyError no native library is found 问题

DBUtils简明教程


#Java#


2015-03-09

本文以Insert和Select操作为例子,介绍apache DBUtils的使用方法和实现机制。

The Commons DbUtils library is a small set of classes designed to make working with JDBC easier

准备工作

建立表格:

CREATE  TABLE `test`.`user` (
  `id` INT NOT NULL AUTO_INCREMENT ,
  `name` VARCHAR(45) NOT NULL ,
  `age` INT NOT NULL ,
  PRIMARY KEY (`id`) ,
  UNIQUE INDEX `name_UNIQUE` (`name` ASC) )
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8
COLLATE = utf8_general_ci;

http://dev.mysql.com/downloads/connector/j下载mysql-connector-java-5.1.34.tar.gz

http://commons.apache.org/proper/commons-dbutils/download_dbutils.cgi下载commons-dbutils-1.6-bin.tar.gz

首先创建DBConf.java,内容如下:

package hellodbutils;

public class DBConf {
   static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";  
   static final String DB_URL = "jdbc:mysql://localhost/test";
   
    static final String USER = "username";
    static final String PASS = "password";
}

先看一个JDBC的示例

// 类hellodbutils.JDBCInsert
package hellodbutils;
import java.sql.*;

public class JDBCInsert {
    public static void main(String args[]) {
        Connection conn = null;
        PreparedStatement pstmt = null;
        try {
            Class.forName(DBConf.JDBC_DRIVER);
            conn = DriverManager.getConnection(DBConf.DB_URL, DBConf.USER, DBConf.PASS);
            
            String sql = "INSERT INTO user(name, age) VALUES(?,?)";
            pstmt = conn.prepareStatement(sql);
            // insert a user
            pstmt.setString(1, "letian");
            pstmt.setInt(2, 18);
            pstmt.execute();
            // insert another user
            pstmt.setString(1, "letiantian");
            pstmt.setInt(2, 19);
            pstmt.execute();
            
            //select
            sql = "SELECT * FROM user";
            pstmt = conn.prepareStatement(sql);
            ResultSet rs = pstmt.executeQuery();
            while(rs.next()) {
                System.out.println(rs.getInt("id"));
                System.out.println(rs.getString("name"));
                System.out.println(rs.getInt("age"));
                System.out.println("------");
            }
            
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    
    
    }
}

运行结果如下:

1
letian
18
------
2
letiantian
19
------

MySQL命令行客户端查询结果:

mysql> select * from user;
+----+------------+-----+
| id | name       | age |
+----+------------+-----+
|  1 | letian     |  18 |
|  2 | letiantian |  19 |
+----+------------+-----+

使用DBUtils插入数据

首先清空user表中的数据:

mysql> delete from user;
mysql> ALTER TABLE user AUTO_INCREMENT = 1;

编写代码:

// 类hellodbutils.DBUtilsInsert
package hellodbutils;

import java.sql.*;
import org.apache.commons.dbutils.*;
import org.apache.commons.dbutils.handlers.ArrayHandler;

public class DBUtilsInsert {
    public static void main(String args[]) {
        Connection conn;
        QueryRunner queryRunner = new QueryRunner();
        ArrayHandler arrayHandler = new ArrayHandler();
        try {
            Class.forName(DBConf.JDBC_DRIVER);
            conn = DriverManager.getConnection(DBConf.DB_URL, DBConf.USER, DBConf.PASS);
            
            String sql = "INSERT INTO user(name, age) VALUES(?,?)";
            Object[] objectArr= queryRunner.insert(conn, sql, arrayHandler, "樂天", 18);
            System.out.println("数组长度:" + objectArr.length + ";第0个元素的值:" +objectArr[0]);
            objectArr = queryRunner.insert(conn, sql, arrayHandler, "樂天天", 19);
            System.out.println("数组长度:" + objectArr.length + ";第0个元素的值:" +objectArr[0]);

        } catch (Exception ex) {
            ex.printStackTrace();
        }
    
    }
}

运行结果:

数组长度:1;第0个元素的值:1
数组长度:1;第0个元素的值:2

MySQL命令行客户端查询结果:

mysql> select * from user;
+----+-----------+-----+
| id | name      | age |
+----+-----------+-----+
|  1 | 樂天      |  18 |
|  2 | 樂天天    |  19 |
+----+-----------+-----+
2 rows in set (0.33 sec)

源码分析:

上面的句子调用了类org.apache.commons.dbutils.QueryRunner的这个方法:

// 类`org.apache.commons.dbutils.QueryRunner`下方法insert(...)
public <T> T insert(Connection conn, String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException {
    return insert(conn, false, sql, rsh, params);
}

最终调用的是下面的重载方法:

// 类`org.apache.commons.dbutils.QueryRunner`下方法insert(...)
private <T> T insert(Connection conn, boolean closeConn, String sql, ResultSetHandler<T> rsh, Object... params)
        throws SQLException {

    // .... 删去了若干代码

    PreparedStatement stmt = null;
    T generatedKeys = null;

    try {
        stmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
        this.fillStatement(stmt, params);
        stmt.executeUpdate();
        ResultSet resultSet = stmt.getGeneratedKeys(); 
        generatedKeys = rsh.handle(resultSet);
    } catch (SQLException e) {
        this.rethrow(e, sql, params);
    } finally {
        close(stmt);
        if (closeConn) {
            close(conn);
        }
    }

    return generatedKeys;
}

this.fillStatement方法来自类org.apache.commons.dbutils.QueryRunner的父类org.apache.commons.dbutils.AbstractQueryRunnerfillStatement的主要内容是:

// 类`org.apache.commons.dbutils.AbstractQueryRunner`下方法fillStatement(...)
public void fillStatement(PreparedStatement stmt, Object... params)
        throws SQLException {

    // .... 删去了若干代码

    for (int i = 0; i < params.length; i++) {
        if (params[i] != null) {
            stmt.setObject(i + 1, params[i]);
        } else {
            // .... 删去了若干代码
        }
    }
}

javadoc中如下介绍stmt.getGeneratedKeys()

ResultSet getGeneratedKeys()
                           throws SQLException
Retrieves any auto-generated keys created as a result of executing this Statement object. If this Statement object did not generate any keys, an empty ResultSet object is returned.
Note:If the columns which represent the auto-generated keys were not specified, the JDBC driver implementation will determine the columns which best represent the auto-generated keys.

org.apache.commons.dbutils.QueryRunnerinsert(...)函数有一参数是ResultSetHandler<T> rsh,ResultSetHandler用来处理sql查询后得到的结果。在类hellodbutils.DBUtilsInsert中,我们使用的ResultSetHandler是类org.apache.commons.dbutils.handlers.ArrayHandler

// 类`org.apache.commons.dbutils.handlers.ArrayHandler`
public class ArrayHandler implements ResultSetHandler<Object[]> {
    // .... 删去了若干代码
    @Override
    public Object[] handle(ResultSet rs) throws SQLException {
        return rs.next() ? this.convert.toArray(rs) : EMPTY_ARRAY;
    }
}

this.convert默认值是new BasicRowProcessor()。类BasicRowProcessor在包org.apache.commons.dbutils中,其中toArray(...)函数详细如下:

// 类org.apache.commons.dbutils.BasicRowProcessor下的toArray(...)函数
@Override
public Object[] toArray(ResultSet rs) throws SQLException {
    ResultSetMetaData meta = rs.getMetaData();
    int cols = meta.getColumnCount();
    Object[] result = new Object[cols];

    for (int i = 0; i < cols; i++) {
        result[i] = rs.getObject(i + 1);
    }

    return result;
}

好了,到了这里,类hellodbutils.DBUtilsInsert使用DBUtils插入数据的思路也就知道了。

使用DBUtils获取一条数据

编写代码:

// 类hellodbutils.DBUtilsSelect
package hellodbutils;

import java.util.Map;
import java.sql.*;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.*;

public class DBUtilsSelect {
    public static void main(String args[]) {
        Connection conn;
        QueryRunner queryRunner = new QueryRunner();
        try {
            Class.forName(DBConf.JDBC_DRIVER);
            conn = DriverManager.getConnection(DBConf.DB_URL, DBConf.USER, DBConf.PASS);
            
            String sql = "SELECT id, name, age FROM user WHERE id=?";
            Map<String, Object> resultMap = queryRunner.query(conn, sql, new MapHandler(), 1);
            System.out.println(resultMap);

        } catch (Exception ex) {
            ex.printStackTrace();
        }
    
    }
}

运行结果:

{id=1, name=樂天, age=18}

源码分析:

我们看一下org.apache.commons.dbutils.handlers.MapHandler做了什么:

// 类org.apache.commons.dbutils.handlers.MapHandler
public class MapHandler implements ResultSetHandler<Map<String, Object>> {

    // .... 删去了若干代码

    @Override
    public Map<String, Object> handle(ResultSet rs) throws SQLException {
        return rs.next() ? this.convert.toMap(rs) : null;
    }

}

这里,this.convert默认值也是new BasicRowProcessor()。看一下toMap(...)函数:

// 类org.apache.commons.dbutils.BasicRowProcessor下的toMap(...)函数
@Override
public Map<String, Object> toMap(ResultSet rs) throws SQLException {
    Map<String, Object> result = new CaseInsensitiveHashMap();
    ResultSetMetaData rsmd = rs.getMetaData();
    int cols = rsmd.getColumnCount();

    for (int i = 1; i <= cols; i++) {
        String columnName = rsmd.getColumnLabel(i);
        if (null == columnName || 0 == columnName.length()) {
          columnName = rsmd.getColumnName(i);
        }
        result.put(columnName, rs.getObject(i));
    }

    return result;
}

CaseInsensitiveHashMap定义在类BasicRowProcessor内部:

private static class CaseInsensitiveHashMap extends LinkedHashMap<String, Object> {

    private final Map<String, String> lowerCaseMap = new HashMap<String, String>();

    private static final long serialVersionUID = -2848100435296897392L;

    @Override
    public boolean containsKey(Object key) {
        Object realKey = lowerCaseMap.get(key.toString().toLowerCase(Locale.ENGLISH));
        return super.containsKey(realKey);
    }

    @Override
    public Object get(Object key) {
        Object realKey = lowerCaseMap.get(key.toString().toLowerCase(Locale.ENGLISH));
        return super.get(realKey);
    }

    @Override
    public Object put(String key, Object value) {
        Object oldKey = lowerCaseMap.put(key.toLowerCase(Locale.ENGLISH), key);
        Object oldValue = super.remove(oldKey);
        super.put(key, value);
        return oldValue;
    }

    @Override
    public void putAll(Map<? extends String, ?> m) {
        for (Map.Entry<? extends String, ?> entry : m.entrySet()) {
            String key = entry.getKey();
            Object value = entry.getValue();
            this.put(key, value);
        }
    }


    @Override
    public Object remove(Object key) {
        Object realKey = lowerCaseMap.remove(key.toString().toLowerCase(Locale.ENGLISH));
        return super.remove(realKey);
    }
}

使用DBUtils获取多条数据

编写代码:

// 类hellodbutils.DBUtilsSelect2
package hellodbutils;

import java.util.Map;
import java.util.List;
import java.sql.*;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.*;

public class DBUtilsSelect2 {
    public static void main(String args[]) {
        Connection conn;
        QueryRunner queryRunner = new QueryRunner();
        try {
            Class.forName(DBConf.JDBC_DRIVER);
            conn = DriverManager.getConnection(DBConf.DB_URL, DBConf.USER, DBConf.PASS);
            
            String sql = "SELECT id, name, age FROM user";
            List<Map<String, Object>> result = queryRunner.query(conn, sql, new MapListHandler());
            System.out.println(result);

        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }   
}

运行结果:

[{id=1, name=樂天, age=18}, {id=2, name=樂天天, age=19}]

源码分析:

先看一下类org.apache.commons.dbutils.handlers.MapListHandler

// 类 org.apache.commons.dbutils.handlers.MapListHandler
public class MapListHandler extends AbstractListHandler<Map<String, Object>> {

    // .... 删去了若干代码

    public MapListHandler() {
        this(ArrayHandler.ROW_PROCESSOR);
    }

    // .... 删去了若干代码
    @Override
    protected Map<String, Object> handleRow(ResultSet rs) throws SQLException {
        return this.convert.toMap(rs);
    }

}

handle(...)方法在MapListHandler的父类org.apache.commons.dbutils.handlers.AbstractListHandler中:

public abstract class AbstractListHandler<T> implements ResultSetHandler<List<T>> {
    @Override
    public List<T> handle(ResultSet rs) throws SQLException {
        List<T> rows = new ArrayList<T>();
        while (rs.next()) {
            rows.add(this.handleRow(rs));
        }
        return rows;
    }

    protected abstract T handleRow(ResultSet rs) throws SQLException;
}

使用handleRow(...)处理ResultSet rs中当前指向的数据,并转换为Map;而handle()方法将获得ResultSet rs所能找到的所有数据对应的Map构成的ArrayList。


( 本文完 )