Consider I have a method doing some stuff and logging mechanism in ActiveRecord pattern style:
#Transactional
public void doSmt() {
try {
someOperation(); // can throw exception
Logger.logIntoDb(); // if everything is OK
} catch {Exception e} {
Logger.logIntoDbWithException(e.getMessage()); // log error into db
throw new MyCustomException(e);
}
}
public static void logIntoDbWithException(String message) {
final LogEntry logEntry = new LogEntry();
logEntry.setDate(new Date());
logEntry.setMessage(message);
logEntry.persist();
}
I want to persist an error message in case of failure, but if I'd rethrow exception in catch clause transaction will be rollbacked and my LogEntry will not be persisted. The only way I see is manually call flush() after persist().
Is there is a more clean solution to perform this?
Thanks.
UPD:
Since I have a static method which performs persisting I need to apply following hack to accepted answer:
public static void logIntoDbWithException(String message) {
new Runnable() {
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void run() {
final LogEntry logEntry = new LogEntry();
logEntry.setDate(new Date());
logEntry.setMessage(message);
logEntry.persist();
}
}.run();
}
Firstly, calling flush() is not going to do you any good: flush() does not commit anything and, as you are logging the error in the same transaction, the insert will be rolled back.
You therefore need to start a new 'nested' transaction to log the error.
public class A {
#Autowired
private B b;
#Transactional
public void doSmt() {
try {
someOperation(); // can throw exception
b.logIntoDb(); // if everything is OK
} catch {Exception e} {
b.logIntoDbWithException(e.getMessage()); // log error into db
throw new MyCustomException(e);
}
}
}
public class B{
//outer transaction is still active
public void logIntoDb(String message) {
final LogEntry logEntry = new LogEntry();
logEntry.setDate(new Date());
logEntry.setMessage(message);
logEntry.persist();
}
// 'outer' transaction will be suspended until this method completes
// this new transaction will commit/rollback independently of the outer transaction
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void logIntoDbWithException(String message) {
final LogEntry logEntry = new LogEntry();
logEntry.setDate(new Date());
logEntry.setMessage(message);
logEntry.persist();
}
}
Related
#Transactional(rollbackFor = someException.class)
public void methodA() throws someException {
try {
methodB();
} catch (someException e) {
throw e;
}
}
public void methodB() throws someException {
try {
someManager.save(object); // This object should only save when the whole transaction is committed
callToSomeOtherServer(); // This call fails and throws exception
} catch () {
throw new someException();
}
}
According to my understanding, in methodB() we are saving an object with someManager and calling another function callToSomeOtherServer(). So this is part of a transaction in the upper method. If callToSomeOtherServer() fails and throws someException, the whole transaction should be rolled back and the saved object should not reflect in the DB.
But this is not working for me, the object is reflected in DB. Can someone help and make me understand why is it not working?
Try This!
#Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED, readOnly = false, timeout = 100, rollbackFor = Exception.class)
I have an EJB Interceptor which should catch and process all exception which was thrown in transaction:
public class ExceptionsInterceptor {
#AroundInvoke
public Object intercept(final InvocationContext invocationContext) throws Exception {
try {
return invocationContext.proceed();
} catch (Exception e) {
throw new MyException(e);
}
}
}
But if during hibernate flush thrown PesistenceException because of constraint violation I can't catch this exception. I understood
that hibernate do flush after my Interceptor finish work.
But I need catch all exception.
To implement this I've decorate this EJB by other EJB.
#Stateless
#TransactionAttribute(TransactionAttributeType.REQUIRED_NEW)
public class TargetServiceBean implements TargetServiceLocal {
#Override
public boolean method1(Integer digit) {
.. some code which my generate exception..
}
}
#Stateless
#Interceptors(ExceptionsInterceptor.class)
public class TargetServiceBean implements TargetServiceDecorator {
#Inject
private TargetServiceLocal service;
#Override
public boolean method1(Integer digit) {
service.method1(digit);
}
}
It works but looks like workaround and I don't like this solution. So basically I need to run interceptor out of transaction. How can I do this?
I have a situation when some cassandra queries may fail and I want to rethrow the exception back to the calling method where I need to perform some task based on the exception occurred, I am not able to achieve it with possible approached available like registering uncaughtExceptionHandler mechanism or by adding a callback handler to the Future object returned by the executeAsync() method of cassandra driver. Please help me out.
ThreadPoolExcecutor.java-
class ThreadPoolExecutor {
private static ThreadPoolExecutor instance;
ExecutorService executor;
public static synchronized ThreadPoolExecutor getInstance()
{
if( instance == null )
{
instance = new ThreadPoolExecutor();
}
return instance;
}
private ThreadPoolExecutor()
{
executor = Executors.newFixedThreadPool(16);
}
}
My Runnable class -
class CassanndraExecCommand implements Runnable {
private final Session session;
private final Statement statement;
public CassanndraExecCommand( Session session, Statement statement )
{
this.session = session;
this.statement = statement;
}
#Override
public void run()
{
ResultSetFuture future = session.executeAsync(statement);
Futures.addCallback(future, new FutureCallback<ResultSet>() {
#Override
public void onSuccess( ResultSet result )
{
// do nothing";
}
#Override
public void onFailure( Throwable t )
{
throw new RuntimeException(t);
}
});
}
}
As you can see above that I am rethrowing the exception in case of failure. Now question is how do I catch this exception in my calling method which is given below -
public static void executeSaveorUpdate( Statement statement, String keyspaceName )
{
ThreadPoolExecutor.getInstance().executor
.execute(new CassanndraExecCommand(getSession(keyspaceName), statement));
}
Note- cassandra version I am using is 2.2.6
You cannot catch exception that has been thrown in a different thread.
So, all you can do is to handle exception using a callback.
That's the price we all pay for async non-blocking calls performance.
Btw, if you need callback for failure only, you can omit "onSuccess":
Futures.addCallback(future,
new MoreFutures.FailureCallback<T>() {
#Override
public void onFailure(Throwable t) {
//Do whatever you need with t
}
},
executor);
I suggest to use ExecutorService.submit(Runnable) method instead of execute. Submit will return Future<T> object for you, so you can easily handle exception if any.
Future<Void> result = ThreadPoolExecutor.getInstance().executor
.submit(new CassanndraExecCommand(getSession(keyspaceName), statement));
try {
result.get();
} (catch ExecutionException e) {
// e.getCause() will return your root exception
}
Hope it helps!
Aside, I think it will be better to implement Callable instead of Runnable with CassanndraExecCommand. This allows you to handle Cassandra response completely (both good and bad) and avoid callbacks.
I try to write a very simple code to test #Transactional, but it won't rollback when I use Propagation.REQUIRED. Here is the code.
#Component
public class A {
private JdbcTemplate jdbcTemplate;
#Resource(name="dataSource")
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
#Transactional(propagation=Propagation.REQUIRED)
public void add(Person person) throws Exception {
try {
String sql = "insert into person (id, name) values(?,?)";
jdbcTemplate.update(sql, new Object[{person.getId(),person.getName()});
} catch (Exception e) {
throw new RuntimeException();
}
#Transactional(propagation=Propagation.REQUIRED)
public void delete(String id) throws Exception {
throw new RuntimeException();
***throw a RuntimeException on purpose***
}
}
public class cases {
#Transactional
public static void testPro() throws Exception {
try {
AbstractApplicationContext aac = new ClassPathXmlApplicationContext("beans.xml");
A a = (A) aac.getBean("a");
a.add(***a random person***);
a.delete("99");
} catch (Exception e) {
throw new RuntimeException();
}
}
#Test
public void test() throws Exception {
testPro();
}
}
When I test add() method alone by creating a new RuntimeException(), it will rollback. This is what I expected. However, when I run the test() method, the add() method won't rollback when delete() throws a new RuntimeException. It means the add() and delete() are not in a same transaction, but what I want is all the things to be rollback. Please help.
#Transactional on testPro() has no effect for 3 separate reasons:
testPro() is static.
Call is internal from test() in same class.
Instance of class cases not created by Spring.
That means that add() and delete() are running in two separate transactions.
To prove it, try changing the propagation on delete() to MANDATORY. It will now throw exception saying that transaction is not in progress (or something to that effect).
I am looking to implement the following functionality.
I need a Junit test class which scans list of classes in a package and verifies whether every method contains begin and close transactions. Any pointers in this regard will is appreciated.
I'm not going to answer your question directly because I think you are not going the right way. A better design would ensure you the property you want to test. You can do something like :
public interface Transaction {
void initiate() throws Exception;
void execute() throws Exception;
void rollBack();
void close();
}
public TransactionManager {
public void executeTransaction(Transaction transaction) {
try {
transaction.initiate();
transaction.execute();
} catch (Exception e) {
transaction.rollBack();
} finally {
transaction.close();
}
}
}
And then, it becomes easy to test :
public class TestTransaction implements Transaction {
private boolean initiated, executed, rollBacked, closed;
#Override
public initiate() { initiated = true; }
// ...
}
public class FailingTestTransaction extends TestTransaction {
#Override
public execute() throws Exception {
super.execute();
throw new Exception("Voluntary failure");
}
// ...
}
public TransactionManagerTest {
private TransactionManager transactionManager;
#Before
public void setUp() {
this.transactionManager = new TransactionManager();
}
#Test
public void initiateAndCloseOnNormalExecution() {
TestTransaction transaction = new TestTransaction();
transactionManager.executeTransaction(transaction);
assert(transaction.isInitiated() && transaction.isClosed());
}
#Test
public void initiateRollbackAndCloseOnFailure() {
TestTransaction transaction = new FailingTestTransaction();
transactionManager.executeTransaction(transaction);
assert(transaction.isInitiated() && transaction.isRollbacked && transaction.isClosed());
}
}