Я пытаюсь подключить объекты Spring Data JPA вручную, чтобы я мог генерировать прокси-серверы DAO (или репозитории) - без использования контейнера bean-компонента Spring.
Неизбежно, меня спросят, почему я хочу это сделать: это потому, что наш проект уже использует Google Guice (а в пользовательском интерфейсе используется Gin с GWT), и мы не хотим поддерживать другую конфигурацию контейнера IoC, или вытащить все полученные зависимости. Я знаю, что мы могли бы использовать Guice's SpringIntegration
, но это было бы последним средством.
Кажется, что все доступно, чтобы связать объекты вручную, но, поскольку это не очень хорошо задокументировано, у меня трудное время.
Согласно руководству пользователя Spring Data, возможно использование автономных фабрик . К сожалению, пример показывает RepositoryFactorySupport
, который является абстрактным классом. После некоторых поисков мне удалось найти JpaRepositoryFactory
JpaRepositoryFactory
на самом деле работает довольно хорошо, за исключением того, что он не создает автоматически транзакции. Транзакции должны управляться вручную, иначе в базу данных не будет сохранено ничего:
entityManager.getTransaction().begin();
repositoryInstance.save(someJpaObject);
entityManager.getTransaction().commit();
Проблема оказалась в том, что аннотации @Transactional
не используются автоматически и нуждаются в помощи TransactionInterceptor
К счастью, JpaRepositoryFactory
может выполнить обратный вызов, чтобы добавить дополнительные рекомендации AOP в сгенерированный прокси-сервер репозитория перед возвратом:
final JpaTransactionManager xactManager = new JpaTransactionManager(emf);
final JpaRepositoryFactory factory = new JpaRepositoryFactory(emf.createEntityManager());
factory.addRepositoryProxyPostProcessor(new RepositoryProxyPostProcessor() {
@Override
public void postProcess(ProxyFactory factory) {
factory.addAdvice(new TransactionInterceptor(xactManager, new AnnotationTransactionAttributeSource()));
}
});
Здесь дела идут не так хорошо. Проходя через отладчик в коде, TransactionInterceptor
действительно создает транзакцию - но на неправильном EntityManager
. Spring управляет активным EntityManager
, просматривая текущий выполняемый поток. TransactionInterceptor
делает это и видит, что нет активного EntityManager
, связанного с потоком, и решает создать новый.
Однако этот новый EntityManager
- это не тот экземпляр, который был создан и передан в конструктор JpaRepositoryFactory
, для которого требуется EntityManager
. Вопрос в том, как заставить TransactionInterceptor
и JpaRepositoryFactory
использовать одно и то же EntityManager
?
Обновление:
При написании этого я выяснил, как решить проблему, но она все еще не может быть идеальным решением. Я опубликую это решение как отдельный ответ. Я был бы рад услышать любые предложения о том, как лучше использовать Spring Data JPA автономно, чем то, как я его решаю.