Ответ 1
Обычно, когда вы смотрите на оптимистичную блокировку, вы также используете библиотеку, например Hibernate или другую реализацию JPA с @Version
.
Пример может выглядеть следующим образом:
public class Person {
private String firstName;
private String lastName;
private int age;
private Color favoriteColor;
@Version
private Long version;
}
хотя, очевидно, нет смысла добавлять аннотацию @Version
, если вы не используете фреймворк, который поддерживает это.
Тогда DDL может быть
CREATE TABLE people (
person_id PRIMARY KEY AUTO_INCREMENT,
first_name VARCHAR(100) NOT NULL,
last_name VARCHAR(100) NOT NULL, # } I realize these column defs are not valid but this is just pseudo-code
age INT NOT NULL,
color_id FOREIGN KEY (colors) NOT NULL, # Say we also have a colors table and people has a 1:1 relationship with it
version BIGINT NOT NULL
);
Что происходит с версией?
- Каждый раз, прежде чем хранить объект, вы проверяете, сохранилась ли версия, хранящаяся в базе данных, той версии, которую вы знаете.
- Если это так, сохраните свои данные с версией, увеличенной на один
Чтобы выполнить оба шага, не рискуя другим процессом, изменяющим данные между обоими шагами, его обычно обрабатывают с помощью инструкции типа
UPDATE Person SET lastName = 'married', version=2 WHERE person_id = 42 AND version = 1;
После выполнения инструкции вы проверяете, обновлена ли строка или нет. Если вы это сделали, никто не изменил данные, так как вы его прочитали, иначе кто-то изменил данные. Если кто-то изменил данные, вы обычно получите OptimisticLockException
используемой вами библиотеки.
Это исключение должно привести к аннулированию всех изменений, и процесс изменения значения, которое необходимо перезапустить, в качестве условия, на котором должен был обновляться объект, может больше не применяться.
Так что никакого столкновения:
- Процесс A читает личность
- Процесс A пишет Person, тем самым увеличивая версию
- Процесс B читает Person
- Процесс B пишет Person, тем самым увеличивая версию
Collision:
- Процесс A читает личность
- Процесс B читает Person
- Процесс A пишет Person, тем самым увеличивая версию
- Процесс B получает исключение при попытке сохранить с изменением версии после того, как Person был прочитан.
Если Color - это другой объект, вы должны поместить туда ту же схему.
Что такое оптимистичная блокировка?
- Оптимистическая блокировка не является волшебством для слияния конфликтующих изменений. Оптимистичная блокировка просто предотвратит случайные перезаписи процессов другим процессом.
- Оптимистичная блокировка фактически не является реальным DB-Lock. Он просто работает, сравнивая значение столбца версии. Вы не запрещаете другим процессам получать доступ к каким-либо данным, поэтому ожидайте, что вы получите
OptimisticLockException
s
Какой тип столбца использовать в качестве версии?
Если многие различные приложения обращаются к вашим данным, вам может быть лучше использовать столбец, автоматически обновляемый базой данных. например для MySQL
version TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;
таким образом приложения, реализующие оптимистичную блокировку, заметят изменения в немых приложениях.
Если вы обновляете объекты чаще, чем разрешение TIMESTAMP
или интерпретацию Java, то подход может не обнаружить определенные изменения. Также, если вы позволите Java генерировать новый TIMESTAMP
, вам нужно убедиться, что все машины, на которых запущены ваши приложения, находятся в идеальном режиме синхронизации времени.
Если все ваши приложения могут быть изменены целой, длинной,... версией, как правило, является хорошим решением, поскольку оно никогда не будет страдать от разных заданных часов; -)
Существуют и другие сценарии. Вы можете, например, использовать хэш или даже произвольно генерировать String
каждый раз, когда строка должна быть изменена. Важно отметить, что вы не повторяете значения, пока какой-либо процесс хранит данные для локальной обработки или внутри кеша, поскольку этот процесс не сможет обнаружить изменения, посмотрев на столбец версии.
В качестве последнего средства вы можете использовать значение всех полей в качестве версии. Хотя это будет самый дорогой подход, в большинстве случаев это способ получить аналогичные результаты без изменения структуры таблицы. Если вы используете Hibernate, существует @OptimisticLocking
-замена, чтобы обеспечить соблюдение этого поведения. Используйте @OptimisticLocking(type = OptimisticLockType.ALL)
для класса сущности для отказа, если какая-либо строка изменилась с тех пор, как вы прочитали объект или @OptimisticLocking(type = OptimisticLockType.DIRTY)
, чтобы просто сбой, когда другой процесс также изменил поля, которые вы изменили.