Я предполагаю, что немного прагматизма было бы хорошо здесь. Отображения между объектами и таблицами всегда имеют немного странности тут и там. Вот то, что я делаю:
я использую Ibatis, чтобы говорить с моей базой данных (Java к Oracle). Каждый раз, когда у меня есть структура наследования, где я хочу, чтобы подкласс был сохранен в базе данных, я использую "различитель". Это - прием, где Вы имеете одну таблицу для всех Классов (Типы) и имеете все поля, которые Вы могли возможно хотеть сохранить. Существует один дополнительный столбец в таблице, содержа строку, которая используется Ibatis для наблюдения, какой тип объекта это должно возвратить.
Это выглядит забавным в базе данных и иногда может получать Вас в проблему с отношениями к полям, которые не находятся во всех Классах, но 80% времени, это - хорошее решение.
Относительно Вашего отношения между категорией и продуктом, я добавил бы categoryId столбец к продукту, потому что это сделает жизнь действительно легкой, и SQL мудрый и Отображающийся мудрый. Если Вы действительно застреваете при выполнении "теоретически корректной вещи", можно рассмотреть дополнительную таблицу, которая имеет только 2 colums, соединяя Категории и их продукты. Это будет работать, но обычно эта конструкция только используется при необходимости в many-many отношениях.
Попытка сохранить его максимально простым. Наличие "академического решения" хорошо, но обычно означает немного излишества и более трудно осуществить рефакторинг, потому что это слишком абстрактно (как сокрытие отношений между Категорией и продуктом).
я надеюсь, что это помогает.
Является ли PK первичным ключом? Тогда это не проблема, если вы уже знаете первичный ключ, нет никакого спорта. Если pk является первичным ключом, тогда возникает очевидный вопрос как узнать pk элемента для удаления из очереди ...
Проблема в том, если вы не знают первичный ключ и хотят исключить из очереди следующий «доступный» (например, status = y) и пометить его как исключенный (удалить или установить status = z).
Правильный способ сделать это заключается в использовании одного оператора. К сожалению, синтаксис Oracle и SQL Server отличается. Синтаксис SQL Server следующий:
update top (1) [<table>]
set status = z
output DELETED.*
where status = y;
Я недостаточно знаком с предложением RETURNING Oracle, чтобы привести пример, аналогичный OUTPUT в SQL.
Другие решения SQL Server требуют, чтобы подсказки блокировки для SELECT (с UPDLOCK) были правильными. В Oracle предпочтительным способом является использование FOR UPDATE, но это не работает в SQL Server, поскольку FOR UPDATE должен использоваться вместе с курсорами в SQL.
В любом случае поведение, которое вы указали в исходном сообщении, неверно . Несколько сеансов могут выбрать одну и ту же строку (и) и даже все обновить ее, возвращая один и тот же исключенный из очереди элемент (ы) нескольким читателям.
У меня есть приложения, работающие по аналогичной схеме. Есть такая же таблица, как ваша, которая представляет очередь работы. В таблице есть два дополнительных столбца: thread_id и thread_date. Когда приложение запрашивает работу в очереди, оно отправляет идентификатор потока. Затем один оператор обновления обновляет все применимые строки столбцом идентификатора потока с отправленным идентификатором и столбцом даты потока с текущим временем. После этого обновления он выбирает все строки с этим идентификатором потока. Таким образом, вам не нужно объявлять явную транзакцию. «Блокировка» происходит при первоначальном обновлении.
Столбец thread_date используется, чтобы гарантировать, что вы не получите потерянные рабочие элементы. Что произойдет, если элементы будут извлечены из очереди, а затем ваше приложение выйдет из строя? У вас должна быть возможность снова попробовать эти рабочие элементы. Таким образом, вы можете взять из очереди все элементы, которые не были отмечены как завершенные, но были назначены потоку с датой потока в далеком прошлом. Определение «дальний» зависит от вас.
Как правило, чтобы выполнить операцию, подобную этой атомарной, вам необходимо убедиться, что вы установили эксклюзивную (или обновленную) блокировку при выполнении выбора, чтобы никакая другая транзакция не могла читать строка перед обновлением.
Типичный синтаксис для этого примерно такой:
select * from table where pk = x and status = y for update
, но вам нужно его найти, чтобы быть уверенным.
Попробовать. Проверка выполняется в операторе UPDATE.
IF EXISTS (SELECT * FROM sys.tables WHERE name = 't1')
DROP TABLE dbo.t1
GO
CREATE TABLE dbo.t1 (
ColID int IDENTITY,
[Status] varchar(20)
)
GO
DECLARE @id int
DECLARE @initialValue varchar(20)
DECLARE @newValue varchar(20)
SET @initialValue = 'Initial Value'
INSERT INTO dbo.t1 (Status) VALUES (@initialValue)
SELECT @id = SCOPE_IDENTITY()
SET @newValue = 'Updated Value'
BEGIN TRAN
UPDATE dbo.t1
SET
@initialValue = [Status],
[Status] = @newValue
WHERE ColID = @id
AND [Status] = @initialValue
SELECT ColID, [Status] FROM dbo.t1
COMMIT TRAN
SELECT @initialValue AS '@initialValue', @newValue AS '@newValue'
ColID Status
----- -------------
1 Updated Value
@initialValue @newValue
------------- -------------
Initial Value Updated Value