(1)No possibility for resource leaks. Correct JDBC coding isn't difficult but it is time-consuming and tedious. This often leads to connection leaks that may be difficult to track down.
(2)Cleaner, clearer persistence code. The amount of code needed to persist data in a database is drastically reduced. The remaining code clearly expresses your intention without being cluttered with resource cleanup.
(3)Automatically populate JavaBean properties from ResultSets. You don't need to manually copy column values into bean instances by calling setter methods. Each row of the ResultSet can be represented by one fully populated bean instance.
(1)An Object/Relational bridge - there are plenty of good O/R tools already. DbUtils is for developers looking to use JDBC without all the mundane pieces.
(2)A Data Access Object (DAO) framework - DbUtils can be used to build a DAO framework though.
(3)An object oriented abstraction of general database objects like a Table, Column, or PrimaryKey.
(4)A heavyweight framework of any kind - the goal here is to be a straightforward and easy to use JDBC helper library.
DbUtils is intentionally a single jar distribution and relies only on a standard Java 1.6 or later JRE.
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
org.
apache.
commons.
dbutils;
import
java.
beans.
IntrospectionException;
import
java.
beans.
Introspector;
import
java.
beans.
PropertyDescriptor;
import
java.
lang.
reflect.
InvocationTargetException;
import
java.
lang.
reflect.
Method;
import
java.
sql.
Connection;
import
java.
sql.
ParameterMetaData;
import
java.
sql.
PreparedStatement;
import
java.
sql.
ResultSet;
import
java.
sql.
SQLException;
import
java.
sql.
Statement;
import
java.
sql.
Types;
import
java.
util.
Arrays;
import
javax.
sql.
DataSource;
/**
* The base class for QueryRunner & AsyncQueryRunner. This class is thread safe.
* QueryRunner & AsyncQueryRunner的基础类:准备PreparedStatement、Connection、
* 填充PreparedStatement中的“?”占位符
* 关闭Connection、Statement、ResultSet
*
@since
1.4 (mostly extracted from QueryRunner)
*/
public
abstract
class
AbstractQueryRunner {
/**
* Is {
@link
ParameterMetaData#getParameterType(int)} broken (have we tried
* it yet)?
* getParameterType(int)不可用吗?
*/
private
volatile
boolean
pmdKnownBroken
=
false;
/**
* The DataSource to retrieve connections from.
* 数据源,我们可以从此数据源中获得数据库连接。在使用dbutils时,要使用数据源。常用dbcp和c3p0
*
@deprecated
Access to this field should be through {
@link
#getDataSource()}.
* 这里提示,要访问数据源,建议调用getDataSource()方法
*/
@
Deprecated
protected
final
DataSource
ds;
/**
* Default constructor, sets pmdKnownBroken to false and ds to null.
* 默认构造函数中,设置数据源为null
*/
public
AbstractQueryRunner() {
ds
=
null;
}
/**
* Constructor to control the use of <code>ParameterMetaData</code>.
* 此构造器中控制了ParameterMetaData的使用,在使用中,个人比较少使用这种方式
*
@param
pmdKnownBroken
* Some drivers don't support
* {
@link
ParameterMetaData#getParameterType(int) }; if
* <code>pmdKnownBroken</code> is set to true, we won't even try
* it; if false, we'll try it, and if it breaks, we'll remember
* not to use it again.
* 有一些驱动不支持getParameterType(int)。如果pmdKnownBroken设置为true,我们不会去尝试
* ,如果为false,我们会尝试调用。如果不行,我们会记住不要再去调用它。
*/
public
AbstractQueryRunner(
boolean
pmdKnownBroken) {
this.
pmdKnownBroken
=
pmdKnownBroken;
//这里也设置了数据源为null
ds
=
null;
}
/**
* Constructor to provide a <code>DataSource</code>. Methods that do not
* take a <code>Connection</code> parameter will retrieve connections from
* this <code>DataSource</code>.
* 使用此构造器,可以为数据源赋值。没有Connection参数的方法将从此数据源中获得数据库连接。
* 在使用dbutils时,经常使用这个构造器
*
*
@param
ds
* The <code>DataSource</code> to retrieve connections from.
*/
public
AbstractQueryRunner(
DataSource
ds) {
this.
ds
=
ds;
}
/**
* Constructor to provide a <code>DataSource</code> and control the use of
* <code>ParameterMetaData</code>. Methods that do not take a
* <code>Connection</code> parameter will retrieve connections from this
* <code>DataSource</code>.
* 这个就是上述两个的综合了,不多讲了
*
@param
ds
* The <code>DataSource</code> to retrieve connections from.
*
@param
pmdKnownBroken
* Some drivers don't support
* {
@link
ParameterMetaData#getParameterType(int) }; if
* <code>pmdKnownBroken</code> is set to true, we won't even try
* it; if false, we'll try it, and if it breaks, we'll remember
* not to use it again.
*/
public
AbstractQueryRunner(
DataSource
ds,
boolean
pmdKnownBroken) {
this.
pmdKnownBroken
=
pmdKnownBroken;
this.
ds
=
ds;
}
/**
* Returns the <code>DataSource</code> this runner is using.
* <code>QueryRunner</code> methods always call this method to get the
* <code>DataSource</code> so subclasses can provide specialized behavior.
* 这个是数据源属性的getter方法,上面建议访问数据源时,通过此方法进行访问
*
@return
DataSource the runner is using
*/
public
DataSource
getDataSource() {
return
this.
ds;
}
/**
* Some drivers don't support
* {
@link
ParameterMetaData#getParameterType(int) }; if
* <code>pmdKnownBroken</code> is set to true, we won't even try it; if
* false, we'll try it, and if it breaks, we'll remember not to use it
* again.
* 这个是pmdKnownBroken属性的getter方法,由于是boolean,可以使用isPmdKnownBroken
*
@return
the flag to skip (or not)
* {
@link
ParameterMetaData#getParameterType(int) }
*/
public
boolean
isPmdKnownBroken() {
return
pmdKnownBroken;
}
/**
* Factory method that creates and initializes a
* <code>PreparedStatement</code> object for the given SQL.
* <code>QueryRunner</code> methods always call this method to prepare
* statements for them. Subclasses can override this method to provide
* special PreparedStatement configuration if needed. This implementation
* simply calls <code>conn.prepareStatement(sql)</code>.
* 工厂方法,为给定的SQL创建和实例化一个PreparedStatement对象。QueryRunner(AbstractQueryRunner的
* 子类)的方法总会调用此方法来准备statements。需要的话,子类可以覆盖此方法来提供特殊的PreparedStatement配置。
* 这里只是简单地调用了conn.prepareStatement(sql)。这是常见的JDBC,很容易明白。
*
@param
conn
* The <code>Connection</code> used to create the
* <code>PreparedStatement</code>
*
@param
sql
* The SQL statement to prepare.
*
@return
An initialized <code>PreparedStatement</code>.
* if a database access error occurs
*/
protected
PreparedStatement
prepareStatement(
Connection
conn,
String
sql)
throws
SQLException {
return
conn.
prepareStatement(
sql);
}
/**
* Factory method that creates and initializes a <code>Connection</code>
* object. <code>QueryRunner</code> methods always call this method to
* retrieve connections from its DataSource. Subclasses can override this
* method to provide special <code>Connection</code> configuration if
* needed. This implementation simply calls <code>ds.getConnection()</code>.
*
* 工厂方法,创建和实例化一个Connection对象。QueryRunner中的方法总会调用此方法从数据源中获得数据库
* 连接。需要的话,子类可以覆写此方法来提供特殊的连接配置。这里只是简单地调用了数据源的getConnection()
*
@return
An initialized <code>Connection</code>.
* if a database access error occurs
*/
protected
Connection
prepareConnection()
throws
SQLException {
if (
this.
getDataSource()
==
null) {
throw
new
SQLException(
"QueryRunner requires a DataSource to be "
+
"invoked in this way, or a Connection should be passed in");
}
return
this.
getDataSource().
getConnection();
}
/**
* Fill the <code>PreparedStatement</code> replacement parameters with the
* given objects.
* 使用给定的对象,填充PreparedStatement中的参数
*
@param
stmt
* PreparedStatement to fill
*
@param
params
* Query replacement parameters; <code>null</code> is a valid
* value to pass in.
* if a database access error occurs
*/
public
void
fillStatement(
PreparedStatement
stmt,
Object...
params)
throws
SQLException {
// check the parameter count, if we can
ParameterMetaData
pmd
=
null;
if (
!
pmdKnownBroken) {
//pmdKnownBroken初始化值是false
pmd
=
stmt.
getParameterMetaData();
//获得元数据
int
stmtCount
=
pmd.
getParameterCount();
//获得占位符的个数
int
paramsCount
=
params
==
null ?
0 :
params.
length;
//给定参数的个数
if (
stmtCount
!=
paramsCount) {
//如果占位符和给定参数的个数不一致,报错
throw
new
SQLException(
"Wrong number of parameters: expected "
+
stmtCount
+
", was given "
+
paramsCount);
}
}
// nothing to do here
if (
params
==
null) {
//给定参数为null,什么也不作
return;
}
// TODO else有些不明白
for (
int
i
=
0;
i
<
params.
length;
i
++) {
if (
params[
i]
!=
null) {
stmt.
setObject(
i
+
1,
params[
i]);
}
else {
// VARCHAR works with many drivers regardless
// of the actual column type. Oddly, NULL and
// OTHER don't work with Oracle's drivers.
int
sqlType
=
Types.
VARCHAR;
if (
!
pmdKnownBroken) {
try {
/*
* It's not possible for pmdKnownBroken to change from
* true to false, (once true, always true) so pmd cannot
* be null here.
*/
sqlType
=
pmd.
getParameterType(
i
+
1);
}
catch (
SQLException
e) {
pmdKnownBroken
=
true;
}
}
stmt.
setNull(
i
+
1,
sqlType);
}
}
}
/**
* Fill the <code>PreparedStatement</code> replacement parameters with the
* given object's bean property values.
*
*
@param
stmt
* PreparedStatement to fill
*
@param
bean
* a JavaBean object
*
@param
properties
* an ordered array of properties; this gives the order to insert
* values in the statement
* if a database access error occurs
*/
public
void
fillStatementWithBean(
PreparedStatement
stmt,
Object
bean,
PropertyDescriptor[]
properties)
throws
SQLException {
//定义一个Object属性,存放代替SQL中占位符的参数
Object[]
params
=
new
Object[
properties.
length];
//循环属性描述符数组,属性描述符要与SQL中占位符一一对应
for (
int
i
=
0;
i
<
properties.
length;
i
++) {
PropertyDescriptor
property
=
properties[
i];
//获得第i个属性
Object
value
=
null;
//获得第i个属性的getter方法
Method
method
=
property.
getReadMethod();
//如果不存在getter方法,就报错
if (
method
==
null) {
throw
new
RuntimeException(
"No read method for bean property "
+
bean.
getClass()
+
" "
+
property.
getName());
}
try {
//调用getter方法,获得此属性的值,因为是get方法,不用参数
value
=
method.
invoke(
bean,
new
Object[
0]);
}
catch (
InvocationTargetException
e) {
throw
new
RuntimeException(
"Couldn't invoke method: "
+
method,
e);
}
catch (
IllegalArgumentException
e) {
throw
new
RuntimeException(
"Couldn't invoke method with 0 arguments: "
+
method,
e);
}
catch (
IllegalAccessException
e) {
throw
new
RuntimeException(
"Couldn't invoke method: "
+
method,
e);
}
//获得了属性值后就存放到Object数组中
params[
i]
=
value;
}
//最后,调用fillStatement来填充SQL中的占位符
fillStatement(
stmt,
params);
}
/**
* Fill the <code>PreparedStatement</code> replacement parameters with the
* given object's bean property values.
* 此方法跟fillStatementWithBean(PreparedStatement stmt, Object bean,
* PropertyDescriptor[] properties)类似,一个是用了属性描述符,这里是直接
* 使用了属性名。
* 在方法内部将属性变成属性描述符,然后即可。
*
*
@param
stmt
* PreparedStatement to fill
*
@param
bean
* A JavaBean object
*
@param
propertyNames
* An ordered array of property names (these should match the
* getters/setters); this gives the order to insert values in the
* statement
* If a database access error occurs
*/
public
void
fillStatementWithBean(
PreparedStatement
stmt,
Object
bean,
String...
propertyNames)
throws
SQLException {
PropertyDescriptor[]
descriptors;
try {
descriptors
=
Introspector.
getBeanInfo(
bean.
getClass())
.
getPropertyDescriptors();
}
catch (
IntrospectionException
e) {
throw
new
RuntimeException(
"Couldn't introspect bean "
+
bean.
getClass().
toString(),
e);
}
PropertyDescriptor[]
sorted
=
new
PropertyDescriptor[
propertyNames.
length];
for (
int
i
=
0;
i
<
propertyNames.
length;
i
++) {
String
propertyName
=
propertyNames[
i];
if (
propertyName
==
null) {
throw
new
NullPointerException(
"propertyName can't be null: "
+
i);
}
boolean
found
=
false;
for (
int
j
=
0;
j
<
descriptors.
length;
j
++) {
PropertyDescriptor
descriptor
=
descriptors[
j];
if (
propertyName.
equals(
descriptor.
getName())) {
sorted[
i]
=
descriptor;
found
=
true;
break;
}
}
if (
!
found) {
throw
new
RuntimeException(
"Couldn't find bean property: "
+
bean.
getClass()
+
" "
+
propertyName);
}
}
fillStatementWithBean(
stmt,
bean,
sorted);
}
/**
* Throws a new exception with a more informative error message.
*
*
@param
cause
* The original exception that will be chained to the new
* exception when it's rethrown.
*
*
@param
sql
* The query that was executing when the exception happened.
*
*
@param
params
* The query replacement parameters; <code>null</code> is a valid
* value to pass in.
*
* if a database access error occurs
*/
protected
void
rethrow(
SQLException
cause,
String
sql,
Object...
params)
throws
SQLException {
String
causeMessage
=
cause.
getMessage();
if (
causeMessage
==
null) {
causeMessage
=
"";
}
StringBuffer
msg
=
new
StringBuffer(
causeMessage);
msg.
append(
" Query: ");
msg.
append(
sql);
msg.
append(
" Parameters: ");
if (
params
==
null) {
msg.
append(
"[]");
}
else {
msg.
append(
Arrays.
deepToString(
params));
}
SQLException
e
=
new
SQLException(
msg.
toString(),
cause.
getSQLState(),
cause.
getErrorCode());
e.
setNextException(
cause);
throw
e;
}
/**
* Wrap the <code>ResultSet</code> in a decorator before processing it. This
* implementation returns the <code>ResultSet</code> it is given without any
* decoration.
*
* <p>
* Often, the implementation of this method can be done in an anonymous
* inner class like this:
* </p>
*
* <pre>
* QueryRunner run = new QueryRunner() {
* protected ResultSet wrap(ResultSet rs) {
* return StringTrimmedResultSet.wrap(rs);
* }
* };
* </pre>
*
*
@param
rs
* The <code>ResultSet</code> to decorate; never
* <code>null</code>.
*
@return
The <code>ResultSet</code> wrapped in some decorator.
*/
protected
ResultSet
wrap(
ResultSet
rs) {
//包装结果集,这里没有进行任何的包装
return
rs;
}
/**
* Close a <code>Connection</code>. This implementation avoids closing if
* null and does <strong>not</strong> suppress any exceptions. Subclasses
* can override to provide special handling like logging.
* 关闭Connection
*
@param
conn
* Connection to close
* if a database access error occurs
*/
protected
void
close(
Connection
conn)
throws
SQLException {
DbUtils.
close(
conn);
}
/**
* Close a <code>Statement</code>. This implementation avoids closing if
* null and does <strong>not</strong> suppress any exceptions. Subclasses
* can override to provide special handling like logging.
* 关闭Statement
*
@param
stmt
* Statement to close
* if a database access error occurs
*/
protected
void
close(
Statement
stmt)
throws
SQLException {
DbUtils.
close(
stmt);
}
/**
* Close a <code>ResultSet</code>. This implementation avoids closing if
* null and does <strong>not</strong> suppress any exceptions. Subclasses
* can override to provide special handling like logging.
* 关闭ResultSet
*
@param
rs
* ResultSet to close
* if a database access error occurs
*/
protected
void
close(
ResultSet
rs)
throws
SQLException {
DbUtils.
close(
rs);
}
}