Why my spring #Transactional with REQUIRED can't rollback? - java

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).

Related

Throw exceptions forcefully in JUnit Testing

I was going to increase the code coverage. In the process, I was unable to test the catch block of a specific method. I was trying to force exceptions to the method call.
RuleSchedule.java
public class RuleScheduler
{
private boolean enabled;
private Integer batchSize;
#Autowired
private ExternalService externalService;
public void executeRuleValidation()
{
if (enabled)
{
try
{
StopWatch stopWatch = new StopWatch();
stopWatch.start();
externalService.executeRuleValidationForProposal(batchSize);
stopWatch.stop();
log.info("Total time taken for RuleValidation:{} for batchSize :{}",
stopWatch.getTotalTimeSeconds(), batchSize);
}
catch (Exception ex)
{
// System.out.println("~~~~~~CATCH~~~~~~");
log.error("Rule exception :{}", ex);
}
}
}
}
I was trying different ways to throw exceptions to get into the catch block of the above code, but was unable to do so. And here is my Test class. Even the batchsize is accepting null values.
RuleScheduleTest.java
#RunWith(MockitoJUnitRunner.class)
public class RuleSchedulerTest
{
#Mock
private ExternalService externalService;
#InjectMocks
private RuleScheduler RuleScheduler;
#Before
public void init()
{
ReflectionTestUtils.setField(RuleScheduler, "enabled", true);
ReflectionTestUtils.setField(RuleScheduler, "batchSize", 5);
}
#Test
public void testExecuteRuleValidation()
{
RuleScheduler.executeRuleValidation();
}
}
One possibility would be to configure your mock externalService instance to throw an exception. You should be able to then verify the interaction with the mock. Something like the following:
#Test
public void testExecuteRuleValidation(){
// configure your mock 'externalService' instance to throw an exception
when(externalService.executeRuleValidationForProposal(any(Integer.class))).thenThrow(IllegalArgumentException.class);
RuleScheduler.executeRuleValidation();
// now verify mock interaction
verify(externalService, times(1)).executeRuleValidationForProposal(any(Integer.class));
}

Spring Transaction #Transactional annotation not working

#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)

EJB Interceptor. Catch exception if transaction is not committed

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?

commit changes in try-catch inside #Transactional

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();
}
}

Junit test class which scans list of classes in a package and verifies whether every method contains begin and close transactions

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());
}
}

Categories