Spring Transaction | Not Rollback some fraction of code - spring

I need to write a method which is being called from a open transaction & will never rollback, even though exception occurs in the system. I used Propagation.NEVER to achieve this, Is it fine, or I should use PROPAGATION_NOT_SUPPORTED?
#Autowired
private PartnerTransactionService partnerTransactionService;
#PostConstruct
#Transactional
private void test(AuditDTO<Object> audit){
partnerTransactionService.saveAudits(audit);
partnerTransactionService.nonTrasactionsalSaveAudits(audit);
throw new NullPointerException();
}
In some other service class(both are spring managed bean ie sprinf proxy is being created for both)-
public <T> void saveAudits(AuditDTO<T> audit){
if(audit!=null){
PartnerTransaction partnerTransaction=new PartnerTransaction(audit);
partnerTransactionRepository.save(partnerTransaction);
}
}
#Transactional(propagation = Propagation.NEVER)
public <T> void nonTrasactionsalSaveAudits(AuditDTO<T> audit){
if(audit!=null){
PartnerTransaction partnerTransaction=new PartnerTransaction(audit);
partnerTransactionRepository.save(partnerTransaction);
}
}
I throws a NPE to check my code , I found both the data being save irrespective of exception, non being rollback, what I am missing?

Related

How to rollback child transaction if any exception in parent transaction?

I have two transaction manager for two database. I need to persist same data into both databases. If one transaction failed, other one need rollback. I have done like below
public interface DataService {
void saveData();
}
#Service
public class DataServiceImpl implements DataService {
#Autowired
private DataRepository dataRepository;
#Autowired
private OrDataRepository orDataRepository;
#Autowired
#Qualifier("orService")
private OrService orDataServiceImpl;
#Override
#Transactional(transactionManager = "transactionManager", rollbackFor = {RuntimeException.class})
public void saveData() {
Data data = new Data();
data.setCompKey(UUID.randomUUID().toString().substring(1,5));
data.setName("data");
dataRepository.save(data);
orDataServiceImpl.save();
//throw new RuntimeException("");
}
}
public interface OrService {
void save();
}
#Service("orService")
public class OrDataServiceImpl implements OrService {
#Autowired
private OrDataRepository orDataRepository;
#Override
#Transactional(rollbackFor = {RuntimeException.class})
public void save() {
OrData data = new OrData();
data.setCompKey(UUID.randomUUID().toString().substring(1,5));
data.setName("ordata");
orDataRepository.save(data);
}
}
I have two transaction manager (entityManager & orEntityManager) for two different DB.
If any exception in OrDataServiceImpl save method, data is not getting persisted in both DB. But if any exception in DataServiceImpl saveData method, data is getting persisted into OrData table.
I want to rollback the data from both DB if any exception.
chainedTransactionManager is deprecated. So can't use. atomikos and bitronix also can't use due to some restrictions. Kindly suggest better way to achieve distributed transation
The code need to be refactored, edit the DataServiceImpl.save() method.
Comment the orDataServiceImpl.save() line
public void saveData() {
Data data = new Data();
data.setCompKey(UUID.randomUUID().toString().substring(1,5));
data.setName("data");
dataRepository.save(data);
//orDataServiceImpl.save();
//throw new RuntimeException("");
}
Refactor/Edit the OrDataService Interface
public interface OrDataService {
void save(String uuid);
void delete(String uuid);
//will be use for compensating transaction
}
Update the OrDataServiceImpl class to implement above interface
Write new orchestration Method and use compensating transaction to rollback
pseudo code
call OrDataServiceImpl.save()
if step#1 was success
-> DataServiceImpl.saveData()
if Exception at step#3,
->OrDataServiceImpl.delete() [//to rollback]
else if, Exception at step#1
//do nothing

Transactional Propagation.REQUIRES_NEW not work

I try save list of entities to Oracle Db.
#Transactional
public void save() {
//logick
for (QuittanceType quittanceType : quittance) {
quittancesService.parseQuittance(quittanceType);
}
//logick
}
On each step I call this method:
#Transactional
#Override
public void parseQuittance(QuittanceType quittance) {
try {
//logick create payToChargeDb
paymentToChargeService.saveAndFlush(payToChargeDb);
} catch (Exception e) {
log.warn("Ignore.", e);
}
}
and method
#Override
public PaymentsToCharge saveAndFlushIn(PaymentsToCharge paymentsToCharge) {
return paymentToChargeRepository.saveAndFlush(paymentsToCharge);
}
When I try save entity with constraint My main transaction rollback and I get stacktrace:
Caused by: java.sql.BatchUpdateException: ORA-02290: CHECK integrity constraint violated(MYDB.PAYMENTS_TO_CHARGE_CHK1)
But I want skip not success entities and save success. I marck my method
#Transactional(propagation = Propagation.REQUIRES_NEW)
and it look like this:
#Transactional
#Override
public void parseQuittance(QuittanceType quittance) {
try {
//logick create payToChargeDb
paymentToChargeService.saveAndFlushInNewTransaction(payToChargeDb);
} catch (Exception e) {
log.warn("Ignore.", e);
}
}
and
#Transactional(propagation = Propagation.REQUIRES_NEW)
#Override
public PaymentsToCharge saveAndFlushInNewTransaction(PaymentsToCharge paymentsToCharge) {
return paymentToChargeRepository.saveAndFlush(paymentsToCharge);
}
But when I try save entity with constraint I not get exception and not enter to catcj block. just stop working debugging and the application continues to work. I do not get any errors. and as if rollback is happening
The proxy created by #Transactional does not intercept calls within the object.
In proxy mode (which is the default), only external method calls
coming in through the proxy are intercepted. This means that
self-invocation (in effect, a method within the target object calling
another method of the target object) does not lead to an actual
transaction at runtime even if the invoked method is marked with
#Transactional. Also, the proxy must be fully initialized to provide
the expected behavior, so you should not rely on this feature in your
initialization code (that is, #PostConstruct).
https://docs.spring.io/spring/docs/current/spring-framework-reference/data-access.html#transaction-declarative
The same documentation recommends use of AspectJ if you want this behaviour.

Save an Object after an error Occured in the current Transaction

Hi i have the following construction, which runs a raw query and then saves the result. But when an exception occoured on the raw queries, i cannot save my Object any more to indicate that an error happened
[c3p0] A PooledConnection that has already signalled a Connection error is still in use!
The classes:
#Service("myService")
#Transactional(propagation = Propagation.REQUIRED)
public class MyService {
cron(){
Job job = myDao.nextJob();
try {
myDao.myFunction(job);
job.setStatus(Status.COMPLETE);
} catch (Exception e) {
job.setStatus(Status.ERROR);
}
myDao.save(job);
}
}
#Repository("myDao")
public class MyDao extends HibernateDaoSupport {
#Autowired
public void setHibernateSessionFactory(#Qualifier("sessionFactory") SessionFactory sessionFactory) {
setSessionFactory(sessionFactory);
}
public void myFunction(final Job job) {
final Session session = getCurrentSession();
session.flush();
session.doWork(new Work() {
#Override
public void execute(Connection con) throws SQLException {
// do something which creates an SQL error
}
}
}
}
what can i do to get a new working session to get my object saved?
1) It looks like you are not releasing the connection to the connection pool after an error, and trying to reuse it.
2) Instead of final Session session = getCurrentSession() create a new session using openSession() as below:
SessionFactory sessionFactory = HibernateUtil.getSessionAnnotationFactory();
final Session session = sessionFactory.openSession();
Try to create new session for each transaction, if you use getCurrentSession and any error occurs or not in the transaction, then the maintainence(closing/flushing etc) of the session object is done by hibernate and not by the programmer. Make use of getCurrentSession for single threaded environment.
3) By default a Connection object is in auto-commit mode, which means that it automatically commits changes after executing each statement. Check whether have you altered its property? Use Connection.commit() to commit the connection.

JMS doesn't rollback XA transaction (or doesn't participate in one)

I'm relatively new to XA transactions. I've been struggling a few days to make a simple XA transaction work to no avail.
First, I tried to use two different databases. I set up 2 XA datasources and had succeeded in rolling back the first database operation when the second fails. So far, so good. But then I tried to replace second datasource with JMS connectionFactory and cannot reproduce the same behavior.
Here's the relevant code:
Database logic:
#Stateless
public class FirstDB implements FirstDBLocal {
#PersistenceContext(unitName = "xaunit")
private EntityManager em;
public void doSomething() {
SomeEntity someEntity = em.find(SomeEntity.class, 1234L);
someEntity.setSomeFlag(false);
}
}
JMS code:
#Stateless
public class SecondJMS implements SecondJMSLocal {
#Resource(mappedName = "java:/JmsXA")
private ConnectionFactory connFactory;
#Resource(mappedName = "queue/Some.Queue")
private Queue q;
#Override
#TransactionAttribute(TransactionAttributeType.MANDATORY)
public void sendMsg() {
Session session = null;
Connection conn = null;
MessageProducer producer = null;
try {
conn = connFactory.createConnection("guest", "guest");
session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
producer = session.createProducer(q);
// Not sure if I need this, but I found it in the sample code
conn.start();
TextMessage tm = session.createTextMessage(new Date().toString());
producer.send(tm);
throw new RuntimeException("Fake exception");
} catch (JMSException e) {
e.printStackTrace();
} catch (RuntimeException e) {
e.printStackTrace();
} finally {
// close all resources
}
}
}
The glue code:
#Stateless
public class TestDBandJMS implements TestDBandJMSLocal {
#EJB
private FirstDBLocal firstDBLocal;
#EJB
private SecondJMSLocal secondJMSLocal;
public void doStuff() {
firstDBLocal.doSomething();
secondJMSLocal.sendMsg();
}
}
XA Connection Factory configuration (everything is JBoss default, except for commented out security settings):
<tx-connection-factory>
<jndi-name>JmsXA</jndi-name>
<xa-transaction/>
<rar-name>jms-ra.rar</rar-name>
<connection-definition>org.jboss.resource.adapter.jms.JmsConnectionFactory</connection-definition>
<config-property name="SessionDefaultType" type="java.lang.String">javax.jms.Topic</config-property>
<config-property name="JmsProviderAdapterJNDI" type="java.lang.String">java:/DefaultJMSProvider</config-property>
<max-pool-size>20</max-pool-size>
<!-- <security-domain-and-application>JmsXARealm</security-domain-and-application> -->
<depends>jboss.messaging:service=ServerPeer</depends>
</tx-connection-factory>
I also have very simple MDB which just prints out received message to console (not going to post the code, since it's trivial).
The problem is, when the exception is thrown in JMS code, the message is still received by MDB and SomeEntity is successfully updated in the database code (whereas I expect it to rollback).
Here is the JMS log. One fishy thing that I see there is this:
received ONE_PHASE_COMMIT request
Like I said, I'm not too familiar with XA yet, but I expect to see here TWO_PHASE_COMMIT, because there should be 2 resources which participate in the active transaction.
Any help would be much appreciated.
UPDATE
It worked eventually, after I tried #djmorton's suggestion.
One other important thing to keep in mind when working with JBoss 5.1 is that the lookup name for XA JMS ConnectionFactory is "java:/JmsXA". I tried the same with
#Resource(mappedName = "XAConnectionFactory")
private ConnectionFactory connFactory;
and it didn't work.
You are catching your RuntimeException after throwing it in your sendMsg() method. The Exception will not trigger a transaction rollback unless it is thrown up the stack. When using Container managed transactions, the container adds interceptors to the method calls to setup the transactions and handle rollbacks when unchecked exceptions are thrown. If the exception isn't thrown out of the method the interceptor doesn't know it needs to role the transaction back.
Edit 1:
Note that only a RuntimeException or a subclass of RuntimeException being thrown will cause the transaction to rollback. A checked exception (One that extends Exception rather than RuntimeException) will not cause a rollback unless it is annotated with #ApplicationException(rollback=true).
The other alternative is to inject an EJBContext object, and call .setRollbackOnly() to force the transaction to rollback when the method goes out of scope:
#Stateless
public class SomeEjb {
#Resource
private EJBContext context;
#TransactionAttribute(TransactionAttributeType.MANDATORY)
public void rollMeBack() {
context.setRollbackOnly();
}
}

Spring Remoting HTTP invoker - exception handling

I'm using Spring's 'HTTP Invoker' remoting solution to expose DAOs to many different applications, but have all database access in a single server.
This works well, but if the server throws, say, a HibernateSystemException, Spring serializes that and sends it over the wire back to the client. That doesn't work because the client doesn't (and shouldn't) have HibernateSystemException in its classpath.
Might there be a way to have Spring Remoting wrap my exception in something that I specify that would be common between client and server to avoid issues like this?
I know that I could do that in my server code by wrapping everything the DAO does in a try/catch, but that's admittedly sloppy.
Thanks,
Roy
I ran into this issue as well; I am exposing a service via HTTP Invoker that accesses a database using Spring 3.1, JPA 2, and Hibernate as the JPA provider.
To work around the problem, I wrote a custom Interceptor and an exception called WrappedException. The interceptor catches exceptions thrown by the service and converts the exceptions and causes to WrappedException using reflection and setters. Assuming the client has the WrappedException on its class path, the stack trace and original exception class names are visible to the client.
This relaxes the need for the client to have Spring DAO on its class path and as far as I can tell, no original stack trace information is lost in the translation.
Interceptor
public class ServiceExceptionTranslatorInterceptor implements MethodInterceptor, Serializable {
private static final long serialVersionUID = 1L;
#Override
public Object invoke(MethodInvocation invocation) throws Throwable {
try {
return invocation.proceed();
} catch (Throwable e) {
throw translateException(e);
}
}
static RuntimeException translateException(Throwable e) {
WrappedException serviceException = new WrappedException();
try {
serviceException.setStackTrace(e.getStackTrace());
serviceException.setMessage(e.getClass().getName() +
": " + e.getMessage());
getField(Throwable.class, "detailMessage").set(serviceException,
e.getMessage());
Throwable cause = e.getCause();
if (cause != null) {
getField(Throwable.class, "cause").set(serviceException,
translateException(cause));
}
} catch (IllegalArgumentException e1) {
// Should never happen, ServiceException is an instance of Throwable
} catch (IllegalAccessException e2) {
// Should never happen, we've set the fields to accessible
} catch (NoSuchFieldException e3) {
// Should never happen, we know 'detailMessage' and 'cause' are
// valid fields
}
return serviceException;
}
static Field getField(Class<?> clazz, String fieldName) throws NoSuchFieldException {
Field f = clazz.getDeclaredField(fieldName);
if (!f.isAccessible()) {
f.setAccessible(true);
}
return f;
}
}
Exception
public class WrappedException extends RuntimeException {
private static final long serialVersionUID = 1L;
private String message = null;
public void setMessage(String message) {
this.message = message;
}
#Override
public String toString() {
return message;
}
}
Bean Wiring
<bean id="exceptionTranslatorInterceptor" class="com.YOURCOMPANY.interceptor.ServiceExceptionTranslatorInterceptor"/>
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames" value="YOUR_SERVICE" />
<property name="order" value="1" />
<property name="interceptorNames">
<list>
<value>exceptionTranslatorInterceptor</value>
</list>
</property>
</bean>
I can understand you don't want your clients to have HibernateSystemException in their classpath, but I'd argue they should, if you're using HTTPInvoker appropriately. It's not designed to be a service facade / interface layer: all it's meant to do is let you run Java methods on a remote JVM, using HTTP instead of RMI.
So if you really don't want the clients to have a dependency on Hibernate, your try/catch block is the way to go. (I'd argue against that though, since it'll make debugging a pain: your stack trace will now be divided between the client and the server).
I haven't used it myself but you could try the org.springframework.remoting.support.RemoteExporter.setInterceptors(Object[]) method to add an aspect to catch that particular exception in just one place rather than adding try/catches all over the place.
I would argue a try/catch in a Facade layer in front of your DAOs is exactly what you want, in order to gain full control over the exceptions you return. I agree it initially feels ugly, but it's a vital layer between client and DAO, in my opinion.
You might even return a OperationStatus object of some sort, rather than use void return types, to convey both outcome (worked, didn't) and error message, for store-data API calls.
I used a solution similar to N1H4L's but with AspectJ.
First I made all the exceptions I want the client to be aware of to extend the class BusinessException (which in my case is a very simple subclass of RuntimeException in the jar with the service interface and DTOs).
Since I don't want the client to know much about the internals of the service I just say "Internal server error".
package com.myproduct.myservicepackage;
import com.myproduct.BusinessException;
import org.aspectj.lang.*;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
#Aspect
#Component
public class InternalServerErrorExceptionAspect {
#Pointcut("execution(public * com.myproduct.myservicepackage..*Service.*(..))")
public void publicServiceMethod() {}
#Around("publicServiceMethod()")
public Object hideNonBusinessExceptions(ProceedingJoinPoint jp) throws Throwable {
try {
return jp.proceed();
} catch (BusinessException e) {
throw e;
} catch (RuntimeException e) {
e.printStackTrace();
throw new RuntimeException("Internal server error.")
}
}
}
Here's the BusinessException class:
package com.myproduct.BusinessException;
public class BusinessException extends RuntimeException {
private static final long serialVersionUID = 8644864737766737258L;
public BusinessException(String msg) {
super(msg);
}
}
I was using AspectJ to wrap exception but it does not work for exception that occur in Spring proxy, e.g. annotation #Transactional when the connection to the database fails.
However, the method setInterceptor on RmiServiceExporter works perfectly.

Resources