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

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.

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.

I solved this by manually binding the EntityManager and EntityManagerFactory to the executing thread, before creating repositories with the JpaRepositoryFactory. This is accomplished using the TransactionSynchronizationManager.bindResource method:
emf = Persistence.createEntityManagerFactory("com.foo.model", properties);
em = emf.createEntityManager();
// Create your transaction manager and RespositoryFactory
final JpaTransactionManager xactManager = new JpaTransactionManager(emf);
final JpaRepositoryFactory factory = new JpaRepositoryFactory(em);
// Make sure calls to the repository instance are intercepted for annotated transactions
factory.addRepositoryProxyPostProcessor(new RepositoryProxyPostProcessor() {
#Override
public void postProcess(ProxyFactory factory) {
factory.addAdvice(new TransactionInterceptor(xactManager, new MatchAlwaysTransactionAttributeSource()));
}
});
// Create your repository proxy instance
FooRepository repository = factory.getRepository(FooRepository.class);
// Bind the same EntityManger used to create the Repository to the thread
TransactionSynchronizationManager.bindResource(emf, new EntityManagerHolder(em));
try{
repository.save(someInstance); // Done in a transaction using 1 EntityManger
} finally {
// Make sure to unbind when done with the repository instance
TransactionSynchronizationManager.unbindResource(getEntityManagerFactory());
}
There must be be a better way though. It seems strange that the RepositoryFactory was designed to use EnitiyManager instead of an EntityManagerFactory. I would expect, that it would first look to see if an EntityManger is bound to the thread and then either create a new one and bind it, or use an existing one.
Basically, I would want to inject the repository proxies, and expect on every call they internally create a new EntityManager, so that calls are thread safe.

Related

Async Transactional Saves do not work in Spring after upgrade

We upgraded Spring Boot from 1.5.8 to 2.6.2. It has introduced a problem that has us perplexed: Transactional saves are not processing from inside spawned threads.
We use JPA managed entities on a Mysql database and make calls down to the CrudRepository to save them.
Transactions inside the main thread work fine. However, when called from an asynchronous operation things go awry:
both async and sync calls go through the Spring SimpleJpaRepository.save() method. But the entityManager returns the object to persist with a null id in the case of the async operation.
I followed the flow through in both types of calls and can see that the save propagates down to the org.hibernate.internal.SessionImpl service.
From there it makes its way to AbstractSaveEventListener.class and that is where the discrepancy appears to be. In the performSaveOrReplicate method (hibernate-core:5.6.3), inTrx boolean is false in the async workflow whereas it is true in the synch one. Because of that the shouldDelayIdentityInserts flag gets set and an id does not appear to be generated for any entities in this thread.
We have tried different things to get this to work. For example, we used the transactionTemplate to have some specific control here, but that has not changed the behavior.
We were originally creating this async process by using the ApplicationEventPublisher to create an event. We also tried using completablefuture and other constructs with the same result as well as annotating the method with #Async and calling it directly.
The issue was that, with the upgrade to Spring Boot 2.6, Spring Batch implements a new Transaction Manager.
What we didn't realize is that this transaction manager was being autowired into our other services and did not work in this threaded context. You do not want to share a Batch processing Tx Manager with your API/misc services. Declaring a specific Transaction Manager there to keep them separate solved the issue.
Here is an example marking a PlatformTransactionManager with the Primary annotation to test its usage explicitly.
#Primary
#Bean
public PlatformTransactionManager platformTransactionManager(DataSource dataSource) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory(dataSource).getObject());
return transactionManager;
}

What is the replacement of EJB SessionContext object in spring boot?

I am migrating an EJB project to Spring boot project. I have successfully replaced other annotations to the spring annotation, but havving problem with SessionContext object.
My legacy code is bellow
#Resource
SessionContext sessionContext;
.....
if (some condition) {
sessionContext.setRollbackOnly();
return false;
}
For this code i am getting the following error
A component required a bean of type 'javax.ejb.SessionContext' that could not be found.
Action:
Consider defining a bean of type 'javax.ejb.SessionContext' in your configuration.
I think you'll have to use a few different functionalities.
setRollbackOnly()
Most often I have seen Session Context used for Rollbacks. In Spring, you can replace this with:
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
or annotate class with
#Transactional(rollbackFor = MyException.class)
so you can throw your exception from class to cause rollback.
getBusinessObject()
The second most commonly used feature is method to load a business object so that I can, for example, create a new transaction within a same bean. In this case you can use Self-inject:
#Lazy private final AccountService self;
and annote method with #Transactional. This, of course, solves any other cases where you need to use the power of a proxy object.
Other functionality is provided by other classes in Spring, but I think that these two are the most commonly used in the Java EE world and when migrating, one will look to replace them in Spring.

Spring - how to debug to find specific use of spring's auto-wiring or a request parameter?

Spring is auto-wiring in a request parameter - let's call it "bob".
I don't know where nor how it is doing this, so I cannot debug it. What spring specific code (using intellij, so I can at lest set a conditional) would be appropriate to find where the auto-wiring of the request parameter is happening, so I can work out what the system is doing?
I think I understood the question, so I will try to answer it as best as I can.
You are facing a dilemma of choosing between managing your instances, or letting Spring manage them. If you let Spring manage dependency injection, you will often face situations where you wish you had more fine control over the beans lifecycle.
By default, Spring beans are "singletons", which means that only one
instance of that object will be created, and every class that demands
a dependency injection of that object will receive the same instance.
The first step on beans lifecycle is its construction. You can setup a breakpoint to catch that moment on any method annotated with #PostConstruct. This article describes the need of running some code on bean initialization, and how it is solved by this annotation. For example:
public class AnyBean {
#PostConstruct
public void init(){
// any code or breakpoints inserted here will
// be run whenever an instance of this bean is created.
// if a singleton bean, only one instance is created and,
// only one #PostConstruct will be called.
// If a bean is a prototype bean, a new instance will be created
// for every dependency injection, and hence one #PostConstruct
// will be called for each.
}
}

Invoking proxied DAO methods from Spring stand alone client :- could not initialize proxy - no Session

I have a third party jar in my class path which has some services and DAO's developed on top of Spring 2.0.6 and Hibernate3.2.6. I need to call some of the services and daos.
Using ClassPathXmlApplicationContext I'm able to load the application context and able to access the services and daos. Both the service and dao are following ProxyFactoryBean pattern.
Problem comes when I'm accessing a DAO which has some single valued associations.When I'm accessing associated entity I'm getting lazy initialization problem.
To solve this problem:- If it is in my own application JAR I'll be able to change the fetch type into join or in DAOImpl method I could use Hibernate.initialize().
Is there a way to avoid this problem from the stand alone code itself? Or any other way to solve this issue without modifying applicationContext.xml and DAOImpl
You need to put the caller method into one single transaction.
If you have Spring transactional environment, you can put the call of the DAO services/repositories in your own service/method which is marked as #Transactional, or if transaction support is not enabled, but you still have spring support in your application, you can just use TransactionTemplate directly, provided by spring
#Autowire
private PlatformTransactionManager txManager;
TransactionTemplate template = new TransactionTemplate(this.txManager);
template.execute( new TransactionCallback<Object>(){
public void doInTransaction(TransactionStatus status){
// work done here will be wrapped by a transaction and committed.
// status.setRollbackOnly(true) is called or an exception is thrown
}
});
Otherwise you have manually handle transactionality by your own , depending on the technologies your app is using.

Using PersistenceContext in a Quartz Job

We're using Spring 3.1, JPA (via Hibernate) and Quartz. Typically we interact with the DB via #PersistenceContext annotation on Service beans, and either SpringMVC controllers, or GraniteDS-managed service invocation.
I'm working on writing a Quartz job that needs to interact with the database. I've tried everything I can find to get this working. I tried passing in a Spring-managed component (annotated with #PersistenceContext and #Transactional) via the jobMap, the call to entityManager.persist(o) executes, but nothing happens in the database. I also tried similar to this answer, creating a factory class to call autowireBean() on the job object. I set up the job class like so:
public class CreateAlertJob implements Job {
#PersistenceContext
EntityManager entityManager;
#Override
#Transactional
public void execute(JobExecutionContext context) throws JobExecutionException {
SomeEntity entity = new SomeEntity();
entityManager.persist(entity);
}
}
Same result, the method executes but the database is unaltered. I found this blog post which references a GitHub project. There he is using JpaInterceptor to establish a Hibernate session, but this uses the DAO pattern and I'd like to stick with using #PersistenceContext.
Clearly there is something about the Quartz thread that is preventing this from working properly? I'm about out of ideas and considering making a web service call to a SpringMVC controller just to get this working.
Since your CreateAlertJob is not created by Spring, #Transactional in it doesn't take effect.
You have the following options:
Delegate actual work to Spring bean and put #Transactional there
Use programmatic transaction management
Use AspectJ-based AOP implementation instead of Spring default implementation (but it would be overkill for such a simple problem)

Resources