Блокировка набора операций с БД в Java
У меня есть требование в моем приложении Java, чтобы выполнить набор операторов БД атомарным & изолированным способом. Например, приложению необходимо прочитать строки данных из одной таблицы и обновить строку данных в другой таблице.
QueryRunner queryRunner = new QueryRunner(); // DBUtils query runner
Object[] params = new Object[] { param };
Connection conn = null;
try {
conn = ...; // get connection
conn.setAutoCommit(false);
result = queryRunner.query(conn, "select x, y, z from table1 where column1 = ?", new BeanHandler<SomeBean>(SomeBean.class), params);
// logic to get value for update
queryRunner.update(conn, "update table2 set p = ? where q = ?", some_value, some_id);
conn.commit();
} catch (SQLException e) {
//
} finally {
DBUtils.closeQuietly(conn);
}
Управление транзакциями достигается установкой автоматической фиксации на false для соединения и последующей явной фиксации, как показано выше. Но приведенный выше код также может быть выполнен в многопоточной среде, и я также хочу, чтобы два оператора БД (выбор и обновление) выполнялись как единое целое.
У меня есть идея использовать общий объект Java Lock в этом методе, как показано ниже.
В классе,
private Lock lock = new ReentrantLock(); // member variable
В методе
lock.lock();
try {
conn = ...; // get connection
conn.setAutoCommit(false);
result = queryRunner.query(conn, "select x, y, z from table1 where column1 = ?", new BeanHandler<SomeBean>(SomeBean.class), params);
// logic to get value for update
queryRunner.update(conn, "update table2 set p = ? where q = ?", some_value, some_id);
conn.commit();
} finally {
DBUtils.closeQuietly(conn);
lock.unlock();
}
Кажется, что-то способное решить проблему. Однако мне интересно, является ли это наилучшей практикой и есть ли лучшая альтернатива (например, фреймворки) для этого?
4 ответа
Единственный способ достичь требуемого атома - это использовать хранимую процедуру в базе данных, чтобы изолировать данные и сразу заблокировать их. Блокировка на уровне Java не может сделать то же самое, что блокировка в базе данных.
Другой способ справиться с такими проблемами - использовать уровень изоляции сериализуемых транзакций для всех транзакций базы данных. Это приводит к тому, что любой набор транзакций ведет себя так, как если бы они выполнялись по одной за раз, фактически не заставляя их запускаться по одной за раз. Если вы сделаете это, вы должны использовать платформу, которая перехватывает ошибки сериализации (SQLState 40001) и повторяет транзакции. Большим плюсом является то, что вам не нужно беспокоиться о конкретных взаимодействиях между транзакциями - если транзакция делает правильные вещи, когда выполняется только она, она будет делать правильные вещи в любом сочетании транзакций.
Обратите внимание, что все транзакции должны быть сериализуемыми, чтобы это работало так просто.
Я предлагаю, чтобы база данных управляла этими блокировками вместо вас. Это обрабатывает случай, когда несколько JVM выполняют код. Упомянутые вами механизмы блокировки могут быть эффективными только в одной JVM.
Способ сделать это состоит в том, чтобы сделать SELECT ... FOR UPDATE
, Это установит блокировку выбранных строк, и блокировка будет снята, когда ваша транзакция будет зафиксирована или откатана. Это лучше, чем блокировка на уровне таблицы, потому что эти строки все еще могут быть прочитаны другими транзакциями, которые просто хотят прочитать текущее значение, но не обновляют их. Если другая транзакция пытается получить FOR UPDATE
заблокировать, затем он будет блокировать, пока не закончится первый.
Насколько я понимаю, вы хотите сделать этот блок кода, содержащий операторы выбора и обновления, безопасным для потока? Для этого используется синхронизированное ключевое слово. Хотя этот вопрос задан давно, я просто хочу отметить это здесь. поместите эти строки кода в синхронизированный блок.