接續上篇 java.sql.Date v.s. java.sql.Timestamp in Oracle 9i
Oracle JDBC 的 FAQ 中提到,在 Oracle 9i 中如果要取得時間部分的資料,最好也最標準的方法是將資料型態由 DATE 改為 TIMESTAMP。經過實際測試將資料型態改為 TIMESTAMP 之後,Oracle 9i JDBC Driver 並非如預期地將 TIMESTAMP 轉為 java.sql.Timestamp,而是轉型為 oracle.sql.TIMESTAMP,應用程式必須再呼叫一次 oracle.sql.TIMESTAMP.timestampValue(),才能正確轉回 java.sql.Timestamp,這對我原先已經撰寫完成的許多程式來說,必須一一修改,相當麻煩。
所幸原先程式係透過一個共用元件來使用 DBUtils,經過檢視 Jakarta Commons DBUtils 的原始碼後,問題出在 org.apache.commons.dbutils.BasicRowProcessor 的 toMap() 函式
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public Map toMap(ResultSet rs) throws SQLException { Map result = new CaseInsensitiveHashMap(); ResultSetMetaData rsmd = rs.getMetaData(); int cols = rsmd.getColumnCount(); for (int i = 1; i <= cols; i++) { result.put(rsmd.getColumnName(i), rs.getObject(i)); } return result; } |
裡面使用 ResultSet.getObject() 來直接取得 JDBC Driver 回傳的型態,而 Oracle 9i JDBC Driver 遇到資料型態為 DATE,則回傳 java.sql.Date,資料型態為 TIMESTAMP,則回傳 oracle.sql.TIMESTAMP。真不知道是 Oracle 的 bug 還是故意這麼搞的,所以 DBUtils 得到的就絕對不會是 java.sql.Timestamp
思考之後,最好的方式應該是寫一個 Wrapper 去繼承 org.apache.commons.dbutils.BasicRowProcessor,然後只改寫 toMap()函式如下
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
|
public class BasicRowProcessorWrapper extends BasicRowProcessor { /** * 繼承自 org.apache.commons.dbutils.BasicRowProcessor * 由於 Oracle 9i JDBC Driver 遇到資料型態為 DATE 會轉為 java.sql.Date * 導致僅留下日期而遺失時間的部分,遇到資料型態為 TIMESTAMP 會轉為 oracle.sql.TIMESTAMP * 必須自行再轉為 java.sql.Timestamp,因此改寫 toMap() 函式 * @param rs ResultSet * @return Map * @throws SQLException */ public Map toMap(ResultSet rs) throws SQLException { Map result = new CaseInsensitiveHashMap(); ResultSetMetaData rsmd = rs.getMetaData(); int cols = rsmd.getColumnCount(); for (int i = 1; i <= cols; i++) { Object obj = rs.getObject(i); if ("oracle.sql.TIMESTAMP".equals(obj.getClass().getName())) { result.put(rsmd.getColumnName(i), rs.getTimestamp(i)); } else if (obj instanceof java.sql.Date) { result.put(rsmd.getColumnName(i), rs.getTimestamp(i)); } else { result.put(rsmd.getColumnName(i), obj); } } return result; } } |
遇到 JDBC Driver 型態為 oracle.sql.TIMESTAMP 或是 java.sql.Date,則強迫使用 getTimestamp() 函式來轉型為 java.sql.Timestamp,如此一來,即使未來 DBUtils 改版更新,也無須更動舊有程式
感謝,正好遇到這樣的問題