2012-04-04 4 views
2

У меня есть требование в моем приложении 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); 
} 

Управление транзакциями достигается путем установки auto commit в false для соединения и в явной фиксации позже, как показано выше. Но приведенный выше код также может быть выполнен в многопоточной среде, и я также хочу, чтобы два оператора БД (выберите & update), которые будут выполняться в целом исключительно взаимно.

У меня есть идея использовать общий объект 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(); 
} 

кажется своего рода способна решить эту проблему. Тем не менее, мне интересно, является ли это лучшей практикой и есть ли для этого лучшая альтернатива (например, рамки)?

ответ

1

Мое предложение состоит в том, чтобы база данных управляла этими замками вместо вас. Это обрабатывает случай, когда несколько JVM запускают код. Указанный механизм блокировки может быть эффективен только в одном JVM.

Способ выполнить это, чтобы сделать SELECT ... FOR UPDATE. Это приведет к блокировке выбранных строк, и блокировка будет освобождена при совершении или откате транзакции. Это лучше, чем блокировка на уровне таблицы, поскольку эти строки все еще могут быть прочитаны другими транзакциями, которые просто хотят прочитать текущее значение, но не обновлять их. Если другая транзакция пытается получить блокировку FOR UPDATE, она будет блокироваться до завершения первого.

+0

Спасибо, Hiro2k. Я считаю, что наше приложение работает в контейнере сервлетов и будет находиться в одной JVM. «SELECT ... FOR UPDATE» может не работать в моем случае, поскольку я обновляю строку в таблице, отличной от таблицы, в которой работает оператор select. – Chen

+0

Правильно, но если вы когда-нибудь решите добавить еще один экземпляр сервера для балансировки нагрузки или масштабирования, тогда вы столкнетесь с этой проблемой. Вы можете добавить 'SELECT ... FOR UPDATE' в строку во второй таблице в самом начале вашего метода, чтобы другим пришлось подождать, пока она не закончится. – Hiro2k

+0

Хорошая точка, @ Hiro2k. Я буду оценивать наши варианты использования на моем конце, чтобы увидеть, можно ли их охватить. – Chen

1

Единственный способ, которым вы будете добиваться атомизации, которую вы требуете, - это использовать хранимую процедуру в базе данных, чтобы изолировать данные и заблокировать их все сразу. Блокировка на уровне Java не может делать то, что блокировка в базе данных может так же легко.

+0

Мое предложение работает только в том случае, если соответствующие данные обновляются через вызовы метода на одном объекте в одной JVM. Он не блокирует уровень базы данных как таковой. Тем не менее, это не предотвращает другие неожиданные обновления одного и того же фрагмента данных, инициированного где-то в другом месте (например, другие вызовы методов, другое приложение). Можно ли это смягчить, используя хранимую процедуру? – Chen

1

Другой способ, с помощью которого вы можете справиться с такими проблемами, - это использовать уровень изоляции транзакций для всех транзакций базы данных. Это приводит к тому, что любой набор транзакций ведет себя так, как если бы они запускались по одному за раз, не заставляя их запускаться по одному. Если вы это сделаете, вы должны использовать фреймворк, который улавливает ошибки сериализации (SQLState 40001) и повторяет транзакции. Большая сторона заключается в том, что вам не нужно беспокоиться о конкретных взаимодействиях между транзакциями - если транзакция делает правильные вещи, когда это единственное, что работает, она будет делать все правильно в любом сочетании транзакций.

Обратите внимание, что все транзакции должны быть сериализованы, чтобы это работало так просто.

0

Насколько я понимаю, вы просто хотите сделать этот блок кода, содержащий операции выбора и обновления, как Thread safe? Для этого используется синхронизированное ключевое слово. Несмотря на то, что этот вопрос задают давно, я просто хочу его здесь запомнить. поместите эти строки кода под синхронизированный блок.

 Смежные вопросы

  • Нет связанных вопросов^_^