Saturday, September 17, 2011

JDBC 欄位資料重複的偵測方式

系統需求偶而會遇到要求用一特定邏輯產生資料代碼,如流水序號。
一個流水序號有可能是 2012010100001,前面八碼20120101代表2012年1月1日產生的資料,後面五碼則是當天資料產生到第幾筆的數字,不足五碼的數字以0填滿。

流水號規則很多種,以上只是其中一個很簡單例子。

只要代碼是由程式產生再寫入資料庫的,就有機會與其他資料重覆。以上描述的例子,在兩個人同時操作時產出的序號就有可能重覆。

資 料庫中儲存此序號欄位應設定唯一的限制,也就是說他可以設為Primary Key或Unique以確保在資料庫中不會有重複的資料發生。我們的程式只需要在寫入資料庫時,偵測到序號重複,再重新產生一次序號寫入就行了。不過還是 記得限制重覆嘗試的次數,如果太多次了,表示系統應該愈到蠻嚴重的問題了。

JDBC寫資料庫時出錯時會丟出 (throw) SQLException,只要將他抓下來 (catch) 再檢查觸發此錯誤的原因就有辦法知道是不是資料重覆造成的。SQLException有一個屬性SQLState,可從getSQLState()取得。因為SQLState的內容已有一個ANSI標準,並且現在主流的資料庫都有支援,我們直接從此屬性就可以知道是不是資料重複。此標準中的代碼 23000 代表Integrity constraint violation,所以有unique限制的欄位發生資料重覆時,SQLState的代碼就會是這個。當然FK的錯誤應該也會產生此代碼,不過正常的程式不太可能會發生這樣的問題,是吧,是吧?

所以一個會產生流水號並寫入資料庫的程式可能看起來會像這樣:

try {
    // 產生序號,寫入資料庫。
} catch (SQLException ex) {
    if (ex.getSQLState().equals("23000")) {
        // 序號重覆了,再產生一次。
    }
    else {
        // 糟糕,其他的資料庫錯誤。
    }
}


這裡可以看到SQLState的ANSI定義。注意SQLState也有可能包含一些廠商自定的代碼,前面連結到的網頁就有包含Informix的定義,不過不會與標準的定義衝突到。另外getErrorCode()是各資料庫自定的錯誤代碼,有些時候也可以參考。