Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you use Spring Data JPA outside of a Spring Container?

I'm trying to wire up Spring Data JPA objects manually so that I can generate DAO proxies (aka Repositories) - without using a Spring bean container.

Inevitably, I will be asked why I want to do this: it is because our project is already using Google Guice (and on the UI using Gin with GWT), and we don't want to maintain another IoC container configuration, or pull in all the resulting dependencies. I know we might be able to use Guice's SpringIntegration, but this would be a last resort.

It seems that everything is available to wire the objects up manually, but since it's not well documented, I'm having a difficult time.

According to the Spring Data user's guide, using repository factories standalone is possible. Unfortunately, the example shows RepositoryFactorySupport which is an abstract class. After some searching I managed to find JpaRepositoryFactory

JpaRepositoryFactory actually works fairly well, except it does not automatically create transactions. Transactions must be managed manually, or nothing will get persisted to the database:

entityManager.getTransaction().begin(); repositoryInstance.save(someJpaObject); entityManager.getTransaction().commit(); 

The problem turned out to be that @Transactional annotations are not used automatically, and need the help of a TransactionInterceptor

Thankfully, the JpaRepositoryFactory can take a callback to add more AOP advice to the generated Repository proxy before returning:

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()));     } }); 

This is where things are not working out so well. Stepping through the debugger in the code, the TransactionInterceptor is indeed creating a transaction - but on the wrong EntityManager. Spring manages the active EntityManager by looking at the currently executing thread. The TransactionInterceptor does this and sees there is no active EntityManager bound to the thread, and decides to create a new one.

However, this new EntityManager is not the same instance that was created and passed into the JpaRepositoryFactory constructor, which requires an EntityManager. The question is, how do I make the TransactionInterceptor and the JpaRepositoryFactory use the same EntityManager?

Update:

While writing this up, I found out how to solve the problem but it still may not be the ideal solution. I will post this solution as a separate answer. I would be happy to hear any suggestions on a better way to use Spring Data JPA standalone than how I've solve it.

like image 207
codemaven Avatar asked Feb 03 '12 05:02

codemaven


People also ask

Where do we use Spring Data JPA?

Spring Data JPA API provides JpaTemplate class to integrate spring application with JPA. JPA (Java Persistent API) is the sun specification for persisting objects in the enterprise application. It is currently used as the replacement for complex entity beans.

How does Spring Data JPA works internally?

Spring then parses the method name and creates a query for it. Here is a simple example of a query that loads a Book entity with a given title. Internally, Spring generates a JPQL query based on the method name, sets the provided method parameters as bind parameter values, executes the query and returns the result.

Can we use Spring Data JPA with spring MVC?

In this tutorial, we will use a Java-based spring configuration to configure Spring MVC 5, Spring Data JPA, Hibernate 5 and MySQL, etc. Spring Data JPA provides CRUD API, so you don't have to write boilerplate code. You just need to create a repository interface and spring will provide implementation automatically.

Is Spring Data JPA part of spring boot?

Spring Boot Starter Data JPASpring Boot provides starter dependency spring-boot-starter-data-jpa to connect Spring Boot application with relational database efficiently.


1 Answers

The general principle behind the design of JpaRepositoryFactory and the according Spring integration JpaRepositoryFactory bean is the following:

We're assuming you run your application inside a managed JPA runtime environment, not caring about which one.

That's the reason we rely on injected EntityManager rather than an EntityManagerFactory. By definition the EntityManager is not thread safe. So if dealt with an EntityManagerFactory directly we would have to rewrite all the resource managing code a managed runtime environment (just like Spring or EJB) would provide you.

To integrate with the Spring transaction management we use Spring's SharedEntityManagerCreator that actually does the transaction resource binding magic you've implemented manually. So you probably want to use that one to create EntityManager instances from your EntityManagerFactory. If you want to activate the transactionality at the repository beans directly (so that a call to e.g. repo.save(…) creates a transaction if none is already active) have a look at the TransactionalRepositoryProxyPostProcessor implementation in Spring Data Commons. It actually activates transactions when Spring Data repositories are used directly (e.g. for repo.save(…)) and slightly customizes the transaction configuration lookup to prefer interfaces over implementation classes to allow repository interfaces to override transaction configuration defined in SimpleJpaRepository.

like image 182
Oliver Drotbohm Avatar answered Oct 03 '22 05:10

Oliver Drotbohm