DbUtils

轻量级JDBC工具类

加载Driver


旧的代码采用Class.forName(driverClassName)加载,实际的Driver注册存在于每个Driver实现的静态初始化块
这里实现的是一种显式的加载,JDBC4以后可以自动加载,无需调用

DbUtils.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public static boolean loadDriver(String driverClassName) {
return loadDriver(DbUtils.class.getClassLoader(), driverClassName);
}
public static boolean loadDriver(ClassLoader classLoader, String driverClassName) {
try {
Class<?> loadedClass = classLoader.loadClass(driverClassName);
//保证加载进来的确实是一个Driver
if (!Driver.class.isAssignableFrom(loadedClass)) {
return false;
}

@SuppressWarnings("unchecked") // guarded by previous check
Class<Driver> driverClass = (Class<Driver>) loadedClass;
Constructor<Driver> driverConstructor = driverClass.getConstructor();

//强制开放构造器访问级别
boolean isConstructorAccessible = driverConstructor.isAccessible();
if (!isConstructorAccessible) {
driverConstructor.setAccessible(true);
}

try {
//创建实例并注册
Driver driver = driverConstructor.newInstance();
DriverManager.registerDriver(new DriverProxy(driver));
} finally {
//恢复构造器访问级别
driverConstructor.setAccessible(isConstructorAccessible);
}

return true;
} catch (RuntimeException e) {
return false;
} catch (Exception e) {
return false;
}
}

关闭资源


关闭全套,注意顺序,先关ResultSet,再关Statement,最后关Connection

DbUtils.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void closeQuietly(Connection conn, Statement stmt,
ResultSet rs) {

try {
closeQuietly(rs);
} finally {
try {
closeQuietly(stmt);
} finally {
closeQuietly(conn);
}
}

}

静默的关闭就是外包一层try-catch,忽略任何Exception

DbUtils.java
1
2
3
4
5
6
7
8
9
10
11
12
public static void close(Connection conn) throws SQLException {
if (conn != null) {
conn.close();
}
}
public static void closeQuietly(Connection conn) {
try {
close(conn);
} catch (SQLException e) { // NOPMD
// quiet
}
}

关闭连接前还可以选择事务提交或者回滚

DbUtils.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static void rollbackAndClose(Connection conn) throws SQLException {
if (conn != null) {
try {
conn.rollback();
} finally {
conn.close();
}
}
}
public static void commitAndClose(Connection conn) throws SQLException {
if (conn != null) {
try {
conn.commit();
} finally {
conn.close();
}
}
}

ResultSet增强–Null处理功能


直接通过ResultSet获取值并不能区分null值
比如rs.getInt(1),如果数据库里存了null,会返回int默认值0,这样就不知道数据库原本存了0还是null
如果要判断的话需要调用rs.wasNull(),返回上次读取是否是null值

SqlNullCheckedResultSet可以替换每个get方法的默认值成指定值
采用Java代理实现,内置各种类型的nullMethod,rs.wasNull()判断遇到null就返回相应方法进行处理

SqlNullCheckedResultSet.java
1
2
3
4
5
6
7
8
9
10
11
12
13
public class SqlNullCheckedResultSet implements InvocationHandler {
//...
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

Object result = method.invoke(this.rs, args);

Method nullMethod = nullMethods.get(method.getName());

return (nullMethod != null && this.rs.wasNull())
? nullMethod.invoke(this, (Object[]) null) : result;
}
}

调用方式,包装后进行配置,创建代理对象

1
2
3
4
SqlNullCheckedResultSet wrapper = new SqlNullCheckedResultSet(rs);
wrapper.setNullString("---N/A---"); // Set null string
wrapper.setNullInt(-999); // Set null integer
rs = ProxyFactory.instance().createResultSet(wrapper);

ResultSet增强–自动trim


如果采用定长存储,那么数据库会自动加空格
StringTrimmedResultSet同样采用代理,对字符串类型数据自动进行trim

StringTrimmedResultSet.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class StringTrimmedResultSet implements InvocationHandler {
private static final ProxyFactory factory = ProxyFactory.instance();

public static ResultSet wrap(ResultSet rs) {
return factory.createResultSet(new StringTrimmedResultSet(rs));
}

private final ResultSet rs;

public StringTrimmedResultSet(ResultSet rs) {
super();
this.rs = rs;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {

Object result = method.invoke(this.rs, args);

if ((method.getName().equals("getObject")
|| method.getName().equals("getString"))
&& result instanceof String) {
result = ((String) result).trim();
}
return result;
}

}

因为无需配置,调用方式比较简单,直接包装就好

1
rs = StringTrimmedResultSet.wrap(rs);