Как заблокировать транзакцию для чтения строки и вставки в Hibernate? - PullRequest
1 голос
/ 25 мая 2010

У меня есть таблица с именем и именем_счетом. Поэтому, когда я вставляю новую запись, я сначала проверяю максимальное значение name_count для этого имени. Затем я вставляю запись с таким максимумом + 1. Отлично работает ... за исключением mysql 5.1 и hibernate 3.5, по умолчанию операции чтения не учитывают границы транзакций. 2 из этих вставок для одного и того же имени могут происходить в одно и то же время и заканчиваться тем же именем name_count, которое полностью портит мое приложение!

К сожалению, есть некоторые специфические ситуации, в которых вышеупомянутое на самом деле довольно распространено. Так что мне делать? Я предполагаю, что могу сделать пессимистическую блокировку, когда любая строка, которую я читаю, заблокирована для дальнейшего чтения, пока я не зафиксирую или не откатлю транзакцию. Или я могу сделать оптимистическую блокировку со столбцом версии, который автоматически продолжает попытки до тех пор, пока не возникнут конфликты?

Каков наилучший подход для моей ситуации и как мне указать его в Hibernate 3.5 и mysql 5.1? Таблица выше массивная и часто используется.

Ответы [ 3 ]

3 голосов
/ 25 мая 2010

Именно поэтому большинство людей используют SEQUENCE для создания уникальных чисел. Тем не менее, вы должны заблокировать всю таблицу (LOCK TABLES). Проблема: Вы должны заблокировать все нужные вам таблицы (т. Е. Все, что может коснуться Hibernate), иначе вы получите ошибки. Затем необходимо разблокировать таблицы и выполнить обе эти операции синхронно с остальной частью транзакции.

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

[РЕДАКТИРОВАТЬ] Вы можете определить последовательность, а затем использовать собственный запрос SQL для получения следующего значения. Вы можете попытаться определить генератор последовательности (см. документы ), но, возможно, сопоставление разрешено только для полей Id.

Re "200 миллионов имен": пока база данных может хранить число, вы также можете определить последовательность по нему.

Re "блокировка на основе строк": какую строку вы планируете заблокировать? Тот, с максимальным значением? Я не уверен, что оператор max() остановится, если вы заблокируете его. То, что вы можете попробовать, это триггер . Поскольку триггеры являются атомарными, никто не может вставить строку во время ее работы. Но триггер немного сложен в обслуживании.

0 голосов
/ 28 мая 2010

Если вы хотите сделать это независимым от базы данных (избегая последовательностей) и не должны запускать функцию max () перед вставкой, попробуйте вместо этого использовать guid:

/**
 * The unique id. This id is generated this object is persited. The id is a 32 character UUID.
 * This gives each entity a completely unqique identifier. This is completely unique across all
 * databases, jvms and entities.
 */
@Id
@GeneratedValue(generator = "system-uuid")
@GenericGenerator(name = "system-uuid", strategy = "uuid")
@Column(length = 32, name = EntityObject.Columns.ID)
@DocumentId
private String id;

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

Мы использовали это со многими базами данных, и мы можем легко перемещаться между ними.

0 голосов
/ 25 мая 2010

MySQL не поддерживает последовательности, поэтому они должны быть смоделированы. Это интересный рецепт для него: http://forums.mysql.com/read.php?61,143867,194058#msg-194058

Обратите внимание, что тип таблицы - MyISAM, поэтому он не учитывает параллелизм транзакций и возвращает следующее значение счетчика для каждого запроса.

...