spring data jpa: entity is still managed after transactional method completes - spring

I am a beginner in spring boot and there is one thing that confuses me. As I understand it, in spring data jpa an EntityManger of type "container-managed transaction-scoped persistence context" is used. So according to my assumption, for each transactional service method a separate EntityManager should always be created to manage the entities within the method and after the method terminates all managed entities should be in detached state.
In the example below, StudentController calls 2 StudentService transactional methods. I thought that "studentService.insertStudent(studentEntity)" returns a detached entity and therefore "studentService.updateStudent(studentEntity1)" has no effect. But on the contrary, the entity is updated in DB, which means that it is still managed.
StudentController.java
#RequestMapping(
value = "/student",
method = {RequestMethod.PUT},
consumes = "application/json",
produces = "application/json"
)
public ResponseEntity<StudentEntity> insertStudent(#RequestBody StudentEntity studentEntity) {
StudentEntity studentEntity1 = studentService.insertStudent(studentEntity);
StudentEntity studentEntity2 = studentService.updateStudent(studentEntity1);
return new ResponseEntity<>(studentEntity2, HttpStatus.OK);
}
StudentService.java
#Transactional
public StudentEntity insertStudent(StudentEntity studentEntity){
return studentRepository.save(studentEntity);
}
#Transactional
public StudentEntity updateStudent(StudentEntity studentEntity){
studentEntity.setFirstName("firstName!!");
return studentEntity;
}
can someone please help me?

Related

Spring #Transactional method with save() command

please can somebody help me?
I have experience with JPA, but not so with Spring, that hides many aspects, that are visible in JPA (for example Hibernate implementation).
Often I was used to work in JPA in this mode (one global transaction) - I will try to explain on saving header (method_A) and its items (method_B) - with result in posting all or nothing. I would like to reach this effect via Spring persistence. I know, that method with #Transactional annotation gets the session from outside, if this exists. My problem is, that I think, that the nested implemented save() method of default Spring repository interface (CrudRepository for example) will open its own transaction anyway - and this is, what I don't want - simply I need to force this save() method to get it from outside. And so I am not sure, if only #Transactional annotation is enough to force this behavior.
This is my JPA code, that works properly in Hibernate:
root_method() {
Transaction tx = session.beginTransaction();
method_A(session);
tx.commit();
}
method_A(Session session) {
Header header = new Header();
session.save(header);
for (Item item : getJustAllocatedItems()) {
item.setHeader(header);
method_B(session, item);
}
}
method_B(Session session, Item item) {
session.save(item);
}
I am sorry, that this is not pure Java, but for explanation purposes I hope it is enough. I will try to mirror Spring code in brute form:
#Transactional
root_method() {
// ... root_method should make overal transaction frame on background (I suppose, that root_method is not called from another method with #Transactional ann.)
method_A();
}
#Transactional
method_A() {
Header header = new Header();
headerRepository.save(header);
for (Item item : getJustAllocatedItems()) {
item.setHeader(header);
method_B(item);
}
}
#Transactional
method_B(Item item) {
itemRepository.save(item);
}
... so I do not think, that save() methods of repositories (in both A and B method) will receive and use transaction from outside - am I right? - and if it is so, please can somebody interpret my JPA code from first part to appropriate Spring representation. Thanks so much.
If you call repository method without transaction, then repository
will create transaction:
Creating new transaction with name [org...SimpleJpaRepository.save]
Committing JPA transaction on EntityManager
If you use transactional annotation (Note that it should be in separate service), then save will reuse transaction:
Creating new transaction with name [com...HeaderService.createHeader]
Committing JPA transaction on EntityManager
Please note, mathods, annotated with #Transactional, should be in different classes (or you should autowire current class using setter). Then Spring will be able to use proxy. Spring wraps service with #Transactional annoaions into proxy.
Enable jpa logging:
logging.level.org.springframework.orm.jpa=DEBUG
logging.level.org.springframework.transaction=DEBUG
This is example implementation of your classes hierarcy:
#Service
#AllArgsConstructor
public class HeaderService {
HeaderRepository headerRepository;
ItemService itemService;
#Transactional
public void methodA() {
Header header = new Header();
headerRepository.save(header);
for (Item item : getJustAllocatedItems()) {
item.setHeader(header);
itemService.methodB(item);
}
}
}
#Service
#AllArgsConstructor
public class ItemService {
ItemRepository itemRepository;
#Transactional
void methodB(item) {
itemRepository.save(item);
}
}
public interface HeaderRepository extends CrudRepository<Header, Long> { }
public interface ItemRepository extends CrudRepository<Item, Long> { }

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

Weird issue in Lazy load

I am totally confused with one issue Spring data + hibernate
We have a Restful service which we are migrating to V2.
So the old controller looks like
#Api(tags = {"assignments"})
#RestController
#CheckedTransactional
public class AssignmentListController {
#Inject
private AssignmentListService assignmentListService;
//REST function
public list() {....}
}
The REST function list calls AssignmentListService to load assignments, which is a collection, and loads some data lazily. Its works excellent.
What I did is I copied this controller as name AssignmentListControllerV2, and it looks like
#Api(tags = {"assignments"})
#RestController
#CheckedTransactional
public class AssignmentListControllerV2 {
#Inject
private AssignmentListService assignmentListService;
#Inject
private AssignmentDtoMapper assignmentDtoMapper;
public list() {...}
}
The code is same except AssignmentDtoMapper bean added, which is created using MapStruct.
Now the problem, When I call this new service, somehow I get a Lazy Load exception. The error is
could not initialize proxy - no Session
I desperately need some help as I have no clue whats happening. I have just copied the code in a new class and its failing.
The exception is actually pretty clear, Hibernate can't load the lazy fetched member because there is no persistence context open when you hit it.
I suppose that in the V2 the:
#Inject
private AssignmentDtoMapper assignmentDtoMapper;
is to change some JPA business entity into DTO?
It's probably the source of the exception if you try to map not loaded member there.
If you want to avoid the exception on unitiliazed proxy you can try something like
public boolean isProxyInitialized(Object obj){
if(obj instanceof HibernateProxy){
HibernateProxy proxy = (HibernateProxy) obj;
return !proxy.getHibernateLazyInitializer().isUninitialized();
}
return obj != null;
}
It should return true if the member as bean fetched otherwise false.

#transactional in spring jpa not updating table

I am using spring jpa transactions in my project.One Case includes inserting a data in a synchronized method and when another thread accesses it the data is not updated.My code is given below :
public UpdatedDTO parentMethod(){
private UpdatedDTO updatedDTO = getSomeMethod();
childmethod1(inputVal);
return updatedDTO;
}
#Transactional
public synchronized childmethod1(inputVal){
//SomeCodes
//Place where update takes place
TableEntityObject obj = objectRepository.findByInputVal(inputVal);
if(obj == null){
childMethod2(inputVal);
}
}
#Transactional
public void childMethod2(inputVal){
//Code for inserting
TableEntityObject obj = new TableEntityObject();
obj.setName("SomeValue");
obj.setValueSet(inputVal);
objectRepository.save(obj);
}
Now if two threads access at the same time and if first thread completes childmethod2 and childmethod1 and without completing parentMethod() after that if second thread comes to the childMethod1() and checks if data exists,the data is null and is not updated by first thread.I have tried many ways like
#Transactional(propagation = Propagation.REQUIRES_NEW)
public synchronized childmethod1(inputVal){
//SomeCodes
//Place where update takes place
TableEntityObject obj = objectRepository.findByInputVal(inputVal);
if(obj == null){
childMethod2(inputVal);
}
}
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void childMethod2(inputVal){
//Code for inserting
TableEntityObject obj = new TableEntityObject();
obj.setName("SomeValue");
obj.setValueSet(inputVal);
objectRepository.save(obj);
}
also tried taking off #transactional in the childMethod1() but nothing works out.I know im doing something wrong here , but couldnt figure out where and what exactly i am doing wrong.Can anyone help me out with this
#Transactional is resolved using proxies on spring beans. It means it will have no effect if your method with #Transactional is called from the same class. Take a look at Spring #Transaction method call by the method within the same class, does not work?
The easiest would be moving those methods into separate service.
Typical checklist I follow in cases like these :
If Java based configuration then make sure
#EnableTransactionManagement annocation is present in the class
containing the #Configuration annotation
Make sure the transactionManager bean is created, again this should be mentioned in the configuration class.
Use of #Transactional annocatio over the method which is calling the repository, typically a class in the DAO layer
Adding the #Service annotation for the class which is invoking the methods in the repository
Nice blog which explains the Transaction configuration with JPA in depth --> http://www.baeldung.com/2011/12/26/transaction-configuration-with-jpa-and-spring-3-1/68954

Spring MVC: issue with conversionservice and #transactional on mvc controller

I get a LazyInitializationException by combining the conversionservice with a #Transactional behavior in a MVC controller.
Following fails:
#RequestMapping(value = "/{userId}", method = RequestMethod.GET)
#ResponseBody
#Transactional
public JsonUser getUser(#PathVariable("userId") User user) {
// convertToJsonUser triggers lazy loading of User.adresses
return mUserPresenter.convertToJsonUser(user);
}
... with following exception:
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: User.adresses, could not initialize proxy - no Session
But the same code without the conversionservice succeeds:
#RequestMapping(value = "/{userId}", method = RequestMethod.GET)
#ResponseBody
#Transactional
public JsonUser getUser(#PathVariable("userId") Long userId) {
User user = mUserRepository.findOne(userId);
// convertToJsonUser triggers lazy loading of User.adresses
return mUserPresenter.convertToJsonUser(user);
}
and the same code without the transactional behavior succeeds:
#RequestMapping(value = "/{userId}", method = RequestMethod.GET)
#ResponseBody
public JsonUser getUser(#PathVariable("userId") User user) {
// changedConvertToJsonUser DOESN'T trigger lazy loading of User.adresses
return mUserPresenter.changedConvertToJsonUser(user);
}
The conversion seems to occur in its own transaction before the main transaction to be opened by the #Transactional annotation. As a result, the User loaded by the conversionmanager is not bound to the main transaction and lazy loading fails for that reason.
Is that behavior known?
How can I get rid of it?
Did I forget something in the configuration?
Thank you in advance!!!
Controller is not a perfect place to put #Transactional annotations. It would be better to add them to service methods. This may cause problems because you may have a proxy object of controller which is mixed up with proxy from transaction manager and you can not predict what the order of execution will be. So my advice is: try to add a service layer.. even a very simple put there #Transactional annotations and test the code again.
... the conversionservice is per se the way of loading entities ...
No, it's not. It's like the name says: conversion. If you load entities, then that's your choice, not Spring's.
if it is not recommanded to open a transaction in the controller-layer, then why did Spring developers have developed the conversionservice feature?
The service in ConversionService implies that we are not in the controller layer.
The conversion seems to occur in its own transaction before the main transaction to be opened by the #Transactional annotation
There is no transaction opened by #Transactional in this case, because it doesn't work.
Transactions usually work on the service layer. You could have something like:
MyUserService implements UserService {
#Override
#Transactional(readonly = true)
public User getUserById(int id) {
But that's not your problem. Your problem is that the User object is not fully initialized. You would need a transaction on convertToJsonUser.
As a side note: The people who develop Spring prefer using the ids directly and load entities in the controller, not before.

Resources