2013-12-22 88 views
3

我有一個用戶創建屏幕,其中包含各種用戶詳細信息以及名字和手機號碼。我有一個相應的USER表,其中名字和手機號碼形成一個複合的唯一密鑰。還有其他完整性約束也在此表中定義。數據庫處理唯一約束違規

當創建用戶屏幕違反此約束上輸入用戶數據,用戶需要顯示一個「用戶友好的」錯誤消息。

當這種衝突發生時,我從MySQL數據庫中獲取的例外是:

com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '1-1' for key `uk_FIRST_NAME__MOBILE_idx` 

有兩個選項顯示有意義的信息(例如:「錯誤:用戶名已經存在給定手機號碼,請更改其中任何一個「)。

選項1:在這個異常的catch塊,解析MySQL的異常的信息,並查找「uk_FIRST_NAME__MOBILE_idx」。如果存在,則如上所述顯示用戶友好的消息。

選項2:寫一個DAO級別的API,將採取的名字和手機號碼作爲唯一的兩個參數,消防數據庫查詢,看看是否有一個現有記錄匹配該名/移動組合。如果爲true,則向用戶顯示錯誤消息;否則,運行插入查詢以將記錄的用戶插入到USER表中。

我不喜歡選項1,因爲它需要我「解析」異常消息,這不是一個乾淨的解決方案。我不喜歡選項2,因爲它需要我在數據庫上運行「兩個查詢」,這比單個查詢解決方案的選項1效率低。

問題:是否有它比這兩個更好的其他選擇嗎?如果不是,上述兩種方法中哪一種是正確的?

+2

我通常*發現它乾淨在這些情況下,以第一個「檢查」,以確保用戶名(或任何限制)是有效的/允許的,然後嘗試實際更新並通過DAL公開它 - 這是也可用於提供數據的「實時驗證」(例如驗證警告)。在小競爭條件下,插入異常可以被視爲「我們很抱歉,請再試一次!」。也就是說,數據庫是安全網,但不對最終用戶錯誤報告負責。 – user2864740

+0

感謝您指出選項2中的'競爭條件'的可能性。 – PhantomReference

回答

6

我認爲「選項2」(在嘗試插入之前手動檢查約束)是非常可怕的,不僅因爲種族危害(可以通過locking reads避免),還因爲額外的負載在數據庫上:畢竟,手動檢查約束完全否定了在數據庫中使用約束的目的和好處。

我同意解析錯誤消息字符串感覺「髒」,但字符串是well defined。甚至可以參考潛在的errmsg.txt或源頭文件。

一旦一個人已經從錯誤消息中提取的鍵值名,一個可以使用KEY_COLUMN_USAGE信息模式去識別出錯的列:

public static final int ER_DUP_ENTRY = 1062; 
public static final int ER_DUP_ENTRY_WITH_KEY_NAME = 1586; 

public static final String REGEX_DUP_ENTRY_WITH_KEY_NAME = 
    "Duplicate entry '(.*)' for key '(.*)'"; 

// ... 


try { 
// ... 
} catch (MySQLIntegrityConstraintViolationException e) { 
    switch (e.getErrorCode()) { 
    case ER_DUP_ENTRY: 
    case ER_DUP_ENTRY_WITH_KEY_NAME: 
     Pattern p = Pattern.compile(REGEX_DUP_ENTRY_WITH_KEY_NAME); 
     Matcher m = p.matcher(e.getMessage()); 

     SQLQuery query = session.createSQLQuery(
     " SELECT COLUMN_NAME" + 
     " FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE" + 
     " WHERE CONSTRAINT_SCHEMA = :schema" + 
     " AND CONSTRAINT_NAME = :key" 
    ); 
     query.setString("schema", "my_schema"); 
     query.setString("key" , m.group(2)); 

     showDuplicateError(query.list()); 

     break; 
    } 
} 
+0

「...畢竟,手動檢查約束完全否定了在數據庫中使用約束的目的和好處「。這是一個很棒的觀點!+1 – PhantomReference

-1

這裏是一個eggyal's answer PHP版本,使用的MySQLi。

// Error: 1062 SQLSTATE: 23000 (ER_DUP_ENTRY)    Message: Duplicate entry '%s' for key %d 
// Error: 1586 SQLSTATE: 23000 (ER_DUP_ENTRY_WITH_KEY_NAME) Message: Duplicate entry '%s' for key '%s' 
if($mysqli->errno === 1062 || $mysqli->errno === 1586) 
{ 
    if(preg_match("/Duplicate entry '(.*)' for key '(.*)'/", $mysqli->error, $matchArray) === 1) 
    { 
     $duplicatedValue = $matchArray[1]; 
     $uniqueKeyName = $matchArray[2]; 

     if(!($stmt = $mysqli->prepare('SELECT COLUMN_NAME' 
            . ' FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE' 
            . ' WHERE CONSTRAINT_SCHEMA = ?' 
            . ' AND CONSTRAINT_NAME = ?'))) 
     { 
      die; // Error? Check $mysqli->errno and $mysqli->error; 
     } 
     $schemaName = // Name of the schema (string). 
     if(!$stmt->bind_param('ss', $schemaName, $uniqueKeyName)) 
     { 
      die; // Error? Check $mysqli->errno and $mysqli->error; 
     } 
     if(!$stmt->execute()) 
     { 
      die; // Error? Check $mysqli->errno and $mysqli->error; 
     } 
     $res = $stmt->get_result(); 
     if(!$res) 
     { 
      die; // Error? Check $mysqli->errno and $mysqli->error; 
     } 
     $row = $res->fetch_assoc(); 
     if($row === null) 
     { 
      die; // No results? 
     } 
     $columnName = $row['COLUMN_NAME']; 
    } 
}