1 DbUtils
1.1 DbUtils类
DbUtils类是一个final类,提供了关于JDBC操作的静态方法,如关闭数据库资源,加载数据库驱动,事务操作和打印信息。工具类是不应该有public或default构造方法,在DbUtils中却提供了:
public DbUtils() {}
说是为了向老版本兼容。
1.2 数据库资源操作
1.2.1 关闭数据库资源的操作分为两种:一种向上抛出异常,另一种是捕获异常后不做处理。以Connection关闭为例:
public static void close(Connection conn) throws SQLException {
if (conn != null) { // 先判断是否为null,防止抛出空指针异常。
conn.close();
}
}
public static void closeQuietly(Connection conn) {
try {
close(conn); // 调用会抛异常的方法
} catch (SQLException e) {
// 不对异常进行处理。
}
}
1.2.2 一起关闭Connection、Statement和ResultSet资源
public static void closeQuietly(Connection conn, Statement stmt, ResultSet rs) {
// try-finally结构,依次关闭rs, stmt, conn。
try {
closeQuietly(rs);
} finally {
try {
closeQuietly(stmt);
} finally {
closeQuietly(conn);
}
}
}
代码中使用到了嵌套式的try-finally语句块,保证了Connection等资源将能够得到关闭。
建议独立使用try-catch和try-finally语句块。
以IO流操作为例,
1) 不独立使用的情况下
InputStream is = ...;
try {
is.available();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
2) 独立使用的情况下
InputStream is = ...;
try {
try {
is.available();
} finally {
if (is != null) {
is.close();
}
}
} catch (IOException e) {
e.printStackTrace();
}
前者,在finally语句块中,当调用is.close()需要另外一个try-catch来处理异常。而后者的finally语句块中,
调用is.close()所抛出的异常将被外层的try-catch来处理。并且,外层try语句块确保关闭输入流,而外层try语句块将报告所出现的异常,也包括了finally子句中出现的。
1.3 事务操作
1.3.1 提交事务操作
public static void commitAndClose(Connection conn) throws SQLException {
if (conn != null) {
try {
conn.commit();
} finally { // 无论commit是否成功,都将关闭连接。
conn.close();
}
}
}
1.3.2 回滚事务操作
public static void rollback(Connection conn) throws SQLException {
if (conn != null) {
conn.rollback();
}
}
public static void rollbackAndClose(Connection conn) throws SQLException {
if (conn != null) {
try {
conn.rollback();
} finally {
conn.close();
}
}
}
都判断了传入进来的Connection是否为空,然后在调用Connection对象的commit()或rollback()方法,选择使用try-finally语句块关闭Connection资源,并向上抛出异常。关于事务还有一组对捕获的异常不做处理的操作,如:
public static void rollbackAndCloseQuietly(Connection conn) {
try {
rollbackAndClose(conn);
} catch (SQLException e) {
// 不做处理
}
}
1.4 加载数据库驱动
public static boolean loadDriver(String driverClassName) {
// 使用加载DbUtils类的类加载器。
return loadDriver(DbUtils.class.getClassLoader(), driverClassName);
}
// 提供一个类加载器
public static boolean loadDriver(ClassLoader classLoader, String driverClassName) {
try {
classLoader.loadClass(driverClassName).newInstance(); // 创建Class对象所对应的类的一个实例。
return true;
} catch (IllegalAccessException e) {
// 若没有默认的构造方法,还是适用于DriverManager的约定,如:Class.forName(driverClassName);所以返回true。
return true;
} catch (Exception e) {
return false;
}
}
1.5 打印栈信息
1.5.1 打印异常栈信息
public static void printStackTrace(SQLException e) {
printStackTrace(e, new PrintWriter(System.err)); // 使用System.err作为目的地,并将其包装成一个PrintWriter。
}
public static void printStackTrace(SQLException e, PrintWriter pw) {
SQLException next = e; // 为传入进来的异常对象取个别名,使其适用于下面的while循环。
while (next != null) { // while循环的终止条件为next == null。
next.printStackTrace(pw); // 将堆栈信息打印到pw。
next = next.getNextException(); // 获取当前异常对象的下一个异常对象
if (next != null) {
pw.println("Next SQLException:"); // 输出Tip
}
}
}
1.5.2 打印警告信息
由于SQLWarning是SQLException的子类,所以调用了
printStackTrace方法
public static void printWarnings(Connection conn) {
printWarnings(conn, new PrintWriter(System.err));
}
public static void printWarnings(Connection conn, PrintWriter pw) {
if (conn != null) {
try {
printStackTrace(conn.getWarnings(), pw);
} catch (SQLException e) {
printStackTrace(e, pw); // 打印conn.getWarnings()抛出的异常栈信息
}
}
}
2 QueryLoader
该类负责将SQL语句注册到内存中,并保证不会重复注册相同SQL语句。
该类实现从.properties文件中加载SQL语句并映射到Map中。
该类提供了protected的构造方法和protected的loadQueries(String path)方法,所以可以从该类扩展出不同的实现。
protected QueryLoader() {}
...
protected Map<String, String> loadQueries(String path) throws IOException {...}
2.1 饿汉式单例类
private static final QueryLoader instance = new QueryLoader();
public static QueryLoader instance() {
return instance;
}
2.2 存放SQL语句块的Map类型final成员属性
private final Map<String, Map<String, String>> queries = new HashMap<String, Map<String, String>>();
外层Map的key是存放文件的路径,而value是内层的Map,内层Map存放每一条SQL语句的key和value。Map可以保证key是唯一的。
2.3 默认实现
protected Map<String, String> loadQueries(String path) throws IOException {
// 将classpath中的资源文件读入到输入流对象中。路径字符串格式应该以"/"打头
InputStream in = getClass().getResourceAsStream(path);
if (in == null) {
throw new IllegalArgumentException(path + " not found.");
}
Properties props = new Properties();
try {
props.load(in);
} finally {
in.close();
}
// 将Properties对象直接拷贝给HashMap,为了更好的性能保证。
@SuppressWarnings({ "rawtypes", "unchecked" })
HashMap<String, String> hashMap = new HashMap(props); // loadQueries()的每一次调用都会创建出一个新的HashMap对象。
return hashMap;
}
2.4 加载
public synchronized Map<String, String> load(String path) throws IOException {
Map<String, String> queryMap = this.queries.get(path); // 试着到成员属性queries中取
// 判断是否已经存在。
if (queryMap == null) {
queryMap = this.loadQueries(path); // 使用默认的实现将SQL语句加载到Map中
this.queries.put(path, queryMap); // 放入queries中
}
return queryMap;
}
2.5 卸载
public synchronized void unload(String path) {
this.queries.remove(path); // 从成员属性queries中删除即可
}
加载与卸载方法都加上了synchronized关键字,适应了多线程环境,避免被重复加载或卸载。