Старая ситуация «@Transactional из одного класса»

Synopsis of the original question: Используя стандартные транзакции Spring с прокси-сервером AOP, невозможно вызвать метод @ Transactional-отмеченный из не-@ Transactional-отмеченного метода в том же классе и находиться внутри транзакции (в частности, из-за вышеупомянутого прокси). Предположительно, это возможно с Spring Transactions в режиме AspectJ, но как это сделать?

Edit: Полное краткое изложение для Spring транзакций в режиме AspectJ с использованиемLoad-Time Плетение:

Добавьте следующее кMETA-INF/spring/applicationContext.xml:

<tx:annotation-driven mode="aspectj" />

<context:load-time-weaver />

(Я предполагаю, что у вас уже естьAnnotationSessionFactoryBean иHibernateTransactionManager настроить в контексте приложения. Можете добавитьtransaction-manager="transactionManager" как атрибут вашего<tx:annotation-driven /> тег, но если значение вашего компонента управления транзакциямиid атрибут на самом деле & quot;transactionManager", тогда это избыточно, как"transactionManager& Quot; значение этого атрибута по умолчанию.)

добавлятьMETA-INF/aop.xml, Содержание выглядит следующим образом:

<aspectj>
  <aspects>
    <aspect name="org.springframework.transaction.aspectj.AnnotationTransactionAspect" />
  </aspects>
  <weaver>
    <include within="my.package..*" /><!--Whatever your package space is.-->
  </weaver>
</aspectj>

добавлятьaspectjweaver-1.7.0.jar а такжеspring-aspects-3.1.2.RELEASE.jar на вашclasspath, Я использую Maven в качестве инструмента сборки, поэтому вот<dependency /> объявления для вашего проектаPOM.xml файл:

<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjweaver</artifactId>
  <version>1.7.0</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-aspects</artifactId>
  <version>3.1.2.RELEASE</version>
</dependency>

spring-instrument-3.1.2.RELEASE.jar не нужен как<dependency /> на вашеclasspath, но это все еще нужноsomewhere так что вы можете указать на это с-javaagent Флаг JVM, следующим образом:

-javaagent:full\path\of\spring-instrument-3.1.2.RELEASE.jar

Я работаю в Eclipse Juno, поэтому чтобы установить это, я выбрал Window - & gt; Настройки - & gt; Java - & gt; Установленные JRE. Затем я щелкнул отмеченную JRE в списке и нажал кнопку & quot; Изменить ... & quot; Кнопка справа от списка. Третье текстовое поле в появившемся всплывающем окне помечено как «Аргументы виртуальной машины по умолчанию:». Это где-javaagent флаг должен быть набран или скопировать + вставить.

Теперь для моих реальных классов тестового кода. Во-первых, мой основной класс,TestMain.java:

package my.package;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestMain {
  public static void main(String[] args) {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("META-INF/spring/applicationContext.xml");
    TestClass testClass = applicationContext.getBean(TestClass.class);
    testClass.nonTransactionalMethod();
  }
}

И тогда мой транзакционный класс,TestClass.java:

package my.package;

import my.package.TestDao;
import my.package.TestObject;
import org.springframework.transaction.annotation.Transactional;

public void TestClass {
  private TestDao testDao;

  public void setTestDao(TestDao testDao) {
    this.testDao = testDao;
  }

  public TestDao getTestDao() {
    return testDao;
  }

  public void nonTransactionalMethod() {
    transactionalMethod();
  }

  @Transactional
  private void transactionalMethod() {
    TestObject testObject = new TestObject();
    testObject.setId(1L);
    testDao.save(testObject);
  }
}

Хитрость в том, что еслиTestClass это поле вTestMain его класс будет загруженClassLoader перед загрузкой контекста приложения. Поскольку переплетение происходит во время загрузки класса, и это переплетение выполняется Spring через контекст приложения, оно не будет соткано, потому что класс уже загружен до того, как контекст приложения загружен и узнает об этом.

Дальнейшие подробностиTestObject а такжеTestDao неважны. Предположим, что они связаны с аннотациями JPA и Hibernate и используют Hibernate для постоянства (потому что они есть, и они делают), и что все необходимое<bean />в файле контекста приложения.

Edit: Полное краткое изложение для Spring транзакций в режиме AspectJ с использованиемCompile-Time Плетение:

Добавьте следующее кMETA-INF/spring/applicationContext.xml:

<tx:annotation-driven mode="aspectj" />

(Я предполагаю, что у вас уже естьAnnotationSessionFactoryBean иHibernateTransactionManager настроить в контексте приложения. Можете добавитьtransaction-manager="transactionManager" как атрибут вашего<tx:annotation-driven /> тег, но если значение вашего компонента управления транзакциямиid атрибут на самом деле & quot;transactionManager", тогда это избыточно, как"transactionManager& Quot; значение этого атрибута по умолчанию.)

добавлятьspring-aspects-3.1.2.RELEASE.jar а такжеaspectjrt-1.7.0.jar на вашclasspath, Я использую Maven в качестве инструмента для сборки, поэтому здесь<dependency /> декларации дляPOM.xml файл:

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-aspects</artifactId>
  <version>3.1.2.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjrt</artifactId>
  <version>1.7.0</version>
</dependency>

В Eclipse Juno: Справка - & gt; Торговая площадка Eclipse - & gt; текстовое поле с надписью & quot; Поиск: & quot; - & GT; введите & quot; ajdt & quot; - & GT; нажмите [Enter] - & gt; & quot; Инструменты разработки AspectJ (Juno) & quot; - & GT; Установить - & gt; И т.п.

После перезапуска Eclipse (это сделает вас) щелкните правой кнопкой мыши ваш проект, чтобы вызвать контекстное меню. Посмотрите внизу: Настроить - & gt; Конвертировать в AspectJ Project.

Добавьте следующее<plugin /> декларация в вашемPOM.xml (опять с мавеном!)

<plugin>
  <groupId>org.codehaus.mojo</groupId>
  <artifactId>aspectj-maven-plugin</artifactId>
  <version>1.4</version>
  <configuration>
    <aspectLibraries>
      <aspectLibrary>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
      </aspectLibrary>
    </aspectLibraries>
  </configuration>
  <executions>
    <execution>
      <goals>
        <goal>compile</goal>
        <goal>test-compile</goal>
      </goals>
    </execution>
  </executions>
</plugin>

Альтернатива: щелкните правой кнопкой мыши свой проект, чтобы вызвать контекстное меню. Посмотрите внизу: Инструменты AspectJ - & gt; Настроить AspectJ Build Path - & gt; Вкладка Aspect Path - & gt; нажмите & quot; Добавить внешние файлы JAR ... & quot; - & GT; найдитеfull/path/of/spring-aspects-3.1.2.RELEASE.jar - & GT; нажмите & quot; Открыть & quot; - & GT; нажмите "ОК".

Если вы взяли маршрут Maven, то<plugin /> выше должно быть псих. Чтобы это исправить: Справка - & gt; Установить новое программное обеспечение ... - & gt; нажмите & quot; Добавить ... & quot; - & GT; введите все, что вам нравится, в текстовое поле с надписью & quot; Имя: & quot; - & GT; введите или скопируйте + вставьтеhttp://dist.springsource.org/release/AJDT/configurator/ в текстовом поле с надписью & quot; Местоположение: & quot; - & GT; нажмите "ОК" - & GT; Подождите секунду - & gt; установите родительский флажок рядом с «Интеграция Maven для Eclipse AJDT Integration». - & GT; нажмите & quot; Далее & gt; & quot; - & GT; Установить - & gt; И т.п.

Когда плагин установлен, и вы перезапустили Eclipse, ошибки в вашемPOM.xml файл должен был уйти. Если нет, щелкните правой кнопкой мыши свой проект, чтобы открыть контекстное меню: Maven - & gt; Обновить проект - & gt; нажмите "ОК".

Теперь для моего реального класса тестового кода. Только один раз,TestClass.java:

package my.package;

import my.package.TestDao;
import my.package.TestObject;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.transaction.annotation.Transactional;

public void TestClass {
  private TestDao testDao;

  public void setTestDao(TestDao testDao) {
    this.testDao = testDao;
  }

  public TestDao getTestDao() {
    return testDao;
  }

  public static void main(String[] args) {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("META-INF/spring/applicationContext.xml");
    TestClass testClass = applicationContext.getBean(TestClass.class);
    testClass.nonTransactionalMethod();
  }

  public void nonTransactionalMethod() {
    transactionalMethod();
  }

  @Transactional
  private void transactionalMethod() {
    TestObject testObject = new TestObject();
    testObject.setId(1L);
    testDao.save(testObject);
  }
}

В этом нет уловки; поскольку переплетение происходит во время компиляции, то есть до загрузки класса и загрузки контекста приложения, порядок этих двух вещей больше не имеет значения. Это означает, что все может идти в одном классе. В Eclipse ваш код постоянно перекомпилируется каждый раз, когда вы нажимаете кнопку Сохранить (когда-нибудь задумывались над тем, что он делал, когда он говорит «Создание рабочего пространства: (XX%)»), так что он сплетен и готов к работе всякий раз, когда ты.

Как в примере с Load-Time: дальнейшие подробностиTestObject а такжеTestDao неважны. Предположим, что они связаны с аннотациями JPA и Hibernate и используют Hibernate для постоянства (потому что они есть, и они делают), и что все необходимое<bean />в файле контекста приложения.

Ответы на вопрос(1)

Ваш ответ на вопрос