Spring Hibernate JPA #Transaction(Propagation.REQUIRES_NEW) can't find object - java

I'm asking this question after 5 hours staring at the monitor
I have code with the following format
#Transaction(readmode=true)
class SomeServiceClass {
#Autowired
SubServiceClassA subServiceClassA;
#Autowired
SubServiceClassB subServiceClassB;
public void doSomething() {
Long id = subServiceClassA.performSave();
subServiceClassB.performAction(id);
}
}
I persist an object and return the id.
#Service
class SubServiceClassA {
#Transactional(propagation=Propagation.REQUIRES_NEW)
public Long performSave() {
SomeObj someObj = someRepository.save(SomeObj someObj);
return someObj.getId();
}
}
Later in a separate REQUIRES_NEW transaction, I attempt to do something with the newly saved object.
#Service
class SubServiceClassB {
#Transactional(propagation=Propagation.REQUIRES_NEW)
public void performAction(Long id) {
SomeObj someObj = someRepository.find(id);
// someObj is null here!!! :(
}
}
It is my understanding that this code should work, since the object saved was in a REQUIRES_NEW transaction in a separate bean.
My problem is that this code works great in my local setting. When I upload this to production setting, I get a NPE because someObj is null. (We are using gradle, so dependencies should be the same)
I would appreciate it greatly if I can get any pointers as to why this may not work.

Found the solution
I guess I was posting without extra info
The code in my ServiceB class was
#Service
class SubServiceClassB {
#Transactional(propagation=Propagation.REQUIRES_NEW, readonly=true)
public void performAction(Long id) {
SomeObj someObj = someRepository.find(id);
// someObj is null here!!! :(
}
}
The problem was that I was writing an object in ServiceA to the master DB, but reading from a slave DB. Apparently, setting readonly=true forced the server to read from a slaveDB, however the newly persisted object hadn't been replicated to the slave DB yet.
After removing the readonly=true, everything worked fine.

Related

Proper Hibernate nested transactions handling

I am sure that I am missing something, but I don't know exactly what...
Giving the following snippet:
#Service
public class MyClass {
private MyClass self;
private UserRepository userRepository;
private ApplicationContext applicationContext;
#PostConstruct
private void init() {
self = applicationContext.getBean(MyClass.class);
}
#Transactional
public void doA(User user) {
...
if (condition) {
self.doB(user);
throw new SecurityException();
}
user.setRandomField("x");
userRepository.save(user);
}
#Transactional(value = Transactional.TxType.REQUIRES_NEW)
public void doB(User user) {
...
userRepository.save(user);
}
}
What do I know about #Transactional is that if it is used, is redundant to call repository.save(entity).
What I am trying to do, is to process an entity from a transactional method, and if there is a breaking condition, call a new method (annotated with REQUIRES_NEW) that will update some fields of the entity and save it. The root method (doA) then throws an exception. FYI: the #Transactional(dontRollbackOn = SecurityException.class) is not an option in this situation.
For using this commiting mechanism, instead of creating a new bean just with one method I just injected the current bean into a variable just called self, therefore I can use the bean proxy for transaction management.
The odd thing is that if I am removing from doB the save call, when doA transaction is rollbacked because of the SecurityException, the changes performed by doB are rollbacked as well. But if I let it in there, this is working as expected.
Am I doing something wrong or am I missing something?
Thanks!
Try to do not pass User instance in the doB().
Pass an Id instead and read the User from the repo internally. I am not sure how the attached entity is handled between the different sessions.

Confusing hibernate behaviour using spring data

I just hit a really strange case which I can't explain to myself. I have have the following scenario:
Hibernate version: 5.4.9
Spring data version: 2.2.3
So the following method is wrapped in a transaction and it only saves the entity
#Transactional
public Bookmark create(Entity entity) {
return repository.save(entity);
}
Here I registered a PostInsertEventListener. Based on some logic it uses the same repository to query the underlying table. I removed the logic in order to make the example more readable.
#Component
public class EntityListener implements PostInsertEventListener {
#Autowired
private EntityRepository repository;
#Autowired
private EntityManagerFactory entityManagerFactory;
#PostConstruct
private void init() {
final EventListenerRegistry registry = ((SessionFactoryImplementor) entityManagerFactory.unwrap(SessionFactory.class)).getServiceRegistry()
.getService(EventListenerRegistry.class);
registry.appendListeners(EventType.POST_INSERT, this);
}
#Override
public void onPostInsert(PostInsertEvent event) {
if (event.getEntity() instanceof Entity) {
repository.findByFieldOneAndFieldTwoIsNotNull(event.getEntity().fieldOne());
}
}
#Override
public boolean requiresPostCommitHanding(EntityPersister persister) {
return false;
}
}
So when I invoke the create(Entity entity) method the onPostInsert(PostInsertEvent event) is triggered(as expected) but when this line is invoked repository.findByFieldOneAndFieldTwoIsNotNull(event.getEntity().fieldOne());
then another insert is executed and the onPostInsert(PostInsertEvent event) is triggered again. And of course at some point this leads to StackOverflowException.
Can someone come up with an idea why another insert is executed when I'm reading data using findBy query?
So i have a progress on that issue. When I execute repository.findByFieldOneAndFieldTwoIsNotNull(event.getEntity().fieldOne()); in a new separate transaction then everything is fine. So it seems that executing queries in the entity listener in the same transaction that the insert was executed on is leading to an infinite recursion which leads to a StackOverflowException. But I can't figure it out why is this happening.

Getting LazyInitializationException although using #Transactional(propagation=Propagation.REQUIRES_NEW)

I use jHipster with Spring Data JPA and have the following method:
#Transactional(propagation=Propagation.REQUIRES_NEW)
public void doSomeWork(EntityA entityA) {
// some code
List<EntityB> entityBList = new ArrayList<EntityB>();
entityBList.add(new EntityB());
entityA.addAllEntityB(entityBList);
}
At the last line I get an org.hibernate.LazyInitializationException excption which I don't understand.
Why is it throwing this excption although the method is run in its own transaction?
Should it not just lazy load the list as the session is still open?
It might be that LazyInitializationException is thrown because a new transaction is started and entityA becomes "detached" as the result.
One can use something like this:
#Transactional
public void addEntityB(long entityAId, entityB) {
EntityA entityA = loadEntityA(entityAId);
addEntityBToEntityA(entityA, entityB);
saveEntity(entityB);
}
called from outside in this manner:
for (EntityB entityB : entityBList) {
try {
addEntityB(entityAId, entityB);
}
catch(Exception e){
log(e);
}
}
It is true that you load each time entityA, though.

JPA synch/commit error of a POJO DTO even if I do not want to save it

Due to lack of key words to capture this scenario, let me just proceed to describe it. The classes have been simplified.
Given this:
public ItemController {
#Autowired
ItemDtoService ItemDtoService;
#Autowired
DiscountService discountService;
#RequestMapping(value = "/viewItems", method = RequestMethod.POST)
public void process() {
List<ItemDto> ItemDtos = ItemDtoService.getItemDtos();
for(ItemDto i: ItemDtos) {
boolean isDiscounted = discountService.hasDiscount(i); //throws exception here on iteration 2 and the last iteration, ItemDto was discounted
if (isDiscounted) {
i.setPrice(discountService.getDiscountedPrice(i));
//do some other i.setter, basically modify the pojo
}
}
}
}
An exception is thrown at the discountService.hasDiscount when:
on subsequent iteration
and the previous iteration, the ItemDto was discounted.
Exception is:
Caused by: org.hibernate.exception.SQLGrammarException: could not update: [somepackage.ItemDto#364]
And somewhere in the stacktrace you will see this:
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:456)"
The problem is that method call uses a dao method underneath that is #Transactional (and maybe for a good reason even though it's only a query, complicated query). When the JPA Tx manager does its job upon method call end, it sees the pojo as modified and tries to synch it. The ItemDto pojo does have #Entity because inside ItemDtoService.getItemDtos uses the getEntityManager().createNativeQuery(nativeSql, ItemDto.class). The 5 other class details are here:
#Entity
public class ItemDto{
//body
}
#Service
public class ItemService {
#Autowired
ItemDao itemDao;
public List<ItemDto> getItems() {
return itemDao.getItems(); //for sake of simplicity
}
}
#Repository
#Transactional
public class ItemDaoImpl {
public List<ItemDto> getItems() {
String nativeSql = "select...."
return getEntityManager().createNativeQuery(nativeSql, ItemDto.class);
}
}
#Service
public class DiscountService {
#Autowired
DiscountDao discountDao;
public boolean hasDiscount(ItemDto i) {
boolean hasDiscount = discountDao.hasDiscount(i);
//do other service stuff that might influence the hasDiscount flag
return hasDiscount;
}
}
#Repository
#Transactional
public class DiscountDaoImpl {
public boolean hasDiscount(ItemDto i) {
String nativeSql = "select...."
boolean hasDiscount;
//in reality the query is a complicated joins, executes and returns if has discount or not
return hasDiscount;
}
}
What am I doing wrong?
Some of the options I tried and worked include:
add to the #Transactional the (readonly=true) on the Dao methods
since they are only queries (negative effect though is those might
be intentionally transactional due to complex queries, and may need
locking to prevent dirty reads)
in the Controller, create a separate loop for modification, it
then have 2 loops, 1 for looping through items and seeing which is
discounted, store those info somewhere to be referenced later on 2nd
loop, which does the modification of said pojos
I am looking at other options, and please comment if you see something wrong with the way it was coded.
Another option I just found is inside the Dao that returns the list of ItemDto, before returning the list, I would execute this:
getEntityManager().clear();
It works fine because the list is Dto anyways and one would expect that these require no DB synching, at the same time the #Transactional is retained for necessary locking for consistent reads.
That's one more alternative, but what is the most appropriate way really?

Spring+JPA+Hibernate: persist is updating the entity surprisingly. Please go through the details

In my code, I did as follows:
queried for a course entity
populate it with the given course data.
courseDao.update(entity) which internally calls persist(entity) method.
Surprisingly, the data is got updated successfully.
I am confused with this behaviour of persist method.
Please help me out.
code is as below:
//My Service......
#Service("myService")
#Transactional
public class MyServiceImpl implements MyService {
#Transactional(rollbackFor = { Throwable.class })
public void updateCourse(final Course course) throws MyServiceException {
------
------
CourseEntity courseEntity = courseDao.findById(course.getId());
populateCourseEntity(courseEntity, course);
courseDao.update(courseEntity);
}
}
//CourseDao.....
public class CourseDaoImpl implements CourseDao {
--------
public void update(final T entity) throws MyDaoException {
if (entity != null) {
this.entityManager.persist(entity);
}
else {
String errMsg = "Object to be updated cannot be null.";
throw new MyDaoException(errMsg);
}
}
}
When an entity is currently managed (attached to a session), all updates to it are directly reflected to the underlying storage even without calling persist().
In your case, you load your entity, so it's in the session. Then even if you don't call persist() it will be updated in the database on transaction commit.
The persist() description from the javadoc:
Make an entity instance managed and persistent.
This means that the method doesn't do anything in your case, since your entity is both persistent and managed.
P.S. Where I say "session", understand "entity manager"
JPA tries very hard to be a helpful API, such that anything you get from it (or save to it) will subsequently be tracked by JPA. This means than any further changes will be automatically handled for you by JPA without any additional work on your part.

Categories

Resources