I have a method annotated with #Transactional(rollbackFor = CustomerRollBackException.class) and it is working as expected when I test it in embebed tomcat.
However, I have it deployed on a jboss wildfly, and the same method is not doing the rollback when it throw the exception..
Do you have any idea if is needed any configuration on jboss?
#Override
#Transactional(rollbackFor = CustomerRollBackException.class)
public void importGenericTable(SheetDTO sheetDTO) throws Exception {
// String tableName, List<Object> rows, UserDTO user
Iterator iterator;
String tableName = sheetDTO.getTableName();
....
try{
..
} catch (ParseException | PersistenceException | SQLGrammarException | ConstraintViolationException e) {
logger.error("importGenericTable. Error " + e);
throw new CustomerRollBackException("E_NPRO_UTIL_IMPORT_0001:" + (countRows + 2));
} catch (CustomerNotFoundException e) {
throw new CustomerRollBackException(e.getMessage());
} catch (Exception e) {
throw new CustomerRollBackException("error desconocido");
}
..
It's entering in the first catch and throwing the CustomerRollBackException and the rollback is not executing.
I think with jboss you should use rollbackOn instead
Solution :
Configure datasource on jboss and use it in the application using
spring.datasource.jndi-name=java:/XXXX
instead of :
spring.datasource.url= jdbc:
spring.datasource.username=
spring.datasource.password=
Related
I was trying to use
#Transactional(noRollbackFor = {SQLIntegrityConstraintViolationException.class, ConstraintViolationException.class, DataIntegrityViolationException.class})
in my serviceImpl class
and following for checking exception:-
try{
dao.saveAll(list)
}catch (Throwable ignore){
...
}
but it is not working, can anyone suggest me how to do that, only add non existing data and avoid the rest.
Remove the noRollbackFor and do this instead:
try {
// flush will execute the SQL statements
dao.saveAllAndFlush(list)
} catch (SQLIntegrityConstraintViolationException | ConstraintViolationException | DataIntegrityViolationException e){
...
}
My problem is that JPA/Hibernate returns true for a call of entityManager.getTransaction().isActive() even when I did not explicitly start a transaction (see code below).
The problem here is that I want to read something from the database and a SerializationException is ok in this scenario, because this just indicates that the persisted object does not fit to the actual code any more and needs to be recalculated. Instead of just returning null the code below throws the following exception:
Transaction rollback failed.
org.hibernate.TransactionException: Unable to rollback against JDBC Connection
This shows me, there must have been a transaction somewhere which I did not start in my code. The finally block in the code above is
final EntityManager entityManager = Persistence.createEntityManagerFactory("test").createEntityManager();
try {
final TypedQuery<Test> query = entityManager.createQuery("SELECT t FROM test t", Test.class);
return query.getResultList();
} catch (final PersistenceException e) {
if (e.getCause() instanceof SerializationException) {
LOG.debug("Implementation changed meanwhile. That's ok - we return null.");
return null;
}
throw e;
} finally {
EntityManagerCloser.closeEntityManager(entityManager);
}
And the EntityManagerCloser looks like this:
public final class EntityManagerCloser {
private static final Logger LOG = LoggerFactory.getLogger(EntityManagerCloser.class);
public static void closeEntityManager(EntityManager entityManager) {
if (entityManager.getTransaction().isActive()) {
try {
entityManager.getTransaction().rollback();
} catch (PersistenceException | IllegalStateException e) {
LOG.error("Transaction rollback failed.", e);
}
}
if (entityManager.isOpen()) {
try {
entityManager.close();
} catch (IllegalStateException e) {
LOG.error("Closing entity manager failed.", e);
}
}
}
}
Hibernate docs says "Always use clear transaction boundaries, even for read-only operations". So do I really need to insert a
entityManager.getTransaction().begin();
....
<do read here>
....
entityManager.getTransaction().commit();
around every read operation I perform on the database?
I could implement another closeEntityManager method for read-only operations without the rollback transaction block but I want to understand why there IS a transaction at all. Thanks for any help!
The problem is that when you call entityManager.getTransaction(); a new transaction object will be created. So it is better to save the transaction reference to a variable as shown below.
Transaction txn = entityManager.getTransaction();
if (txn.isActive()) {
try {
txn.rollback();
} catch (PersistenceException | IllegalStateException e) {
LOG.error("Transaction rollback failed.", e);
}
}
Thanks to Jobin I quickly found the solution to my problem:
I think I need to call entityManager.isJoinedToTransaction() in my closeEntityManager method before calling entityManager.getTransaction().isActive().
This will prevent the EntityManagerCloser to start its own transaction which I can not rollback later because I did not explicitly call transaction.begin() on it.
I'm making some tests with Hazelcast XA Transactions and I'm having trouble when using Bitronix as the Transaction Manager.
I have followed Hazelcast official documentation:
http://docs.hazelcast.org/docs/3.5/manual/html/xatransactions.html
Libs versions:
Hazelcast: 3.5.4
Bitronix Transaction Manager: 2.1.4
Java: 1.7
Code:
Test method:
#Test
public void hazelcastBitronixXATransactionTest() throws Exception{
try{
doHazelcastXATransactionTest(createInstance("myCluster"));
}catch(Exception e){
Assert.fail();
System.out.println("Other Exception:" + e.getMessage());
}
}
Hazelcast instance creation method:
private HazelcastInstance createInstance(String clusterName){
System.setProperty("hazelcast.logging.type", "slf4j");
Config config = new Config();
config.getGroupConfig().setName(clusterName);
config.getNetworkConfig().getJoin().getMulticastConfig().setEnabled(true);
TcpIpConfig tcpIpConfig = config.getNetworkConfig().getJoin().getTcpIpConfig();
tcpIpConfig.setEnabled(false);
return Hazelcast.newHazelcastInstance(config);
}
Do method:
private void doHazelcastXATransactionTest(HazelcastInstance hazelcastInstance) throws Exception{
BitronixTransactionManager btm = TransactionManagerServices.getTransactionManager();
btm.setTransactionTimeout(60);
btm.begin();
HazelcastXAResource xaResource = hazelcastInstance.getXAResource();
Transaction transaction = btm.getTransaction();
transaction.enlistResource(xaResource);
try {
TransactionContext context = xaResource.getTransactionContext();
TransactionalMap map = context.getMap("m");
map.put("key", "value");
transaction.delistResource(xaResource, XAResource.TMSUCCESS);
btm.commit();
} catch (Exception e) {
System.out.println("Exception do rollback:" + e.getMessage());
btm.rollback();
}
}
I'm receiving a BitronixSystemException, thrown when trying to enlist the XA resource transaction.enlistResource(xaResource).
The complete stacktrace:
bitronix.tm.internal.BitronixSystemException: unknown XAResource HazelcastXaResource {myCluster}, it does not belong to a registered resource
Someone has faced this issue? Any clues on this?
Apparently Bitronix needs XAResources to be registered before using them.
I've added below line after obtaining the HazelcastXAResource and it worked.
EhCacheXAResourceProducer.registerXAResource(xaResource.getName(), xaResource);
I am using MS SQL Server, and my program recently started losing the DB connection randomly. I am using a non-XA driver.
The most likely suspect is the asynchronous database logging I added.
The sneaky thing is, I have used a thread pool:
ExecutorService ruleLoggingExecutor = Executors.newFixedThreadPool(10);
and in the finally block of my process, I start off a new thread that calls down to the addLogs() method.
The code works for hours, days, and then during a totally unrelated query, it will lose the DB connection. I have an inkling that the problem is that two concurrent inserts are being attempted. But I don't know if putting 'synchronized' on the addLogs method would fix it, or if I need transactional code, or what. Any advice?
In the DAO:
private EntityManager getEntityManager(InitialContext context) {
try {
if (emf == null) {
emf = (EntityManagerFactory) context
.lookup("java:jboss/persistence/db");
}
return emf.createEntityManager();
} catch (Exception e) {
logger.error(
"Error finding EntityManagerFactory in JNDI: "
+ e.getMessage(), e);
return null;
}
}
public void addLogs(InitialContext context, String key, String logs,
String responseXml) {
EntityManager em = getEntityManager(context);
try {
TblRuleLog log = new TblRuleLog();
log.setAuthKey(key);
log.setLogMessage(logs);
log.setDateTime(new Timestamp(new Date().getTime()));
log.setResponseXml(responseXml);
em.persist(log);
em.flush();
} catch (Exception e) {
logger.error(e.getMessage(), e);
} finally {
em.close();
}
}
It seems the connection is closed after a timeout, perhaps due to transaction not being commited/rolled back (and locks not being released on the tables/rows).
Manual flushing looks suspicious. I'd use entityManager.getTransaction().begin/commit() and remove em.flush().
I want that if i get an E-Mail Exception, to NOT rollback the transaction.
I am using HibernateTransactionManager and
set property name="nestedTransactionAllowed" value="true"
because i have nested transactions.
Also because i call this.getService() i have set
lookup-method name="getService" bean="enrollmentProcessorService" .
This way i should get the Spring Proxy.
But if exceptions occurs, the transaction is still rolledback.
my code looks like this:
#Override
#Transactional(propagation = Propagation.REQUIRED, readOnly = false)
public void processConfirmation() throws SystemException {
//do something
this.getService().processConfirmationData(as400ContractId);
}
#Transactional(propagation = Propagation.REQUIRES_NEW, noRollbackFor = {MailException.class})
public void processConfirmationData(final long as400ContractDataId) throws SystemException {
final AS400ContractData as400ContractData = this.readAS400ContractData(as400ContractDataId, false);
this.populateEnrollmentOptionAnswers(as400ContractData.getContractData());
final PersonalData personalData = this.readPersonalData(as400ContractData.getContractData()
.getEpiphanyPersonalData().getPersonalData().getId(), true);
try {
personalData.setConfirmMailSent(true);
as400ContractData.getContractData().getEpiphanyPersonalData().getPersonalData().setConfirmMailSent(true);
this.personalDataDAO.flush();
this.emailService.sendConfirmationMailToLOI(as400ContractData); //commit if exception is thrown here
} catch (final DataAccessException dae) {
LOGGER.error(CANNOT_UPDATE_PERSONAL_DATA_OBJECT, dae);
throw new SystemException(StringUtils.EMPTY, CANNOT_UPDATE_PERSONAL_DATA_OBJECT, dae);
} catch (final MessagingException e) {
LOGGER.error(CANNOT_SEND_CONFIRMATION_EMAIL, e);
throw new SystemException(StringUtils.EMPTY, CANNOT_SEND_CONFIRMATION_EMAIL, e);
} catch (final MailException e) {
Throwable rootCause = e.getRootCause();
System.out.println("caught");
First, you catch the exception, so there is no way the Spring interceptor can see it and rollback the transaction.
And then, even if you didn't catch it, you've configured the method with **no**RollbackFor = {MailException.class}. This means that you don't want a rollback if this exception is thrown. Use rollbackFor = {MailException.class} instead.
Whenever you get exception, current transaction is rollbacked. No rollback rules, for those times when you do not want a transaction to be marked for rollback when an exception is thrown. Try to remove noRollbackFor annotation.
Also you should rethrow exception. Don't catch MailException.
Yesterday, I encountered the same problem.
When I saw the source :org.springframework.transaction.interceptor.TransactionInterceptor#invoke.
I found where the problem is.
this.emailService.sendConfirmationMailToLOI is another Service, so the TransactionAttribute is new.
you can fix this problem :
#Transactional(propagation = Propagation.REQUIRES_NEW, noRollbackFor = {MailException.class})
In this method(emailService.sendConfirmationMailToLOI*), the configuration above