Я пытаюсь подключить объекты JPA Spring Data вручную, чтобы я мог генерировать прокси DAO (также известные как репозитории) - без использования контейнера bean-компонентов Spring.
Меня неизбежно спросят, почему я хочу это сделать: это потому, что наш проект уже использует Google Guice (и в пользовательском интерфейсе с использованием Gin с GWT), и мы не хотим поддерживать другую конфигурацию контейнера IoC, или извлеките все полученные зависимости. Я знаю, что мы могли бы использовать Guice SpringIntegration
, но это было бы крайней мерой.
Кажется, что все доступно для подключения объектов вручную, но поскольку это плохо документировано, у меня возникают трудности.
Согласно руководству пользователя Spring Data, использование фабрик репозиториев автономно возможно. К сожалению, в примере показан абстрактный класс RepositoryFactorySupport
. После некоторого поиска мне удалось найти JpaRepositoryFactory
JpaRepositoryFactory
действительно работает довольно хорошо, за исключением того, что он не создает транзакции автоматически.Транзакциями необходимо управлять вручную, иначе в базе данных ничего не сохранится:
entityManager.getTransaction().begin();
repositoryInstance.save(someJpaObject);
entityManager.getTransaction().commit();
Проблема оказалась в том, что аннотации @Transactional
не используются автоматически и требуют помощи TransactionInterceptor
К счастью, JpaRepositoryFactory
может принять обратный вызов, чтобы добавить дополнительные рекомендации АОП к сгенерированному прокси-серверу репозитория перед возвратом:
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, чем то, как я ее решил.