How to rollback the first transaction and not rollback the new transaction? - java

I have a method that save a object but I need consumes an API and save their return. If the API return is "not authorized", I need rollback the transaction but I want preserve the return.
E.g.
#Resource
private SessionContext context;
#Transactional
public Invoice createSale(SaleDTO saleDTO) {
this.dao.save(saleDTO);
Send send = this.context.getBusinessObject(Send.class);
Invoice invoice = this.send.send(saleDTO);
if (invoice.isAuthorized()) {
invoice.setSale(saleDTO);
return invoice;
} else {
throw new IllegalArgumentException();
}
}
public class Send implements Serializable {
#Transactional(Transactional.TxType.REQUIRES_NEW)
private Invoice send(SaleDTO saleDTO) {
Invoice invoice;
...
this.dao.save(invoice);
return invoice;
}
}
When I thrown the IllegalArgumentException, the invoice is not saved. I need save it.

Annotating a private method, or even a public method called from another method of the same class, can't work.
Transactional handling is based on proxies.
A transaction can only be started if you call a transactional method on another bean, injected in the current bean, so that the transactional proxy wrapping the other bean intercepts the call and starts a transaction.
Read https://docs.spring.io/spring/docs/current/spring-framework-reference/data-access.html#tx-decl-explained. You don't seem to use Spring, but the way it works in Java EE is the same.

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.

Spring #Transactional anotation not working in a for loop with using try catch

My question is given below. Pseudocode Code is given below:
public Object rollBackTestMainMethod(List<Object> list) {
List<Object> responseList = new ArrayList<>();
for(Object item:list){
try{
Boolean isOperationSuccess = rollBackTestSubMethod(item);
if (isOperationSuccess==null || !isOperationSuccess){
item.addError("Operation failed");
item.addSuccess(false);
} else {
item.addError(null);
item.addSuccess(true);
}
} catch(Exception exception) {
item.addError(exception.getMessage());
item.addSuccess(false);
}
responseList.add(item);
}
return responseList;
}
#Transactional(rollbackFor = {Exception.class, SQLException.class})
private Boolean rollBackTestSubMethod(Object listItem){
Long value1=save(listItem.getValue1());
if(value1==null){
throw new Exception("Error during save 1");
}
Long value2=save(listItem.getValue2());
if(value2==null){
throw new Exception("Error during save 2");
}
Long value3=save(listItem.getValue3());
if(value3==null){
throw new Exception("Error during save 3");
}
return Boolean.TRUE;
}
What I am doing here:
Iterate a list in rollBackTestMainMethod(). Sending one list item in rollBackTestSubMethod() and performing a 3 save operation.
If all save complete then returning true response, otherwise throwing an exception.
In rollBackTestMainMethod(), after getting response or exception, it is adding error or successful value on each item.
It is adding this item in new list named responseList. After all operations it is sending this back as response.
My questions:
After throwing from rollBackTestSubMethod() it will not be rolled back because it is calling from a try catch block.
If I want to forcefully roll back via TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); then it will be rolled back all item for any throw/exception.
Here I want rollback only for throw item not all item.
This method's are in a spring bean
I am saving data into my relational database via spring data jpa
My imports:
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
It's because you're invoking #Transactional method from within same bean.
#Transactional only works on methods invoked on proxies created by spring. It means, that when you create a #Service or other bean, method called from the outside will be transactional. If invoked from within bean, nothing will happen, as it doesn't pass through proxy object.
The easiest solution would be to move the method to another #Service or bean. If you really want to keep it within same component, then you need to invoke it, so that it gets wrapped in proxy by spring AOP. You can do this like that:
private YourClass self;
#Autowired
private ApplicationContext applicationContext;
#PostConstruct
public void postContruct(){
self = applicationContext.getBean(YourClass.class);
}
Then invoking method on self would result in opening a transaction.
Marking a non-public method #Transactional is both useless and misleading because Spring doesn't "see" non-public methods, and so makes no provision for their proper invocation. Nor does Spring make provision for the methods invoked by the method it called.
Therefore marking a private method, for instance, #Transactional can only result in a runtime error or exception if the method is actually written to be #Transactional.

How to pass a JPA entity to an Asynchronous EJB method

While processing a request, I would like to 'kick off' separate task in a separate transaction based on the data received and stored in the database.
The main advantage is that I can return the result to my request before all additional processing is done, also if additional processing fails, this will not affect the original processing of the request (as they are in different transactions). This additional processing might require adding extra information to the data.
I have the following setup in mind. Where using #Asynchronous the additional task is created.
#Stateless
public class Bean1
{
#EJB
Bean2 bean2;
#PersistenceContext
private EntityManager entityManager;
public MyResult doSomething(MyInput input) {
MyEntity myEntity = new MyEntity();
// Fill data based on input
entityManager.persist(myEntity);
bean2.asyncActOnData(myEntity);
// Perhaps do some more work and return result
}
}
#Stateless
public class Bean2
{
#Asynchronous // This causes a new transaction to happen
public void asyncActOnData(MyInput input){
// Act upon data and store result in DB
}
}
This seems like a clean way, however this causes JPA Entity to become detached, possibly during processing in Bean2.
Currently I don't plan on changing the data after the persist call (and asnyc), but as the application grows I feel it would be safer to allow this to avoid mistakes.
What is the correct way to kick off the separate asynchronous task based on the persisted data?
I am using: Java EE 6 with Eclipselink 2.5.2 for JPA.
You can continue to make use of the detached instance provided that:
You're not planning to perform further persistence operations;
All associated entities (if any) were available when asyncActOnData was invoked.
However, if you need to perform further persistence operations you can do the following:
#Stateless
public class Bean1
{
#EJB
Bean2 bean2;
#PersistenceContext
private EntityManager entityManager;
public MyResult doSomething(MyInput input) {
MyEntity myEntity = new MyEntity();
// Fill data based on input
entityManager.persist(myEntity);
// Flush the persistence context and detach the entity
// An entity is not permitted to be in more than one
// persistence context at a time. This should prevent
// a race condition with the merge in bean2.
entityManager.flush();
entityManager.detach(myEntity);
Future<Result> futureResult = bean2.asyncActOnData(myEntity);
// Perhaps do some more work and return result
....
return futureResult.get();
}
}
#Stateless
public class Bean2 {
#PersistenceContext
private EntityManager entityManager;
#Asynchronous
public Future<Result> asyncActOnData(MyInput input) {
// this will ensure that the database still matches input
// and add input into the persistence context
input = entityManager.merge(input);
...
return new javax.ejb.AsyncResult<Result>(result);
}
}
You will find it useful to read §3.2 of the "Java™ Persistence API, Version 2.1" specification.

How to make one JPA repository Transactional but another not?

I have method where I'm trying to change some entity, also in this method I want to save transaction information.
When any exception occurred I want to roll back saving entity but still want to save transaction.
So how to make one repository for entity transactional but repository for transactions not?
There code from repository
#Override
#Transactional(noRollbackFor=NotEnoughAmountInAccountException.class)
<T extends Transaction> T save(T transaction);
but it doesn't help.
Saving transaction placed in final block.
UPDATE
I solved it by using AOP. I create Transaction object in aspect advice and save it here, out of JPA transaction.
Do it in a new #Transactional
#Transactional(propagation=Propagation.REQUIRES_NEW)
<T extends Transaction> T save(T transaction);
This will save your transaction element even if the other #Transactional gets rolled back
I solved it by using AOP. I create Transaction object in aspect advice and save it here, out of JPA transaction.
It's #Transactional method
#SaveTransaction
#Transactional
public synchronized Transaction move(#NonNull String accountName, #NonNull String destinationName, #NonNull BigDecimal amount, String comment) {
checkAmountIsPositive(amount);
Account donor = getAccount(accountName);
Account acceptor = getAccount(destinationName);
if (!isEnoughAmount(accountName, amount)) throw new NotEnoughAmountInAccountException();
BigDecimal newDonorAmount = donor.getAmount().add(amount.negate());
BigDecimal newAcceptorAmount = acceptor.getAmount().add(amount);
donor.setAmount(newDonorAmount);
acceptor.setAmount(newAcceptorAmount);
accountRepository.save(donor);
accountRepository.save(acceptor);
return null;
}
And it's aspect advice
#Around("#annotation(com.softjourn.coin.server.aop.annotations.SaveTransaction)")
public Transaction saveTransaction(ProceedingJoinPoint joinPoint) throws Throwable {
Transaction transaction = prepareTransaction(joinPoint);
try {
joinPoint.proceed();
transaction.setStatus(TransactionStatus.SUCCESS);
return transaction;
} catch (Throwable e) {
transaction.setStatus(TransactionStatus.FAILED);
transaction.setError(e.getLocalizedMessage());
throw e;
} finally {
transactionRepository.save(transaction);
}
}
Also it's important to make order of this advice higher than order of #Transactional so this advice will be over transaction.
Set #Order(100) on aspect class.
By default it's order is smaller so it's under transaction.

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