Spring EntityNotFoundException when accessing entity from async method - spring

I have spring app with one method where I have following logic.
I save entity into database via saveMyEntity(e)
Then I call asyncMethod(e) that is executing in custom thread.
The problem is when async method is trying to access for entity via getEntityById(id) then it returns NotFoundEntity exception.
Could you please explain me why I cannot see the object that was saved before?
May it be caused by #Transactional annotation? I dont know.
Thanks
method1(Entity e) {
e = saveMyEntity(e);
Future<> thread = asyncMethod(e.id);
}
#Transactinoal
Entity saveMyEntity(e) {
Entity e = repository.saveAndFlush(e);
return e;
}
#Async
Future<> asyncMethod(id) {
Entity e = getEntityById(id); // throws not found exp
//some processing with entity
return new future<>;
}
#Transacitonal
Entity getEntityById(id) {
Entity e = repository.getById(id);
return e;
}

Related

Handle Hibernate optimistic locking with Spring

I am using Hibernate and Spring Data, it will perform optimistic locking when insert or update an entity, and if the version in database doesn't match with the one to persist, it will throw exception StaleObjectStateException, in Spring, you need to catch it with ObjectOptimisticLockingFailureException.
What I want to do is catch the exception and ask the user to refresh the page in order to get the latest data from database like below:
public void cancelRequest()
{
try
{
request.setStatus(StatusEnum.CANCELLED);
this.request = topUpRequestService.insertOrUpdate(request);
loadRequests();
//perform other tasks...
} catch (ObjectOptimisticLockingFailureException ex)
{
FacesUtils.showErrorMessage(null, "Action Failed.", FacesUtils.getMessage("message.pleaseReload"));
}
}
I assume it will also work with the code below but I have not tested it yet.
public void cancelRequest()
{
RequestModel latestModel = requestService.findOne(request.getId());
if(latestModel.getVersion() != request.getVersion())
{
FacesUtils.showErrorMessage(null, "Action Failed.", FacesUtils.getMessage("message.pleaseReload"));
}
else
{
request.setStatus(StatusEnum.CANCELLED);
this.request = requestService.insertOrUpdate(request);
loadRequests();
//perform other tasks...
}
}
I need to apply this checking on everywhere I call requestService.insertOrUpdate(request); and I don't want to apply them one by one. Therefore, I decide to place the checking code inside the function insertOrUpdate(entity) itself.
#Transactional
public abstract class BaseServiceImpl<M extends Serializable, ID extends Serializable, R extends JpaRepository<M, ID>>
implements BaseService<M, ID, R>
{
protected R repository;
protected ID id;
#Override
public synchronized M insertOrUpdate(M entity)
{
try
{
return repository.save(entity);
} catch (ObjectOptimisticLockingFailureException ex)
{
FacesUtils.showErrorMessage(null, FacesUtils.getMessage("message.actionFailed"),
FacesUtils.getMessage("message.pleaseReload"));
return entity;
}
}
}
My main question is, there will be one problem with this approach. The caller side will not know whether the entity persisted successfully or not since the exception will be caught and handled inside the function, so the caller side will always assume the persist was success, and continue do the other tasks, which is I don't want. I want it to stop performing tasks if fail to persist:
public void cancelRequest()
{
try
{
request.setStatus(StatusEnum.CANCELLED);
this.request = topUpRequestService.insertOrUpdate(request);
//I want it to stop here if fail to persist, don't load the requests and perform other tasks.
loadRequests();
//perform other tasks...
} catch (ObjectOptimisticLockingFailureException ex)
{
FacesUtils.showErrorMessage(null, "Action Failed.", FacesUtils.getMessage("message.pleaseReload"));
}
}
I know when calling the insertOrUpdate , I can catch the returned entiry by declaring new model variable, and compare it's version to the original one, if version is same, means the persistance was failed. But if I doing it this way, I have to write the version checking code on everywhere I call insertOrUpdate. Any better approach then this?
The closest way to being able to do this and not having to necessarily make significant code changes at all the invocation points would be to look into some type of Spring AOP advice that works similar to Spring's #Transactional annotation.
#FacesReloadOnException( ObjectOptimisticLockingFailureException.class )
public void theRequestHandlerMethod() {
// call your service here
}
The idea is that the #FacesReloadOnException annotation triggers an around advice that catches any exception provided in the annotation value and does basically handles the call the FacesUtils should any of those exception classes be thrown.
The other options you have available aren't going to be nearly as straight forward and will require that you touch all your usage points in some fashion, its just inevitable.
But I certainly would not consider putting the try/catch block in the service tier if you don't want to alter your service tier's method return types because the controllers are going to need more context as you've pointed out. The only way to push that try/catch block downstream would be if you returned some type of Result object that your controller could then inspect like
public void someControllerRequestMethod() {
InsertOrUpdateResult result = yourService.insertOrUpdate( theObject );
if ( result.isSuccess() ) {
loadRequests();
}
else {
FacesUtils.showErrorMessage( ... );
}
}
Otherwise you'd need to get creative if you want to somehow centralize this in your web tier. Perhaps a web tier utility class that mimics your BaseService interface like the following:
public <T extends BaseService, U> U insertOrUpdate(T service, U object, Consumer<U> f) {
try {
U result = service.insertOrUpdate( object );
f.accept( result );
return result;
}
catch ( ObjectOptimisticLockingFailureException e ) {
FacesUtils.showErrorMessage( ... );
}
}
But being frank, unless you have a lot of call sites that are similar enough to where such a generalization with a consumer like this makes sense, you may find its more effort and work to generalize it than it would to just place the try/catch block in the controller itself.

Spring #Transactional behavior calling both Transactional and Non-Transactional Methods

I'm looking at some existing code and wanted to know what happen's in the following scenario with Spring's #Transactional annotation? Consider the following example:
A POST request hits a #Controller annotated with #Transactional:
#ResponseBody
#Transactional
#RequestMapping(value="/send", method=RequestMethod.POST)
public void send(#RequestBody Response response) {
try {
DBItem updatedDbItem = repository.updateResponse(response);
if (updatedDbItem == null){
//some logging
}
} catch(Exception ex) {
//some logging
}
}
The controller calls a non #transactional repository method which sets a value and in turns calls a another #Transactional method:
#Override
public DBItem updateResponse(Response response) {
try {
DBItem dBItem = findResponseById(response.getKey());
if (dBItem != null){
dBItem.setSomeField(response.getValue());
return updateDataBaseItem(response);
}
} catch (Exception ex) {
//some logging
}
return null;
}
The following updateDataBaseItem() method is common and called from other non transactional methods as well as the above method:
#Transactional
#Override
public DBItem updateDataBaseItem(Response response){
try {
DBItem dBItem = em.merge(response);
return dBItem;
} catch (Exception ex) {
//some logging
}
return null;
}
send() => spring detect #transaction with default parameters
actually Propagation setting is REQUIRED and the spring join the exist transaction or create new if none.
repository.updateResponse(..) => No transactions params the method execute within the same transaction already exist
updateDataBaseItem(..) => calling the method in same repository , spring will not recognize the #Transaction annotation because the use of proxy mode, so this method will be executed within the same transaction
a method within the target object calling another method of the target
object, will not lead to an actual transaction at runtime even if the
invoked method is marked with #Transactional

How to manage 2 DAO methods in a single transaction?

I need to manage 2 Dao methods in a single transaction ,failure of either of them should rollback the other. The calling method is in the service layer. The technology used in Spring and Hibernate native sql query. Is there a way to achieve this?
Calling Method::
#Transactional(propagation= Propagation.REQUIRED)
public String save(AllowFileTypesForm formBeanObj,Hashtable global)
Called Method1::
public boolean deleteData( String strTableName,String strWhereClause) {
Session session = sessionFactory.getCurrentSession();
String strSqlQuery = null;
boolean deleted=false;
strSqlQuery = "DELETE FROM Persons where" + strWhereClause;
try {
Query query=session.createSQLQuery(strSqlQuery);
if (query.executeUpdate() <= 0) {
throw new SQLException("No row deleted from table " +strTableName);
}
else{
deleted=true;
}
}
catch(Exception e){
e.printStackTrace();
}
return deleted;
}
Similar to this method there is another method which deletes data from some other table.
It will not rollback because you are catching the exceptions.
Spring's transaction management works through exceptions exiting the transaction boundary which are detected by the #Transactional AOP.
If you catch a sql exception for logging or something you must rethrow or throw a new exception to kick off rollback.
Add #Transactional on the service method from where you are calling those DAO methods. This article nicely sums up how it works.

EJB 3.0 CMP Transaction after a rollback

I'm having some trouble with transaction management in EJB3.0. What I want to do is to log an error into the database in case an exception happens.
For that purpose I have 2 stateless beans: Bean A and Bean B.
Bean A does the following:
save something
call Bean B to log an error if needed
In Step 1, the save is basically using the EntityManager#merge(-) method.
In Step 2, I have put the following lines at the top of Bean B:
#Stateless(name = "ErrorLogDAO", mappedName = "ErrorLogDAO")
#Remote
#Local
#TransactionManagement(TransactionManagementType.CONTAINER)
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public class ErrorLogDAOBean {...}
However, when an exception is taking place in the save method, I'm catching it and then I manually invoke the ctx.setRollBackOnly() method and after that I call ErrorLogDAOBean that inserts an error log to the DB. But the error log is not being inserted and the error I'm getting is:
javax.transaction.TransactionRolledbackException: EJB Exception: :
weblogic.transaction.internal.AppSetRollbackOnlyException at
weblogic.transaction.internal.TransactionImpl.setRollbackOnly(TransactionImpl.java:551)
at
weblogic.transaction.internal.TransactionManagerImpl.setRollbackOnly(TransactionManagerImpl.java:319)
at
weblogic.transaction.internal.TransactionManagerImpl.setRollbackOnly(TransactionManagerImpl.java:312)
at
org.eclipse.persistence.transaction.JTATransactionController.markTransactionForRollback_impl(JTATransactionController.java:145)
at
org.eclipse.persistence.transaction.AbstractTransactionController.markTransactionForRollback(AbstractTransactionController.java:196)
at
org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.rollbackTransaction(UnitOfWorkImpl.java:4486)
at
org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabase(UnitOfWorkImpl.java:1351)
at
org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.commitToDatabase(RepeatableWriteUnitOfWork.java:468)
at
org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabaseWithPreBuiltChangeSet(UnitOfWorkImpl.java:1439)
at
org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.writeChanges(RepeatableWriteUnitOfWork.java:316)
at
org.eclipse.persistence.internal.jpa.EntityManagerImpl.flush(EntityManagerImpl.java:527)
....
I'm well familiar with transaction management logic, and based on the code above I assumed I had this covered, but it appears not.
Any ideas?
UPDATE
Bean A Code:
#TransactionManagement(value = TransactionManagementType.CONTAINER)
#TransactionAttribute(value = REQUIRED)
public class WMServiceBOBean {
public void saveInBeanA {
int errorCode = save();
if (errorCode != SUCCESS)
{
ClassX.logError();
ctx.setRollbackOnly();
return errorCode;
}
}
}
Class X Code:
public class classX
{
...
public void logError()
{
ErrorLog e = new ErrorLog;
BeanB beanB = //Local lookup of Bean B
beanB.insertErrorLog (e);
}
...
}
BEAN B Code:
#TransactionManagement(TransactionManagementType.CONTAINER)
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public class ErrorLogDAOBean
{
...
public void insertErrorLog (ErrorLog e)
{
merge (e);
}
...
}
I finally figured out the problem.
Here's the problem, when looking up the ErrorLogBean, i was instantiating a new Persistence Manager. When the transaction gets flagged for rollback, the process of getting a new PM was failing. I know it doesn't make sense to get a new Persistence Manager but it was part of test we are conducting.
Thanks Piotr for all your help in this!

CMT rollback : how to roll back the transaction

The following code does not help in roll back even if I throw null pointer exception at update() method. Everytime it inserts values into the database if I run the code. Please help me how can I roll back the transaction if null pointer is thrown at update() method. Am I missing something in the code?
#TransactionManagement(value = TransactionManagementType.CONTAINER)
public class Bean implements RemoteIF {
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public void insertIntoDb() {
insert();
update();
}
private Integer update() {
val=0;
try {
Connection con = DbConn.getConnection();
Statement st = con.createStatement();
val1 = st.executeUpdate("INSERT INTO tab VALUES('ab')");
st.close();
throw new NullPointerException();
} catch (Exception e) {
System.out.println(e);
}
return val;
}
private Integer insert() {
int val = 0;
try {
Connection con = DbConn.getConnection();
Statement st = con.createStatement();
val = st.executeUpdate("INSERT INTO tab VALUES('bnm')");
st.close();
} catch (Exception e) {
System.out.println(e);
}
return val;
}
}
Couple things that stick out at me as suspect.
No #Stateless, #Stateful or #Singleton annotation on the Bean class. Unless you've declared the bean in an ejb-jar.xml file, then this is not getting recognized as an EJB. Definitely double check that.
The DbConn.getConnection() looks suspiciously like you might be trying to manage database connections yourself. If you have any code that uses the DriverManager or does new FooDataSource(), then that is definitely the problem. If you want transaction management to work you have to get all resources from the container via either
Injection via a #Resource DataSource datasource field in the EJB class
JNDI lookup of java:comp/env/yourDataSource, where yourDataSource is the name of a datasource you configured in the ejb-jar.xml or declared on the bean class via using #Resource(type=DataSource.class, name="youDataSource") -- that annotation goes on the class itself rather than a method or field.
See also these answers for some insight as to how transaction management works:
How does UserTransaction propagate?
Programming BMT - UserTransaction

Resources