Управляемые транзакции Spring, EclipseLink JPA, пользовательский уровень изоляции

#java #spring #jpa #transactions #eclipselink

#java #spring #jpa #транзакции #eclipselink

Вопрос:

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

У меня есть приложение Spring с транзакциями, управляемыми Spring. Он использует EclipseLink JPA. У меня есть метод, который выполняет a findByNativeQuery() , за которым следует a merge() . Мне нужно, чтобы это происходило на реальном уровне изоляции последовательных транзакций. Я попытался добавить @Transactional(isolation=Isolation.SERIALIZABLE)

Это не работает, поскольку org.springframework.orm.jpa.vendor.EclipseLinkJpaDialect#beginTransaction не поддерживает какой-либо уровень изоляции транзакций, кроме уровня по умолчанию. Итак, я попытался добраться до внутренних компонентов ElcipseLink и запустить / завершить свои собственные транзакции, но затем я получаю сообщение об ошибке:

 "java.lang.IllegalStateException : Not allowed to create transaction on shared EntityManager - use Spring transactions or EJB CMT instead
  

Что, конечно, имеет смысл… но что мне делать??

Комментарии:

1. Взгляните на это . Я сам не пробовал, но, похоже, это хорошее место для начала.

2. Я думаю, что JPA не поддерживает пользовательские уровни изоляции. Это ограничение JPA, а не Spring. Может потребоваться реализовать пользовательский JpaDialect

Ответ №1:

Я попробовал это, но я не совсем уверен в решении. Я взял код из этого блога и адаптировал его для EclipseLink. Вот код:

 package com.byteslounge.spring.tx.dialect;

import java.sql.SQLException;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceException;

import org.eclipse.persistence.sessions.UnitOfWork;
import org.springframework.orm.jpa.vendor.EclipseLinkJpaDialect;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionException;

public class CustomEclipseLinkJpaDialect extends EclipseLinkJpaDialect {

    private static final long serialVersionUID = 1L;

    private boolean lazyDatabaseTransaction = false;

    @Override
    public void setLazyDatabaseTransaction(boolean lazyDatabaseTransaction) {
        this.lazyDatabaseTransaction = lazyDatabaseTransaction;
    }

    @Override
    public Object beginTransaction(final EntityManager entityManager,
            final TransactionDefinition definition)
            throws PersistenceException, SQLException, TransactionException {

        UnitOfWork uow = (UnitOfWork) getSession(entityManager);
        uow.getLogin().setTransactionIsolation(definition.getIsolationLevel());

        entityManager.getTransaction().begin();
        if (!definition.isReadOnly() amp;amp; !lazyDatabaseTransaction) {
            uow.beginEarlyTransaction();
        }

        return null;
    }
}
  

Я вижу, что СЕРИАЛИЗУЕМАЯ изоляция регистрируется при инициировании транзакции, но это необходимо правильно протестировать, чтобы подтвердить, что она работает.

Комментарии:

1. Я отладил свой код, вошел в JpaTransactionManager.doBegin() метод и проверил ConnectionHandle его получение, чтобы найти фактический com.mysql.jdbc.JDBC4Connection экземпляр MySQL. Фактическое используемое соединение фактически использует уровень изоляции, установленный для этого метода (javax.sql.Connection. TRANSACTION_SERIALIZABLE).

2. У нас не было времени протестировать его, но я полагаю, что я бы дал щедрость, поскольку это единственный ответ, требующий значительных усилий. Будущий читатель остерегается — этот принятый ответ еще не был оценен на правильность.

3. Я попробовал это. Пожалуйста, просветите меня .. будет ли это применимо, если определение транзакции как PROPAGATION_REQUIRED и ISOLATION_SERIALIZABLE приведет к созданию всех транзакций, созданных из другого потока, <b> ПОДОЖДИТЕ </b>, если уже выполняется транзакция с тем же определением? ну, если я хочу этого добиться, что нужно изменить?

4. Работает для меня PROPAGATION_REQUIRED с Spring 3.2.13. Спасибо.

Ответ №2:

Поддержка пользовательского уровня изоляции была добавлена в Spring 4.1.2 в EclipseLinkJpaDialect

Ответ №3:

Вы можете обратиться

«Для достижения сериализуемой изоляции транзакций с помощью EclipseLink мы рекомендуем использовать изолированный клиентский сеанс следующим образом:

Настройте изоляцию транзакций базы данных как сериализуемую. Настройте объекты как изолированные (см. раздел Настройка изоляции кэша на уровне проекта или Настройка изоляции кэша на уровне дескриптора). Используйте метод UnitOfWork beginTransactionEarly (см. Метод Unit of Work beginTransactionEarly). Если вас беспокоит только аспект записи serializable, достаточно оптимистичной блокировки «.

в http://docs.oracle.com/middleware/1212/toplink/OTLCG/cache.htm или пройти через http://docs.oracle.com/middleware/1212/toplink/OTLCG/cache.htm если какой-либо из уровней изоляции соответствует вашим требованиям