2013-12-22 2 views
3

У меня есть экран создания пользователя, который принимает различные данные пользователя вместе с именем и номером мобильного телефона. У меня есть соответствующая таблица USER, в которой имя и номер мобильного телефона образуют составной уникальный ключ. Существуют и другие ограничения целостности, определенные в этой таблице.База данных - обработка уникального нарушения ограничений

Когда пользовательские данные вводятся на экране «Создать пользователя», который нарушает это ограничение, пользователю необходимо показать сообщение об ошибке «user friendly».

Когда происходит такое нарушение, исключение, которое я получаю из базы данных 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: Напишите API уровня DAO, который будет принимать первое имя и номер мобильного телефона только в качестве двух параметров, и вызовет запрос базы данных, чтобы увидеть, существует ли существующая запись, соответствующая этому имени/мобильной комбинации. Если значение true, покажите сообщение об ошибке пользователю; else, запустите запрос вставки, чтобы вставить пользователя записи в таблицу USER.

Мне не нравится вариант 1, так как мне нужно «разобрать» сообщение об исключении, которое не является чистым решением. Мне также не нравится вариант 2, так как мне нужно запустить «два запроса» в базе данных, которая менее эффективна, чем вариант 1, который является единственным решением для запросов.

Вопрос: Есть ли другие варианты, которые лучше этих двух? Если нет, то какой из них является правильным подходом к этим двум?

+2

I * обычно * найти его чистейших в этих случаях в первую «проверить», чтобы убедиться, что имя пользователя (или любые ограничения) действительны/разрешено, а затем попробовать фактическое обновление и выставить его через 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

Вот PHP версия eggyal's answer, используя 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']; 
    } 
} 
Смежные вопросы