DbUtils入门#

Apache出品的极为轻量级的Jdbc访问框架,核心类只有两个:QueryRunner和ResultSetHandler。

各类ResultSetHandler:

ArrayHandler:把结果集中的第一行数据转成对象数组。

ArrayListHandler:把结果集中的每一行数据都转成一个数组,再存放到List中。

BeanListHandler:将结果集中的每一行数据都封装到一个对应的JavaBean实例中,存放到List里。

请参考孤傲苍狼的博客javaweb学习总结(四十一)——Apache的DBUtils框架学习,墙裂推荐。

Detail#

测试样例代码##

package org.wit.ff.jdbc;

import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;
import org.wit.ff.jdbc.access.IDataAccessor;
import org.wit.ff.jdbc.converter.ParamsConverter;
import org.wit.ff.jdbc.sql.db.MySQLBuilder; import java.util.ArrayList;
import java.util.Date;
import java.util.List; /**
* Created by F.Fang on 2015/3/31.
* Version :2015/3/31
*/
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class CRUDTest extends AbstractJUnit4SpringContextTests{ @Autowired
private IDataAccessor dataAccessor; @Test
public void query(){
MySQLBuilder builder = new MySQLBuilder();
// sql: select * from Audience limit 2
builder.SELECT("*").FROM("Audience").PAGE(0, 2);
String sql = builder.toString();
final Audience audience = new Audience();
audience.setName("ff"); // 也可以直接填写SQL.
List<Audience> list = dataAccessor.query(sql, null, Audience.class);
System.out.println(list);
} @Test
public void insert(){
// 批量新增.
String sql = "insert into audience(id,name,pay) values(?,?,?)";
Object[][] params = new Object[1][];
params[0] = new Object[]{100,"ff",100.1};
dataAccessor.insert(sql, params);
} @Test
public void insertDate(){
// 试试日期.
String sql = "insert into test_date(id,current) values(?,?)";
Object[][] params = new Object[1][];
params[0] = new Object[]{100, new Date()};
dataAccessor.insert(sql, params);
} @Test
public void insertWithParamsConverter(){
// 试试PramsConverter好用不.
ParamsConverter<Audience> converter = new ParamsConverter<Audience>() {
@Override
public Object[] convert(Audience audience) {
return new Object[]{audience.getId(), audience.getName(), audience.getPay()};
}
}; String sql = "insert into audience(id,name,pay) values(?,?,?)";
// 构造数据.
List<Audience> list = new ArrayList<>();
Audience audience1 = new Audience();
audience1.setId(250);
audience1.setName("ff");
audience1.setPay(1000.00); Audience audience2 = new Audience();
audience2.setId(251);
audience2.setName("ff1");
audience2.setPay(1000.00); list.add(audience1);
list.add(audience2); dataAccessor.insert(sql, list, Audience.class, converter); } }

Audience

package org.wit.ff.jdbc;

import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle; /**
* Created by F.Fang on 2015/2/16.
* Version :2015/2/16
*/
public class Audience {
private Integer id; private String name; private Double pay; public Integer getId() {
return id;
} public void setId(Integer id) {
this.id = id;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public Double getPay() {
return pay;
} public void setPay(Double pay) {
this.pay = pay;
} @Override
public String toString() {
return ReflectionToStringBuilder.toString(this, ToStringStyle.SIMPLE_STYLE);
}
}

配置

<!-- 数据库连接池 -->
<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${db.driverClass}"/>
<property name="url" value="${db.jdbcUrl}"/>
<property name="username" value="${db.user}"/>
<property name="password" value="${db.password}"/>
</bean> <bean id="dataAccessor" class="org.wit.ff.jdbc.access.dbutils.DefaultDataAccessor">
<property name="dataSource" ref="dataSource"/>
</bean>

备注:实际数据源配置还是需要优化的

看上去还凑合,调用比较简单,那么接下来看看具体的实现吧!

AbstractDataAccessor##

  • QueryRunner无参构造对象
  • 抽象获取连接和关闭连接的方法
package org.wit.ff.jdbc.access.dbutils;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.*;
import org.apache.commons.lang3.StringUtils;
import org.wit.ff.jdbc.converter.ParamsConverter;
import org.wit.ff.jdbc.access.IDataAccessor;
import org.wit.ff.jdbc.exception.DbUtilsDataAccessException;
import org.wit.ff.jdbc.id.IdGenerator; import java.sql.Connection;
import java.sql.SQLException;
import java.util.List; /**
* Created by F.Fang on 2015/3/31.
* Version :2015/3/31
*/
public abstract class AbstractDataAccessor implements IDataAccessor { /**
* 获取连接的方法时抽象的,目的是为了和事务绑定,开放Connection来源.
* @return
* @throws SQLException
*/
protected abstract Connection getConnection() throws SQLException; /**
* 获取连接的方法时抽象的,目的是为了和事务绑定,因为事务执行前后包含的sql逻辑可能不只一条, 不可在一条sql执行逻辑完成后关闭.
* @param conn
*/
protected abstract void closeConn(Connection conn); @Override
public <T> List<T> query(String sql, Class<T> resultType){
return query(sql, null, resultType);
} /**
* 查询时BeanListHandler将结果集转换成对象列表.
* @param sql 查询语句
* @param params 查询参数
* @param resultType 返回类型
* @param <T>
* @return
*/
@Override
public <T> List<T> query(String sql, Object[] params, Class<T> resultType) {
if (resultType == null || StringUtils.isBlank(sql)) {
throw new DbUtilsDataAccessException("resultType can't be null and sql can't be blank!");
}
QueryRunner runner = new QueryRunner();
Connection conn = null;
List<T> result = null;
try {
conn = getConnection();
if (params != null) {
result = (List<T>) runner.query(conn, sql, new BeanListHandler(resultType), params);
} else {
result = (List<T>) runner.query(conn, sql, new BeanListHandler(resultType));
}
} catch (Exception e) {
throw new DbUtilsDataAccessException("query error, sql is:" + sql, e);
} finally {
closeConn(conn);
}
return result;
} /**
*
* @param sql insert语句.
* @param params 对象参数列表.
* @param paramsType 参数类型.
* @param converter 参数转换器.
* @param <T>
*/
@Override
public <T> void insert(String sql, List<T> params, Class<T> paramsType, ParamsConverter<T> converter) {
if (StringUtils.isBlank(sql)) {
throw new DbUtilsDataAccessException("sql can't be blank!");
}
if (params == null || params.isEmpty()) {
throw new DbUtilsDataAccessException("params can't be null or empty!");
}
if (paramsType == null || converter == null) {
throw new DbUtilsDataAccessException("paramsType or converter can't be null!");
} QueryRunner runner = new QueryRunner();
Connection conn = null;
ArrayListHandler handler = new ArrayListHandler();
List<Object[]> keys = null;
try {
conn = getConnection();
int len = params.size();
Object[][] arr = new Object[len][];
for (int i = 0; i < len; ++i) {
// 将单个对象处理成单行记录,用数组表示.
arr[i] = converter.convert(params.get(i));
}
// 获取主键.
keys = (List<Object[]>) runner.insertBatch(conn, sql, handler, arr);
if (keys != null && keys.size() == len) {
// implements IdGenerator.
// 如果当前对象实现了IdGenerator接口, 则执行主键赋值的逻辑, 赋值逻辑由用户自定义在具体的对象类型当中
if (params.get(0) instanceof IdGenerator) {
for (int i = 0; i < len; ++i) {
// 对每一个对象执行主键赋值(解析)
((IdGenerator) params.get(i)).parseGenKey(keys.get(i));
}
}
}
} catch (Exception e) {
throw new DbUtilsDataAccessException("batch insert error, sql is:" + sql, e);
}finally {
closeConn(conn);
}
} /**
* 批量插入.
* @param sql
* @param params
*/
@Override
public void insert(String sql, Object[][] params) {
if(params==null || StringUtils.isBlank(sql)){
throw new DbUtilsDataAccessException("params can't be null and sql can't be empty!");
}
QueryRunner runner = new QueryRunner();
Connection conn = null;
ArrayListHandler handler = new ArrayListHandler();
try {
conn = getConnection();
runner.insertBatch(conn, sql, handler, params);
} catch (Exception e) {
throw new DbUtilsDataAccessException("batch insert error, sql is:" + sql, e);
}finally {
closeConn(conn);
}
} /**
* 单条插入.
* @param sql
* @param params
*/
@Override
public void insert(String sql, Object[] params) {
if(params==null || StringUtils.isBlank(sql)){
throw new DbUtilsDataAccessException("params can't be null and sql can't be empty!");
}
QueryRunner runner = new QueryRunner();
Connection conn = null;
ArrayHandler handler = new ArrayHandler();
try {
conn = getConnection();
runner.insert(conn, sql, handler, params);
} catch (Exception e) {
throw new DbUtilsDataAccessException("insert error, sql is:" + sql, e);
}finally {
closeConn(conn);
}
} /**
* 批量更新.
* @param sql
* @param params
* @return
*/
@Override
public int[] update(String sql, Object[][] params) {
if(params==null || StringUtils.isBlank(sql)){
throw new DbUtilsDataAccessException("params can't be null and sql can't be empty!");
}
QueryRunner runner = new QueryRunner();
Connection conn = null;
int[] result = null;
try {
conn = getConnection();
result = runner.batch(conn, sql, params);
} catch (Exception e) {
throw new DbUtilsDataAccessException("batch update error, sql is:" + sql, e);
}finally {
closeConn(conn);
}
return result;
} /**
* 单条更新.
* @param sql
* @param params
* @return
*/
@Override
public int update(String sql, Object[] params) {
QueryRunner runner = new QueryRunner();
Connection conn = null;
int result = 0;
try {
conn = getConnection();
if (params != null) {
result = runner.update(conn, sql, params);
} else {
result = runner.update(conn, sql);
}
} catch (Exception e) {
throw new DbUtilsDataAccessException("update error, sql is:" + sql, e);
}finally {
closeConn(conn);
}
return result;
} /**
* 删除,没有必要批量.
* 即使是批量,也可以调用批量更新的方法.
* 事实上此方法并无太大必要,只是为了避免歧义而已.
* @param sql
* @param params
* @return
*/
@Override
public int delete(String sql, Object[] params) {
return update(sql, params);
} }

DefaultDataAccessor##

package org.wit.ff.jdbc.access.dbutils;

import org.apache.commons.dbutils.DbUtils;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException; /**
* Created by F.Fang on 2015/3/31.
* 默认以配置数据源的方式获取连接.
* Version :2015/3/31
*/
public class DefaultDataAccessor extends AbstractDataAccessor { private DataSource dataSource; protected Connection getConnection() throws SQLException{
return dataSource.getConnection();
} @Override
protected void closeConn(Connection conn) {
try {
DbUtils.close(conn);
} catch (SQLException e) {
// do nothing!
}
} public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
}

DefaultTransactionDataAccessor##

package org.wit.ff.jdbc.access.dbutils;

import org.springframework.jdbc.datasource.DataSourceUtils;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException; /**
* Created by F.Fang on 2015/4/23.
* Version :2015/4/23
*/
public class DefaultTransactionDataAccessor extends AbstractDataAccessor { private DataSource dataSource; protected Connection getConnection() throws SQLException { return DataSourceUtils.getConnection(dataSource);
// 虽然可以将连接绑定到事务,但是当外部循dataAccessor的方法时,循环调用时会产生多个连接.
//return DataSourceUtils.getConnection(dataSource);
// 以下写法无法解决事务问题,无法将连接绑定到Spring 事务.
// return dataSource.getConnection();
} @Override
protected void closeConn(Connection conn) {
// do nothing!
} public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
}

采用Spring事务集成方案,将Connection交给Spring管理,实际上Spring将Connection绑定到当前执行线程当中(以ConnectionHolder包装Connection,ThreadLocal包装ConnectionHolder绑定线程)

详情参考DataSourceTransactionMananger的源代码,最好在doBeign和doCleanupAfterCompletion执行断点调试

备注:AbstractDataAccessor开放getConnection()和closeConnection方法的原因也是调用过程Connection的开启和关闭不能由自身维持,否则会导致事务处理时Spring持有的Connection和AbstractDataAccessor执行方法的Connection不一致或在执行时拿到了关闭了的Connection对象

QA#

轻量级封装DbUtils&Mybatis之二Dbutils的更多相关文章

  1. 轻量级封装DbUtils&Mybatis之三MyBatis分页

    MyBatis假分页 参考DefaultResultSetHandler的skipRows方法. 温馨提示:部分代码请参考轻量级封装DbUtils&Mybatis之一概要 解决方案 1)之前公 ...

  2. pache—DBUtils框架简介、DbUtils类、QueryRunner类 、ResultSetHandler接口

    Apache—DBUtils框架简介.DbUtils类.QueryRunner类 .ResultSetHandler接口 commons-dbutils 是 Apache 组织提供的一个开源 JDBC ...

  3. MyBatis系列二 之 数据库列名于程序实体类中字段名称不一致

    MyBatis系列二  之   数据库列名于程序实体类中字段名称不一致 情景:当数据库中的列名与我们程序实体类中的字段名称不一致         使用ResultMap节点配置信息  在映射文件中  ...

  4. MyBatis笔记二:配置

    MyBatis笔记二:配置 1.全局配置 1.properites 这个配置主要是引入我们的 properites 配置文件的: <properties resource="db.pr ...

  5. 轻量级封装DbUtils&Mybatis之一概要

    Why 一时兴起,自以为是的对Jdbc访问框架做了一个简单的摸底,近期主要采用Mybatis,之前也有不少采用Dbutils,因此希望能让这两个框架折腾的更好用. DbUtils:非常简单的Jdbc访 ...

  6. 轻量级封装DbUtils&Mybatis之四MyBatis主键

    MyBatis主键 不支持对象列表存储时对自增id字段的赋值(至少包括3.2.6和3.3.0版本),如果id不是采用底层DB自增主键赋值,不必考虑此问题 温馨提示:分布式DB环境下,DB主键一般会采用 ...

  7. Apache DbUtils - JDBC轻量级封装的工具包

    前段时间使用了Apache Common DbUtils这个工具,在此留个印,以备不时查看.大家都知道现在市面上的数据库访问层的框架很多,当然很多都是包含了OR-Mapping工作步骤的例如大家常用的 ...

  8. Java学习笔记49(DBUtils工具类二)

    上一篇文章是我们自己模拟的DBUtils工具类,其实有开发好的工具类 这里使用commons-dbutils-1.6.jar 事务的简单介绍: 在数据库中应用事务处理案例:转账案例 张三和李四都有有自 ...

  9. Java框架之Mybatis(二)

    本文主要介绍 Mybatis(一)之后剩下的内容: 1 mybatis 中 log4j的配置 2 dao层的开发(使用mapper代理的方式) 3 mybatis的配置详解 4 输入输出映射对应的类型 ...

随机推荐

  1. Linux 系统启动过程,Linux 系统目录结构

    一.Linux 系统启动过程 linux启动时我们会看到许多启动信息. Linux系统的启动过程并不是大家想象中的那么复杂,其过程可以分为5个阶段: 内核的引导. 运行 init. 系统初始化. 建立 ...

  2. 002——vue小结

    1.new 一个vue对象的时候你可以设置他的属性,其中最重要的包括三个,分别是:data,methods,watch. 2.其中data代表vue对象的数据,methods代表vue对象的方法,wa ...

  3. linux FTP 操作

    1.登陆: ftp 172.xxx.xxx.xxx 按提示输入用户名和密码 2.上传: 单个文件:put /路径/文件名 批量: 输入 prom 此命令是关闭交互(否则总是询问你是否要上传) 输入下载 ...

  4. jqeury 基础

    jquery 选择器: 基本选择器:#id ..class.*(匹配所有) 层次选择器: $(div span) 选取<div>里的所有的<span>元素. $(div> ...

  5. FMDB给表添加新的字段

    1.首先判断添加的字段是否存在,如果不存在就添加. 2.代码演示: (1)判断是否存在,判断之前先导入头文件确保可以调用FMDB的api(#import “FMDatabaseAdditions.h” ...

  6. iOS使用Instruments的工具

    使用Instruments的工具 iOSXcodeInstrumentsInstruments是一个官方提供的强大的性能调试工具集. 1.Blank(空模板):创建一个空的模板,可以从Library库 ...

  7. Python面向对象 --- 类的设计和常见的内置方法

    面向对象:一种基于面向过程的新的编程思想.也就是说面向对象是将功能等通过对象来实现,将功能封装进对象之中,让对象去实现具体的细节:这种思想是将数据作为第一位,而方法或者说是算法作为其次,这是对数据一种 ...

  8. Linux:col命令详解

    col 经常用于将说明文件转存为纯文本以方便阅读 语法 col(选项) 选项 -b:过滤掉所有的控制字符,包括RLF和HRLF: -f:滤掉RLF字符,但允许将HRLF字符呈现出来: -x:以多个空格 ...

  9. 新浪云git 上传 nodejs项目

    1 .新建一个空文件夹: 2.在当前文件夹下,初始化本地git: 3.将要上传的nodejs工程,拷贝到这里: 这一步很容易出问题,所以最好不要拷贝别人给的node_modules文件,尽量做到现用现 ...

  10. IOS开发 arc与非Arc代码的区别

    是属于ios开发中的内存管理问题:在这我简要概述一下,详细讲的话内容挺多,而且是作为一个ios开发人员,或ios开发爱好者,这是必须了解的:Objective-c中提供了两种内存管理机制MRC(Man ...