轻量级JDBC工具类
加载Driver
旧的代码采用Class.forName(driverClassName)加载,实际的Driver注册存在于每个Driver实现的静态初始化块
这里实现的是一种显式的加载,JDBC4以后可以自动加载,无需调用
DbUtils.java1 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); if (!Driver.class.isAssignableFrom(loadedClass)) { return false; }
@SuppressWarnings("unchecked") 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.java1 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.java1 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) { } }
|
关闭连接前还可以选择事务提交或者回滚
DbUtils.java1 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.java1 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---"); wrapper.setNullInt(-999); rs = ProxyFactory.instance().createResultSet(wrapper);
|
ResultSet增强–自动trim
如果采用定长存储,那么数据库会自动加空格
StringTrimmedResultSet同样采用代理,对字符串类型数据自动进行trim
StringTrimmedResultSet.java1 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);
|