Как сообщить Oracle, что записи в Users блокировать нельзя? Для этого нужно явно указать таблицу, а лучше—первичный ключ в этой таблице: FOR UPDATE OF имя поля. После ключевого слова OF указывается поле, по которому сервер узнает, какую запись из связанных таблиц нужно заблокировать. Итак, наш запрос должен выглядеть следующим образом:
SELECT *
FROM Docs d, Users u WHERE d.PrimaryKey=10 AND d.UserlD =u.PrimaryKey FOR UPDATE OF d.PrimaryKey
Вот теперь будет заблокирована только одна запись документа и только из таблицы Docs.
Продолжительность
Чтобы пользователи не блокировали данные надолго, при открытии формы можно запускать таймер и через пять минут запрашивать подтверждения продолжения работы. Если пользователь не подтвердит, то форма должна закрыться и освободить ресурс. Это поможет в тех случаях, если кто-нибудь забывчивый уйдет на обед или домой, оставив запущенную программу и открытые ресурсы. Если у вас многооконная система и предусмотрена возможность открытия сразу множества документов, то пользователь может забывать закрывать окна редактирования, что опять-таки приведет к лишним, а главное — неоправданным блокировкам. Не помешало бы средство, отключающее таймер нате случаи, когда пользователь действительно хочет работать с данными долго и должен делать это осознано.
Система
Теперь поговорим о системных представлениях, с помощью которых вы можете управлять и контролировать блокировки. Все блокировки можно получить с помощью представления v$lock:
SELECT * FROM v$lock
Результат не очень информативен, потому что содержит какие-то адреса и цифры, да и записей очень много. В поле sid находиться идентификатор сессии, а в поле Туре можно увидеть тип блокировки. Когда вызывается SELECT FOR UPDATE, то создается блокировка транзакции, а в поле Туре можно увидеть ТХ. Существуют и другие типы блокировки, например, блокировка сервера, изменение структуры таблиц и т.д. Более подробно об этом можно прочитать в документации по Oracle.
Исходя из вышесказанного, более информативным будет следующий запрос:
SELECT s.username, 1.* FROM v$lock 1, v$session s WHERE l.TYPE = 'TX' and l.sid=s.sid
Здесь мы связались с представлением v$session, которое возвращает сессии, и теперь в результат попадает имя пользователя, который удерживает блокировку. Из представления v$session можно получить много полезной информации. Просто выполните следующий запрос, чтобы определиться, какие еще поля можно включить в запрос, показанный выше:
SELECT *
FROM v$session
Пока все хорошо, но по полученным данным мы до сих пор не можем определить, кто же именно заблокировал определенную строку. Воттутя бы порекомендовал создать пользовательскую таблицу журнала из следующих пол ей:
1. Идентификатор документа;
2. Идентификатор пользователя;
3. Дата.
Теперь, после каждого удачного открытия ресурса на редактирование можно занести запись в эту таблицу с указанием документа, пользователя и даты. В программе (в окне просмотра реестра) можно реализовать функцию просмотра журнала по определенному документу. Теперь, если что-то не открывается на редактирование, то с помощью журнала пользователи сами смогут узнать, кто последний заблокировал запись и не дает работать другим.
Журнал позволит избежать вам множества звонков с вопросами, кто и что заблокировал. Если злополучного пользователя нет, то тогда уже будут обращаться к вам, а вы с помощью таблиц v$lock и v$session сможете отыскать блокировки и снять их.
LOCK TABLE
Допустим, что нам нужно произвести несколько действий по изменению данных во всей таблице (или в большинстве ее записей), и все эти изменения невозможно уложить в один единственный запрос UPDATE, да и сама работа с данными отнимет не пять минут. При выполнении одной операции UPDATE блокировать таблицу не имеет смысла, но при серьезных изменениях это просто необходимо. Если после выполнения некоторых действий кто-то заблокирует хотя бы одну запись, дальнейшие ваши действия будут парализованы. Такой трюк может привести к нарушению целостности данных, поэтому перед большим количеством изменений лучше заблокировать всю таблицу. Для блокировки всей таблицы лучше использовать не SELECT FOR UPDATE, a LOCK TABLE IN EXCLUSIVE MODE. Этот оператор блокирует всю таблицу сразу, а не каждую строку в отдельности.
Заблокировав всю таблицу, вы можете не торопясь модифицировать данные, и никто другой не оторвет вас от этого интересного занятия.
Итого
Блокировки — очень мощное и удобное средство для многопользовательских приложений. Используйте их и вы избавитесь от множества проблем. Главное—следовать правилам:
1. Старайтесь блокировать минимально необходимое количество записей в таблице.
2. Не забудьте после закрытия формы сохранить или откатить изменения и закрыть набор данных, чтобы освободить ресурс.
Блокировки гарантируют, что введенные пользователем данные будут сохранены в базе, и никто другой в этот момент не сможет их изменить. Другой пользователь получит доступ к документу только после его освобождения, а значит, не сможет отменить изменения.
А теперь серьезный недостаток—если на какую-то запись в таблице есть блокировка, то у вас возникнут проблемы с изменением структуры — нельзя будет добавить или удалить какое-то поле. Чтобы внести изменения в структуру таблицы, придется ждать окончания рабочего дня или просить всех пользователей выйти из программы. В этом случае, если в системе останутся какие-то незакрытые сессии, то их можно будет убивать, потому что они явно мертвые.
Список литературы
IT спец № 07 ИЮЛЬ 2007