Вставка объекта в БД с использованием Hibernate, только если он не существует - PullRequest
1 голос
/ 25 февраля 2009

Допустим, у меня есть сопоставленный объект User с user_id в качестве первичного ключа и почтовый столбец с уникальным ограничением. Я хочу, чтобы моя программа сохраняла объект User в таблице users, только если он не существует.

Я могу сделать следующее в моей функции вставки:

  1. начать транзакцию
  2. запрос пользователя с указанным письмом
  3. если его не существует, создайте нового пользователя и сохраните его, используя сеанс
  4. фиксация или откат в случае неудачи

Есть две проблемы:

  1. Дорого выполнять два запроса. Если бы я использовал простой SQL, я мог бы использовать синтаксис «INSERT IGNORE», но в моем случае я хочу использовать метод сохранения в спящем режиме, а также в конце я хочу сохранить объект User. Есть ли решение для этого?
  2. параллелизм. Возможно ли, что два экземпляра моего приложения будут работать одновременно. Оба будут использовать функцию «addUser» с одинаковым параметром «mail». Первый пройдет через фазу 2 и не найдет пользователя с таким письмом. Второй также пройдет фазу 2, не найдя пользователя, потому что первый экземпляр все еще не сохранил своего пользователя и зафиксировал. Затем первый экземпляр сохранит и зафиксирует пользователя, а затем второй экземпляр сохранит и зафиксирует пользователя, который имеет ту же почту, что и почта первого экземпляра. Конечно, это не удастся из-за ограничений. Я что-то пропустил?

Если я прав, что мне делать:

  1. Надеюсь, что этого не произойдет.
  2. Блокировка таблицы пользователей для чтения и записи в начале транзакции.
  3. Добавить try ... кроме функции, которая будет исключать исключение из уникального ограничения, и, если выброшено исключение, просто выберет пользователя из БД в соответствии с его почтой.
  4. Другие решения ...

Ответы [ 3 ]

1 голос
/ 25 февраля 2009

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

0 голосов
/ 22 сентября 2011

Пока что единственный способ, которым я могу это сделать, работает:

<sql-insert check="none" callable="true">
            {call saveOrUpdate (?, ?, ?, ?, ?)}
 </sql-insert>

http://forum.springsource.org/showthread.php?65265-Hibernate-insert-problem

Другие опции, которые я не могу сделать, чтобы это работало:

Может быть, вы можете сделать "вставку, где не существует" с аннотацией @SQLInsert Hibernate

следующая опция предназначена только для редактирования:

другой вариант - использовать «оптимистическую блокировку со столбцом версии» и обрабатывать исключение в случае исключения параллелизма.

ссылка: http://docs.jboss.org/hibernate/core/3.3/reference/en/html/transactions.html#transactions-optimistic

0 голосов
/ 26 февраля 2009

Вы должны просто сосредоточиться на изящной обработке уникального нарушения ограничения.

Вы можете сделать это, перехватив исключение ConstraintViolationException. Если вы хотите быть более зернистым, если, возможно, существуют множественные нарушения, вы можете сделать что-то вроде этого:

...
} catch (ConstraintViolationException e) {
    if (e.getConstraintName() != null && e.getConstraintName().toLowerCase().equals("the_name_of_your_contraint")) {
        // do something, like add an error to be displayed on the UI
    }
}
...