why persistence context is not cleared? - spring

#Autowired
private EntityManager entityManager;
#Transactional
void test(MemberEntity namedEntity) {
repository.save(entity);
entityManager.clear();
List<MemberEntity> result = repository
.search(PageRequest.of(0, 100), searchCause, memberEntity.modifiedDate.desc())
.getContent();
}
I thought that when repository save proceeds, it is saved in the persistence context, and clear will remove the entity from the persistence context.
And I thought that an empty value would come out when the repository.search proceeds because the entity is removed from the persistence context before it is flushed, but the saved namedEntity is output, so I have doubts.
English is insufficient, but thank you for reading and ask for a reply.

Related

Hibernate: Detached Entity error on Selects after adding #Transactional

I have a big Service method in my Spring/Hibernate app which previously wasn't transactional, and was working OK. As soon as I added #Transactional(readOnly = false, rollbackFor = {Exception.class}) to it, the Select (Fetch) part of that method started breaking at a Select statement (even prior to any persistence):
org.hibernate.PersistentObjectException: detached entity passed to persist: app.domain.Activities
in this code:
#Transactional(readOnly = false, rollbackFor = {Exception.class})
public String save(PlansModel plan, String id, Model model,HttpServletRequest request) throws IllegalAccessException, InvocationTargetException {
//...
//... some initial Selects - NO Persistence yet
Project proj = planService.getProjectByID(projId); // <-- BROKE HERE "detached entity passed to persist"
// ... NO Persistence yet
// ...
// At the end, some persistence starts
planService.savePlan(plan); //etc.
}
We're maintaining this messy code and it's hard to analyze what may have caused it. Some objects are being modified there.
We decided to work around it by putting #Transactional just around the actual persistence bottom part, which is all that matters for transactionality. (Our goal is to ensure that this whole method has transactionality/rollback.)
But when we did that, we found that in this new design, transactions don't get rolled back in case of errors. We've split it up into 2 methods, and just the 2nd method is #Transactional, but there's no rollback from here, and the Transaction mechanism doesn't kick in. Everywhere else it works.
public String save(PlansModel plan, String id, Model model,HttpServletRequest request) throws IllegalAccessException, InvocationTargetException {
//...
//... some initial Selects - NO Persistence yet
Project proj = planService.getProjectByID(projId); // <-- WORKING AGAIN without #Transactional
// ... NO Persistence yet
// ...
return persistPart(..); // for persistence
}
#Transactional(readOnly = false, rollbackFor = {Exception.class})
public String persistPart(..) {
planService.savePlan(plan); // <-- NO ROLLBACK from here
}
It seems like we're dealing with a method which has some issues as soon as #Transactional is added, but we need to ensure transactionality/rollback is enabled for it. Based on these symptoms, does anyone know what the solution might be?
#Transactional doesn't work when you call this method from inside the service class like you do. It's true not only for #Transactional. It's a limitation of Spring AOP.
Your question is almost a duplicate of this one. So you either need to inject this service into itself and call this transactional method on the injected object OR create another service which will call this transactional method

Why does OpenEntityManagerInViewFilter change #Transactional propagation REQUIRES_NEW behavior?

Using Spring 4.3.12, Spring Data JPA 1.11.8 and Hibernate 5.2.12.
We use the OpenEntityManagerInViewFilter to ensure our entity relationships do not throw LazyInitializationException after an entity has been loaded. Often in our controllers we use a #ModelAttribute annotated method to load an entity by id and make that loaded entity available to a controller's request mapping handler method.
In some cases like auditing we have entity modifications that we want to commit even when some other transaction may error and rollback. Therefore we annotate our audit work with #Transactional(propagation = Propagation.REQUIRES_NEW) to ensure this transaction will commit successfully regardless of any other (if any) transactions which may or may not complete successfully.
What I've seen in practice using the OpenEntityManagerInviewFilter, is that when Propagation.REQUIRES_NEW transactions attempt to commit changes which occurred outside the scope of the new transaction causing work which should always result in successful commits to the database to instead rollback.
Example
Given this Spring Data JPA powered repository (the EmployeeRepository is similarly defined):
import org.springframework.data.jpa.repository.JpaRepository;
public interface MethodAuditRepository extends JpaRepository<MethodAudit,Long> {
}
This service:
#Service
public class MethodAuditorImpl implements MethodAuditor {
private final MethodAuditRepository methodAuditRepository;
public MethodAuditorImpl(MethodAuditRepository methodAuditRepository) {
this.methodAuditRepository = methodAuditRepository;
}
#Override #Transactional(propagation = Propagation.REQUIRES_NEW)
public void auditMethod(String methodName) {
MethodAudit audit = new MethodAudit();
audit.setMethodName(methodName);
audit.setInvocationTime(LocalDateTime.now());
methodAuditRepository.save(audit);
}
}
And this controller:
#Controller
public class StackOverflowQuestionController {
private final EmployeeRepository employeeRepository;
private final MethodAuditor methodAuditor;
public StackOverflowQuestionController(EmployeeRepository employeeRepository, MethodAuditor methodAuditor) {
this.employeeRepository = employeeRepository;
this.methodAuditor = methodAuditor;
}
#ModelAttribute
public Employee loadEmployee(#RequestParam Long id) {
return employeeRepository.findOne(id);
}
#GetMapping("/updateEmployee")
// #Transactional // <-- When uncommented, transactions work as expected (using OpenEntityManagerInViewFilter or not)
public String updateEmployee(#ModelAttribute Employee employee, RedirectAttributes ra) {
// method auditor performs work in new transaction
methodAuditor.auditMethod("updateEmployee"); // <-- at close of this method, employee update occurrs trigging rollback
// No code after this point executes
System.out.println(employee.getPin());
employeeRepository.save(employee);
return "redirect:/";
}
}
When the updateEmployee method is exercised with an invalid pin number updateEmployee?id=1&pin=12345 (pin number is limited in the database to 4 characters), then no audit is inserted into the database.
Why is this? Shouldn't the current transaction be suspended when the MethodAuditor is invoked? Why is the modified employee flushing when this Propagation.REQUIRES_NEW transaction commits?
If I wrap the updateEmployee method in a transaction by annotating it as #Transactional, however, audits will persist as desired. And this will work as expected whether or not the OpenEntityManagerInViewFilter is used.
While your application (server) tries to make two separate transactions you are still using a single EntityManager and single Datasource so at any given time JPA and the database see just one transaction. So if you want those things to be separated you need to setup two Datasources and two EntityManagers

JPA: Nested transactional method is not rolled back

UPD 1: Upon further research I think the following information may be useful:
I obtain datasource through JNDI lookup on WildFly 9.0.2, then 'wrap' it into in instance of HikariDataSource (e. g. return new HikariDataSource(jndiDSLookup(dsName))).
the transaction manager that ends up being used is JTATransactionManager.
I do not configure the transaction manager in any way.
ORIGINAL QUESTION:
I am experiencing an issue with JPA/Hibernate and (maybe) Spring-Boot where DB changes introduced in a transactional method of one class called from a transactional method of another class are committed even though the changes in the caller method are rolled back (as they should be).
Here are my transactional services
StuffService:
#Service
#Transactional(rollbackFor = IOException.class)
public class StuffService {
#Inject private BarService barService;
#Inject private StuffRepository stuffRepository;
public Stuff updateStuff(Stuff stuff) {
try {
if (null != barService.doBar(stuff)) {
stuff.setSomething(SOMETHING);
stuff.setSomethingElse(SOMETHING_ELSE);
return stuffRepository.save(stuff);
}
} catch (FirstCustomException e) {
logger.error("Blah", e);
throw new SecondCustomException(e.getMessage());
}
throw new SecondCustomException("Blah 2");
}
// other methods
}
and BarService:
#Service
#Transactional
public class BarService {
#Inject private EntityARepository entityARepository;
#Inject private EntityBRepository entityBRepository;
/*
* updates existing entity A and persists new entity B.
*/
public EntityA doBar(Stuff stuff) throws FirstCustomException {
EntityA a = entityARepository.findOne(/* some criteria */);
a.setSomething(SOMETHING);
EntityB b = new EntityB();
b.setSomething(SOMETHING);
b.setSomethingElse(SOMETHING_ELSE);
entityBRepository.save(b);
return entityARepository.save(a);
}
// other methods
}
EntityARepository and EntityBRepository are very similar Spring-Boot repositories defined like this:
public interface EntityARepository extends JpaRepository<EntityA, Long>{
EntityA findOne(/* some criteria */);
}
FirstCustomException extends Throwable
SecondCustomException extends RuntimeException
Stuff entity is versioned, and every once in a while it is concurrently updated by StuffService.updateStuff(). In that case changes to one of the stuff instances are rolled back, as expected, but everything that happens in the barService.doBar() ends up being committed.
This puzzles me quite a lot since transaction propagation on both methods should be REQUIRED (the default one) and both methods belong to different classes, hence #Transactional should apply for both.
I did see Transaction is not completely rolled back after server throws OptimisticLockException1
But it did not really answer my question.
Can anyone please give me an idea of what's going on?
Thank you.
This isn't a 'nested' transaction - these services are operating in completely independent transactions. If you want the rollback of one to affect the other, you need to have them take part in the same transaction rather than start its own.
Or if your issue is that there is a problem with the version of 'stuff' passed into the doBar method and you want it verified, you will need to do something with the stuff instance that would cause an optimistic lock check, and so result in an exception if it is stale. see EntityManager.lock

How to link JPA persistence context with single database transaction

Latest Spring Boot with JPA and Hibernate: I'm struggling to understand the relationship between transactions, the persistence context and the hibernate session and I can't easily avoid the dreaded no session lazy initialization problem.
I update a set of objects in one transaction and then I want to loop through those objects processing them each in a separate transaction - seems straightforward.
public void control() {
List<> entities = getEntitiesToProcess();
for (Entity entity : entities) {
processEntity(entity.getId());
}
}
#Transactional(value=TxType.REQUIRES_NEW)
public List<Entity> getEntitiesToProcess() {
List<Entity> entities = entityRepository.findAll();
for (Entity entity : entities) {
// Update a few properties
}
return entities;
}
#Transactional(value=TxType.REQUIRES_NEW)
public void processEntity(String id) {
Entity entity = entityRepository.getOne(id);
entity.getLazyInitialisedListOfObjects(); // throws LazyInitializationException: could not initialize proxy - no Session
}
However, I get a problem because (I think) the same hibernate session is being used for both transactions. When I call entityRepository.getOne(id) in the 2nd transaction, I can see in the debugger that I am returned exactly the same object that was returned by findAll() in the 1st transaction without a DB access. If I understand this correctly, it's the hibernate cache doing this? If I then call a method on my object that requires a lazy evaluation, I get a "no session" error. I thought the cache and the session were linked so that's my first confusion.
If I drop all the #Transactional annotations or if I put a #Transactional on the control method it all runs fine, but the database commit isn't done until the control method completes which is obviously not what I want.
So, I have a few questions:
How can I make the hibernate session align with my transaction scope?
What is a good pattern for doing the separation transactions in a loop with JPA and declarative transaction management?
I want to retain the declarative style (i.e. no xml), and don't want to do anything Hibernate specific.
Any help appreciated!
Thanks
Marcus
Spring creates a proxy around your service class, which means #Transactional annotations are only applied when annotated methods are called through the proxy (where you have injected this service).
You are calling getEntitiesToProcess() and processEntity() from within control(), which means those calls are not going through proxy but instead have the transactional scope of the control() method (if you aren't also calling control() from another method in the same class).
In order for #Transactional to apply, you need to do something like this
#Autowired
private ApplicationContext applicationContext;
public void control() {
MyService myService = applicationContext.getBean(MyService.class);
List<> entities = myService.getEntitiesToProcess();
for (Entity entity : entities) {
myService.processEntity(entity.getId());
}
}

Spring, AOP - how to define the aspect after Hibernate DAO transaction closes

My project is Web app with Spring and Hibernate. I need to perform some routine operation on the data returned by my DAO layer and transaction is committed and session is closed. I try to use aspects with annotations like this:
#AfterReturning(pointcut = "execution(* com.dao..*.*(..))", returning = "result")
public void afterReturning(JoinPoint joinPoint, Object result){
processResult(result);
}
or using
#Around("execution(* com.dao..*.*(..))")
but in either event I got Hibernate exceptions like this:
org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing
Off course everything works if I just apply that same operation in my service:
public void serviceMethod(Object param) {
Object result = dao.getObject(param);
processResult(result);
}
'Cause I need to use processResult against every DAO method's result I am reluctant to use the latter approach and would be grateful for some advice on how to use either aspects or some other alternative way on doing so....

Resources