Как я могу получить Spring JdbcTemplate для read_uncommitted?

Во-первых, я не могу использовать декларативный подход @Transactional, так как приложение имеет несколько источников данных JDBC, я не хочу утомлять деталями, но достаточно сказать, что методу DAO передается правильный источник данных для выполнения логика. Все источники данных JDBC имеют одинаковую схему, они разделены, так как я предоставляю остальные службы для системы ERP.

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

Используя JDBC, я бы выполнил следующее:

private Customer getCustomer(DataSource ds, String id) {
    Customer c = null;
    PreparedStatement stmt = null;
    Connection con = null;
    try {
        con = ds.getConnection();
        con.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
        stmt = con.prepareStatement(SELECT_CUSTOMER);
        stmt.setString(1, id);
        ResultSet res = stmt.executeQuery();
        c = buildCustomer(res);
    } catch (SQLException ex) {
        // log errors
    } finally {
        // Close resources
    }
    return c;
}

Ладно, много шаблонного, я знаю. Итак, я попробовал JdbcTemplate, так как использую Spring.

Использовать шаблон Jdbc

private Customer getCustomer(JdbcTemplate t, String id) {
    return t.queryForObject(SELECT_CUSTOMER, new CustomerRowMapper(), id);
}

Гораздо лучше, но по-прежнему используется изоляция транзакций по умолчанию. Мне нужно как-то это изменить. Поэтому я подумал об использовании TransactionTemplate.

private Customer getCustomer(final TransactionTemplate tt,
                             final JdbcTemplate t,
                             final String id) {
    return tt.execute(new TransactionCallback<Customer>() {
        @Override
        public Customer doInTransaction(TransactionStatus ts) {
            return t.queryForObject(SELECT_CUSTOMER, new CustomerRowMapper(), id);
        }
    });
}

Но как здесь установить изоляцию транзакций? Я не могу найти его ни в обратном вызове, ни в TransactionTemplate, чтобы сделать это.

Я читаю Spring в действии, третье издание, в котором объясняется, насколько я это сделал, хотя в главе о транзакциях продолжается использование декларативных транзакций с аннотациями, но, как уже упоминалось, я не могу использовать это, поскольку мой DAO должен определить в runtime, какой источник данных использовать на основе предоставленных аргументов, в моем случае код страны.

Любая помощь будет принята с благодарностью.


person Brett Ryan    schedule 27.07.2012    source источник


Ответы (3)


Использование TransactionTemplate поможет вам в этом, вам нужно настроить его соответствующим образом. Шаблон транзакции также содержит конфигурацию транзакции. На самом деле TransactionTemplate расширяет DefaultTransactionDefinition.

Итак, где-то в вашей конфигурации у вас должно быть что-то вроде этого.

<bean id="txTemplate" class=" org.springframework.transaction.support.TransactionTemplate">
  <property name="isolationLevelName" value="ISOLATION_READ_UNCOMMITTED"/>
  <property name="readOnly" value="true" />
  <property name="transactionManager" ref="transactionManager" />
</bean>

Если вы затем введете этот bean-компонент в свой класс, вы сможете использовать код на основе TransactionTemplate, который вы разместили/попробовали ранее.

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

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

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

person M. Deinum    schedule 21.08.2013
comment
Означает ли это, что мне нужно будет настроить несколько txTemplate для каждой базы данных и каждой изоляции? Итак, если бы я хотел изоляцию для записи и грязную изоляцию только для чтения с 6 базами данных, мне потребовалось бы 12 txTemplates? - person Brett Ryan; 22.08.2013
comment
Либо это, либо создавайте их самостоятельно, когда это необходимо, это потребует от вас перехода в менеджер транзакций и соответствующей настройки конфигурации. Я предполагаю, что лучшим решением является использование AbstractRoutingDataSource таким образом, чтобы вы могли использовать декларативное управление транзакциями Springs (и при выполнении очищать свой код). - person M. Deinum; 22.08.2013

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

private Customer getCustomer(final DataSourceTransactionManager txMan,
                             final JdbcTemplate t,
                             final String id) {
    DefaultTransactionDefinition def = new DefaultTransactionDefinition();
    def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_UNCOMMITTED);

    TransactionStatus status = txMan.getTransaction(def);
    Customer c = null;
    try {
        c = t.queryForObject(SELECT_CUSTOMER, new CustomerRowMapper(), id);
    } catch (Exception ex) {
        txMan.rollback(status);
        throw ex;
    }
    txMan.commit(status);
    return c;
}

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

См. Spring. 3.1.x Документация — Глава 11 — Управление транзакциями

person Brett Ryan    schedule 27.07.2012
comment
Это старый вопрос и ответ, поэтому я просто подумал, есть ли новый способ решения проблемы, которую вы описали. Я думаю, что новый атрибут диспетчера транзакций в новой версии аннотации транзакций решает проблему Транзакционная Spring 3.1 - person Sabir Khan; 12.09.2018

Определите прокси-источник данных, класс org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy и установите уровень изоляции транзакций. Введите фактический источник данных либо через установщик, либо через конструктор.

<bean id="yourDataSource" class="org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy">
    <constructor-arg index="0" ref="targetDataSource"/>
    <property name="defaultTransactionIsolationName" value="TRANSACTION_READ_UNCOMMITTED"/>
</bean>
person Satish    schedule 10.03.2017