Использование WHERE NOT EXISTS с фиктивной таблицей, не работающей в базе данных Apache Derby

Итак, я использую Java с Apache Derby и пытаюсь вставить запись, но только если запись с тем же ключом еще не существует, потому что все значения, которые я хочу, чтобы существовали в моем коде, а не в базе данных, которую я используйте фиктивную таблицу derbys (аналог DUAL для db2), это запрос, который я использую (md5 является первичным ключом)

PreparedStatement stmt = conn.prepareStatement("INSERT INTO artwork (md5,image) " +
               "SELECT ?,?" +
               "FROM SYSIBM.SYSDUMMY1 " +
               "WHERE NOT EXISTS ( SELECT 1 FROM artwork WHERE md5=?)");

    stmt.setString(1, key);
    stmt.setBinaryStream(2, new ByteArrayInputStream(imageData), imageData.length);
    stmt.setString(3, key);

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

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

Итак, мой запрос не делает то, что я думаю, или я неправильно понимаю общую концепцию?


person Paul Taylor    schedule 09.01.2012    source источник
comment
Вы пробовали использовать транзакции?   -  person Romain    schedule 09.01.2012
comment
Не понимаю, как это относится   -  person Paul Taylor    schedule 09.01.2012
comment
Я не вижу, как транзакции помогут в этой ситуации. К OP: есть ли что-то плохое в том, чтобы просто позволить повторяющимся ошибкам значения и игнорировать их? Это позволит достичь той же цели.   -  person ean5533    schedule 09.01.2012
comment
... или не разрешать их и обрабатывать исключения??   -  person soulcheck    schedule 09.01.2012
comment
Возможно, нет, но я не понимаю, почему мой код не работает должным образом, и я думаю, что БД медленнее выдает исключения после недопустимой вставки, чем не делать вставку.   -  person Paul Taylor    schedule 09.01.2012
comment
И с точки зрения @soulcheck, вы можете получить атомарные свойства, используя транзакции (при условии, что Derby проходит весь путь до SERIALIZABLE, в чем я не уверен).   -  person Romain    schedule 09.01.2012
comment
@soulcheck Интересно, не знал. Спасибо.   -  person ean5533    schedule 09.01.2012
comment
@Romain Я устанавливаю setAutoCommit(false) , а затем commit() в конце, это то, что вы подразумеваете под транзакциями   -  person Paul Taylor    schedule 09.01.2012
comment
@soulcheck, это действительно интересно и неожиданно, я уверен, что использовал этот метод в других БД в прошлом.   -  person Paul Taylor    schedule 09.01.2012
comment
@user294896 user294896 Да, хотя это может привести к использованию недостаточного уровня изоляции (я не знаю значения по умолчанию в Derby).   -  person Romain    schedule 09.01.2012
comment
Я до сих пор не понимаю, почему он работает в однопоточном режиме. то есть, если один поток пытается дважды добавить одну и ту же строку, предложение Where, по-видимому, предотвращает вставку и, следовательно, исключение   -  person Paul Taylor    schedule 09.01.2012
comment
@soulcheck: конечно, запросы с подзапросами являются атомарными в PostgreSQL (или Oracle, или DB2, ...). Это называется согласованностью чтения и обычно достигается путем реализации контроля параллелизма нескольких версий.   -  person a_horse_with_no_name    schedule 09.01.2012
comment
@user294896 user294896, когда однопоточный контент таблицы иллюстраций не может меняться между проверкой условия и вставкой. В многопоточном приложении все может происходить в следующей последовательности: 1. поток t1 проверяет истинность условия; 2. контекст переключается на поток t2, который проверяет условие, оно снова истинно; 3. контекст переключается на t1, который продолжается вставкой; 4. контекст переключается на t2, который продолжает вставку и терпит неудачу   -  person soulcheck    schedule 09.01.2012
comment
@a_horse_with_no_name да, я действительно должен был сказать «недостаточно изолирован», так что хорошая мысль.   -  person soulcheck    schedule 09.01.2012
comment
@a_horse_with_no_name они атомарны в кислотном смысле (это означает, что они либо преуспеют, либо потерпят неудачу), они не атомарны в том смысле, что проверка условия гарантированно произойдет с тем же состоянием данных, что и вставка (это означает, что выполнение запроса на самом деле происходит как минимум в два шага, а не в один неделимый шаг). проблема ОП сохраняется. postgres позаботится о том, чтобы ваш запрос отображал согласованные данные, но все же не гарантирует отсутствие фантомных чтений (как в случае с запросом OP), если не указано иное.   -  person soulcheck    schedule 09.01.2012
comment
@soulcheck: я не знаю, какую СУБД вы использовали до сих пор, но, по крайней мере, в Postgres, Oracle и DB2 выполнение одного оператора является одной атомарной операцией, и все подоператоры будут видеть один и тот же снимок данных (который это версия, которая была действительна при запуске инструкции)   -  person a_horse_with_no_name    schedule 09.01.2012
comment
Люди @a_horse_with_no_name в списках рассылки postgres, похоже, не согласны, если только я их неправильно не понял. вот сообщение Тома Лейна, участника postgres< /а>.   -  person soulcheck    schedule 09.01.2012
comment
@soulcheck: нет, Том на самом деле поддерживает мою точку зрения: будут видны только те строки, которые были зафиксированы до запуска команды. Здесь команда — это оператор всего. Из-за этого INSERT может завершиться ошибкой, так как за это время кто-то еще мог вставить конфликтующую строку.   -  person a_horse_with_no_name    schedule 09.01.2012
comment
@a_horse_with_no_name нет, это не так. Он явно говорит, что это не атомарно. Он не спорит, что означает «атомарный», потому что он предполагает значение из контекста (и это то же самое атомарное, что и в атомарном тесте и установке, а не в ACID-атомном).   -  person soulcheck    schedule 10.01.2012
comment
@a_horse_with_no_name, проверка души: Можем ли мы привлечь третью сторону для обсуждения этого аргумента? Вы оба кажетесь уверенными в своих ответах, и я хотел бы знать, каково последнее слово.   -  person ean5533    schedule 10.01.2012
comment
Пожалуйста, рассмотрите возможность переноса этого разговора в чат.   -  person Tim Post♦    schedule 10.01.2012


Ответы (1)


ИНФОРМАЦИЯ: Это решение не работает. Подробности смотрите в комментариях. Я оставил это здесь, чтобы тем, кто когда-либо ищет решение, не пришлось пробовать и это.

У меня еще не было проблемы, но я предполагаю, что вы можете обойти это, если оптимизируете подзапрос. Вот так (я не проверял, возможно, нужно доработать):

PreparedStatement stmt = conn.prepareStatement("INSERT INTO artwork (md5,image) " +
           "SELECT ?,?" +
           "FROM SYSIBM.SYSDUMMY1 left join artwork exi on ? = md5" +
           "WHERE exi.md5 IS NULL");

stmt.setString(1, key);
stmt.setBinaryStream(2, new ByteArrayInputStream(imageData), imageData.length);
stmt.setString(3, key);
person Angelo Fuchs    schedule 09.01.2012
comment
Привет, спасибо, только что попробовал, и это не сработало. Я думаю, если бы я понял комментарии выше, проблема заключается не в использовании подзапроса, а в использовании запроса для предоставления данных для вставки, и другой поток может добавить строку между первым потоком, выполняющим SELECT и выполняющим INSERT на основе на ВЫБОР. - person Paul Taylor; 09.01.2012
comment
@user294896 user294896 вы правы, у него точно такая же проблема, как и у исходного запроса. - person soulcheck; 09.01.2012
comment
@ user294896 Хорошо, я отредактировал сообщение в своем ответе, чтобы пометить этот ответ как неработающий. - person Angelo Fuchs; 12.01.2012