Another LazyInitializationException (in combination with Spring+GSON) - spring

I guess I'm another newbie guy who fails to understand Hibernate sessions, may be Spring's TransactionTemplate, dunno. Here's my story.
I'm using Hibernate 3.5.5-Final, Spring 3.0.4.RELEASE, trying to live only with annotations (for Hibernate as well as Spring MVC).
My first try was to use #Transactional annotations in combination with properly setup transaction manager. Seemed to work at first, but in long run (about 36hours) I started to receive "LazyInitializationExceptions" over and over again (from places that were running just fine in previous hours!).
So I switched to manual transactions using Spring TransactionTemplate.
Basically I'm having something like this protected stuff in my BaseService
#Autowired
protected HibernateTransactionManager transactionManager;
protected void inTransaction(final Runnable runnable) {
TransactionTemplate transaction = new TransactionTemplate(transactionManager);
transaction.execute(new TransactionCallback<Boolean>() {
#Override
public Boolean doInTransaction(TransactionStatus status) {
try {
runnable.run();
return true;
} catch (Exception e) {
status.setRollbackOnly();
log.error("Exception in transaction.", e));
throw new RuntimeException("Exception in transaction.", e);
}
}
});
}
And using this method from the service's impls worked OK, I did not see LazyInitializationException for 10 days (running Tomcat with this single app 24*7 for 10days, no restarts) ... but than hoops! It has popped again :-/
The LazyInitializationException comes from place under "inTransaction" method and there is no "inTransaction recursion" involved, so I'm pretty sure I should be in the same transaction alas in the same Hibernate session. There is no "data from previous session" involved (as far as my code goes that service layer opens the transaction, gathers all data from Hibernate it needs, process it and returns some result == service does not recall other top-services)
I have not profiled my app (I don't even know how to do that properly in long runs such as 10 days), but my only guess is that I'm leaking memory somewhere and JVM hits heap-limit...
Is there some "SoftReferences" involed inside Spring or Hibernat? I don't know...
Another funny thing is that the exception always happen when I try to serialize the result into JSON using Google GSON serializer. I know, it does not use getters ... I have my own patched version that is using getters instead of actual fields (so I'm making sure not to bypass Hibernate proxy mechanisms), do you think it may play some role here?
Last funny thing is that the exception is always happing in the single service method (not anywhere else), which is driving me nuts because this method is as simple-stupid as it could be (no extrem DB operations, just loads data and serialize them to JSON using lazy-fetching), huh???
Does anybody have any suggestions what should I try? Do you have some similar experiences?
Thanks,
Jakub

Related

guava eventbus post after transaction/commit

I am currently playing around with guava's eventbus in spring and while the general functionality is working fine so far i came across the following problem:
When a user want's to change data on a "Line" entity this is handled as usual in a backend service. In this service the data will be persisted via JPA first and after that I create a "NotificationEvent" with a reference to the changed entity. Via the EventBus I send the reference of the line to all subscribers.
public void notifyUI(String lineId) {
EventBus eventBus = getClientEventBus();
eventBus.post(new LineNotificationEvent(lineId));
}
the eventbus itself is created simply using new EventBus() in the background.
now in this case my subscribers are on the frontend side, outside of the #Transactional realm. so when I change my data, post the event and let the subscribers get all necessary updates from the database the actual transaction is not committed yet, which makes the subscribers fetch the old data.
the only quick fix i can think of is handling it asynchronously and wait for a second or two. But is there another way to post the events using guava AFTER the transaction has been committed?
I don't think guava is "aware" of spring at all, and in particular not with its "#Transactional" stuff.
So you need a creative solution here. One solution I can think about is to move this code to the place where you're sure that the transaction has finished.
One way to achieve that is using TransactionSyncrhonizationManager:
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization(){
void afterCommit(){
// do what you want to do after commit
// in this case call the notifyUI method
}
});
Note, that if the transaction fails (rolls back) the method won't be called, in this case you'll probably need afterCompletion method. See documentation
Another possible approach is refactoring your application to something like this:
#Service
public class NonTransactionalService {
#Autowired
private ExistingService existing;
public void entryPoint() {
String lineId = existing.invokeInTransaction(...);
// now you know for sure that the transaction has been committed
notifyUI(lineId);
}
}
#Service
public class ExistingService {
#Transactional
public String invokeInTransaction(...) {
// do your stuff that you've done before
}
}
One last thing I would like to mention here, is that Spring itself provides an events mechanism, that you might use instead of guava's one.
See this tutorial for example

Spring Boot: Rollback transaction before request resolves

I've got a method in my service class which performs some changes to an entity, afterwards it executes some critical code (communication to different microservice) which may come back with error information attached to an exception. If an error occurs, the changes should be reverted. To achieve this I thought I'd use #Transactional.
Service
#Service
public class MyService {
#Autowired
private EntityRepository repository;
#Transactional(rollbackFor = CriticalCodeException.class, isolation = Isolation.READ_COMMITTED, propagation = Propagation.NESTED)
public Entity foo(entity) {
/* perform changes ... */
entity.setBar(...)
try {
/* critical code */
} catch (CriticalCodeException e) {
e.setEntityId(entity.getId());
throw e;
}
}
public attachError(CriticalCodeException e) {
// reload entity which should be rolled back but is not
Entity reloaded = repository.findOne(e.getEntityId);
reloaded.setError(e.getError); // attach error
repository.save(reloaded) // saves changes which should be rolled back
}
}
As the CriticalCodeException is a RuntimeException and might also occur in another context, I've created a designated ExceptionHandler which is supposed to catch it and attach the error to my entity.
ExceptionHandler
#ExceptionHandler(CriticalCodeException .class)
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
#ResponseBody
public ErrorResponse processError(CriticalCodeException e) {
entityService.attachError(e);
return e.getError();
}
Yet, when I attach the error I do not wont to save the pervious made changes too. So when I reload the entity after the Exception has been thrown out of my nested #Transactional-method I'd expect it to be in its initial state. Well, it is not. If I do not attach the error (and thus omit my save() call) the changes do not get persisted. So I figure the rollback eventually executes after my exception handling.
Can anybody tell me how I can access my entity in its initial state AFTER the rollback is done?
I am using Spring Boot Data with JPA and Hibernate. I've tried various configurations with things like #EnableTransactionManagement and setting up different combinations of beans. Yet, after a lot of research, I'm pretty confindent that the #Transactional is actually working (as to be seen from the eventual rollback described above) and should work without further configuration. What am I doing wrong?
EDIT
The changes were in fact rolled back but my entity-manager did not get reset, just M. Deinum pointed out. In my persistence setup changes to an entity were only persisted by a call to repository.save(entity). What I did eventually was to omit the transactional approach and instead implement repository.reload(entity) which refreshes my entity via the entity-manager. This way I can get rid of previous made changes which did not even get persisted in the first place.

Do Spring transactions propagate through new instantiations

I'm working on a bunch of legacy code written by people before me and I'm confused about a particular kind of setup and wonder if this has ever worked to begin with.
There is a managed bean in spring that has a transactional method.
#Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Throwable.class)
public boolean updateDraftAndLivePublicationUsingFastDocumentsOfMySite(List<FastDocumentLite> fastDocumentLites, Long mySiteId) throws Exception { ... }
Now inside that method I find new instantiations calling update methods fe:
boolean firstFeed = new MySiteIdUpdate(publishing, siteDao, siteDomainService).update(siteId, fastDocumentLites.get(0).getMySiteId());
From my understanding on IOC this new class isn't managed by spring , it's just a variable in the bean. Now going further inside the update method you see another service gets called.
#Transactional(propagation=Propagation.REQUIRED, rollbackFor=Throwable.class)
public void activateSubdomainForSite(Long siteId, boolean activationOfSite)
So if there is a transaction open it should be propagated into this service. But here is what I don't get if that MySiteIdUpdate object isn't managed by spring does the first transaction move forward to the activateSubdomainForSite method ?? Or is another transaction being opened here. I looked in the logs and I believe it to be the latter but I rather ask the experts for a second oppinion before I proclame this legacy code to be complete rubbish to the project lead. I'm suffering with a StaleStateException somewhere further down the road and I'm hoping this has anything to do with it.
I think the code is correct, and the second #Transactional should reuse the existing transaction.
Because:
1) Spring Transaction handling is done either by Proxies or by AspectJ advices. If it is done by Proxies then it is required that MySiteIdUpdate invoke an instance that is injected (this is what you did). If you use AspectJ, then it should work anyway.
2) The association Transactions to the code that use is done by the Thread, this mean, as long as you "are" in the thread which started the transaction you can use it. (you do not start an new thread, so it should work)
An other way to explain: It is perfect legal when you have some method in your call hierarchy that does not belong to an spring bean. This should not make the transaction handling fail.

Spring Database Integration Test, when to flush or?

I am fairly new to spring, and doing some integration tests.
Using Hibernate, MySql and Spring data JPA.
I am using transaction support and everything gets rolled back at the end of each test.
For example:
#Test (expected=DataIntegrityViolationException.class)
public void findAndDelete() {
UUID uuid = UUID.fromString(TESTID);
User user= iUserService.findOne(uuid);
iUserService.delete(cashBox);
iUserService.flush();
assertNull(iUserService.findOne(uuid));
}
In the above code, I call the iUserService.flush(), so that the sql gets sent to the DB, and an expected DataIntegrityViolationException occurs because there is a foreign key from User to another table (Cascade is not allowed, None). All good so far.
Now, if I remove the iUserService.flush()
then the expected exception does not occur because the sql does not get sent to the DB.
I tried adding the flush() into a teardown #After method, but that didn't work as the test does not see the exception outside of the test method.
Is there any way to avoid calling the flush within the test methods?
It would be preferable if the developers on my team did not have to use the flush method at all in their testing code
Edit:
I tried adding the following
#Before
public void before() {
Session session = entityManagerFactory.createEntityManager().unwrap(Session.class);
session.setFlushMode(FlushMode.ALWAYS);
}
but it does seem to flush the sqls, before each query.
In my humble opinion, it's better than the developers of your team know what they are doing.
It includes the things that are configured by default and the consequences of that.
Please, take a look to why you need avoid false positives when testing ORM code

Spring nested transaction marked as rollbackonly with unchecked exception

I am fairly new to Spring and transactions. I am sure this question has been asked before, but I still cannot figure the correct way to go about it.
I am using Spring and hibernate. I have a service method that goes like this:
#Transactional
public void processPendingReport(Report report) {
try {
// Do processing stuff, update report object state
reportDAO.save(report);
} catch (Exception e) {
reportDAO.markReportAsFailed(report);
}
}
If a RuntimeException occurs during processing, a "Transaction marked as rollbackOnly" RollbackException will be thrown, having as a result that the report will not be marked as failed (although I would like it to be).
I have tried using #Transactional(noRollbackFor=Exception.class), but still get the same issue.. Any suggestions? Could it be a configuration issue?
If a database exception (e.g. constraint violation) occurs in reportDAO.save() or reportDAO.markReportAsFailed() the transaction will be rolled back on the database level no matter what you are doing on the application level.
You can still mark the report as failed if reportDao.save() fails when you create a new transaction for reportDAO.markReportAsFailed(). Since ReportDAO is annotated #Transactional just remove the #Transactional annotation from the service method. You could also change the reportDAO.save() implementation to use a database function or stored procedure that wraps the insert statement and catches any exceptions on the database level.
HTH.

Resources