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關鍵字,適應了多線程環境,避免被重複加載或卸載。