How to handle reversal with JPA? - java

I am using Spring and JPA for a financial data handling project. I have to handle scheduling transactions for future dates. In a daily run quartz cron job and execute all schedule transaction and persist to real table.
My problem is, when going to execute trigger for a given time, one record failed due to some reason, then all other records were not executed.
I need to execute all other transactions and failed transactions should rollback.
Is there a way to handle these things?
following code block get all the schedule job
public void bankingPaymentSchedullerRun(){
// get all pending job
List<BankScheduler> bankSchedulers = bankSchedulerDao.findByStatus(ScheduleStatus.PENDING);
Calendar currentDate = DateUtil.getFormatedCalenderDate(DateUtil.currentDate(), "yyyy-MM-dd");
if (bankSchedulers != null) {
for (BankScheduler bankScheduler : bankSchedulers) {
LOGGER.info("bankingPaymentSchedullerRun " + bankScheduler.toString());
//compare from date and to date
if ((bankScheduler.getFromDate().compareTo(currentDate) <= 0)
&& (bankScheduler.getToDate().compareTo(currentDate)) >= 0) {
if (bankScheduler.getSchedulerType().equals(SchedulerType.FUND_TRANSFER.toString())) {
scheduleFundTransfer(bankScheduler);
}else {
scheduleUtilityPayment(bankScheduler);
}
}
}
}
}
Fund transfer execution code block
public PaymentResponse scheduleFundTransfer(final BankScheduler bankScheduler){
FundTransfer fundTransfer = new FundTransfer();
fundTransfer.setBranchName(bankScheduler.getBankSchedulerFundTransfer().getBeneficiaryBranchName());
fundTransfer.setBeneficiaryBankName(bankScheduler.getBankSchedulerFundTransfer().getBeneficiaryBankName());
fundTransfer.setBeneficiaryType(bankScheduler.getBankSchedulerFundTransfer().getBeneficiaryType());
fundTransfer.setBeneficiaryName(bankScheduler.getBankSchedulerFundTransfer().getBeneficiaryName());
fundTransfer.setCurrency(bankScheduler.getBankSchedulerFundTransfer().getCurrency());
fundTransfer.setNarration(bankScheduler.getDetail());
fundTransfer.setThirdPartyType(bankScheduler.getBankSchedulerFundTransfer().getThirdPartyType());
fundTransfer.setTransferedAmount(bankScheduler.getBankSchedulerFundTransfer().getAmount());
fundTransfer.setUserAccountNumber(bankScheduler.getBankSchedulerFundTransfer().getAccountNumber());
fundTransfer.setUserName(bankScheduler.getBankSchedulerFundTransfer().getUserName());
FundTransfer fundTransferUpdate = null;
//commit to fundtransfer real table
try {
fundTransferUpdate = fundTransferDao.create(fundTransfer);
} catch (Exception e) {
LOGGER.error("Exception occur when call update fund transfer in fundTransfer()", e);
}
// getting from currecy Decimals
BankCurrecyInfor currecyInfo =
bankCurrecyInforDao.getDecimalpointsByCurrecy(fundTransfer.getUserAccount().getCurrencyCode());
fundTransferUpdate.setDecimalAmt(String.valueOf(currecyInfo.getNoOfDecimal()));
//call to bank back-end to update
PaymentResponse response = accountServiceInvoker.fundTransfer(fundTransferUpdate);
//for sending sms
SmsCriteria smsCriteria = new SmsCriteria();
smsCriteria.setBank_name("ABC");
messageServiceInvoker.sentIbSms(smsCriteria);
return response;
}

Yes there is, use proper exception handling so when single transaction fails, other will still (try to) execute.
Pseudocode
for(scheduled task from all scheduled tasks) {
try{
begin transaction
do your stuff with jpa
commit transaction
}catch(Exception e){
rollback transaction, log error and stuff
}finally{
release resources
}
}

Related

Using Spring #Transactional in a method with multiple operations

I have a service which runs every few seconds and processes some records from a queue which is stored in the database. I noticed that a #Transactional attribute added and was trying to figure out what it's usage is and wanted to make sure it's the right usage.
#Scheduled(initialDelay = 10000, fixedDelay = 10000)
#Transactional
public void notificationTask() {
Integer txId = myDao.getMinTxIdByStatusCode("Pending");
myDao.updateStatusByTxId(txId, "Processing");
boolean status = true;
Transaction tx = myDao.getTxById(txId);
if (txId != null) {
status = myService.processTx(txId, myTx.getX(), myTx.getY());
if (status) {
myDao.updateStatusByTxId(txId, "Complete");
} else {
myDao.updateStatusByTxId(txId, "Failed");
}
}
}
My main question here is, if for some reason either the getTxById or the processTx methods fail, does that mean with the #Transational attribute, it will roll back the updateStatusByTxId? What is a use case where the Transactional attribute would be used here?
When you use #Transactional on a method, Spring wraps the entire method in a transaction. This means that a new transaction is started before the method is entered and committed after the method is exited.
If there is an exception, the whole transaction will be rolled back. That's what is called "atomicity": transactions are "all or nothing".
Thus, #Transactional is a "shortcut" for something like this when using Hibernate:
#Scheduled(initialDelay = 10000, fixedDelay = 10000)
public void notificationTask() {
Transaction transaction = null;
try {
Session session = sessionFactory.openSession(); //Hibernte session
transaction = session.beginTransaction();
//... your stuff
Integer txId = myDao.getMinTxIdByStatusCode("Pending");
myDao.updateStatusByTxId(txId, "Processing");
boolean status = true;
Transaction tx = myDao.getTxById(txId);
if (txId != null) {
status = myService.processTx(txId, myTx.getX(), myTx.getY());
if (status) {
myDao.updateStatusByTxId(txId, "Complete");
} else {
myDao.updateStatusByTxId(txId, "Failed");
}
}
//... your stuff done
transaction.commit();
}catch (Exception ex) {
ex.printStackTrace();
transaction.rollback();
}
}
So, if your method updateStatusByTxId is just a database call (is not flushing, nor committing or has is own nested transaction): yes, it is rolled back.
And since the transaction is also active, when entering processTx, this service call will also be in the same transaction, even if you add an additional #Transactional to processTx. Except you explicitly start a new transaction by e.g. #Transactional(Transactional.TxType.REQUIRES_NEW).
Additionally, Spring has a really detailed doc:
https://docs.spring.io/spring-framework/docs/current/reference/html/data-access.html#transaction

Spring Statemachine timeout and persistence conflict

I have noticed an issue in using timeouts and redis persistence with statemachinefactory.
Above is my uml diagram for the state machine. I have added a stateListener in my code and every time its persisted.
StateMachine stateMachine = factory.getStateMachine();
stateMachine.addStateListener(new CompositeStateMachineListener<String, String>() {
#Override
public void stateContext(StateContext<String, String> arg0) {
String user = (String) arg0.getExtendedState().getVariables().get("imei");
if (user == null) {
return;
}
log.info(arg0.getStage().toString() + "**********" + stateMachine.getState());
try {
redisStateMachinePersister.persist(arg0.getStateMachine(), "testprefixSw:" + user);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
});
Note : ExitPointGQ points to a initial state called WAITFORCOMMAND of the parent machine.
Now taking the scenario where I need to wait by giving the signal WAIT, the machine goes back to WaitForGenQueryRes which is right. But by now, the first timer has started and after 60 seconds, the timer fires and exits through the exit point and persists that the state is now at WAITFORCOMMAND whereas it has to be at WaitForGenQueryRes because I looped it.
Please point out my mistake so I could fix this.

connection gone out while scheduled operation in Spring with BoneCP: "Database access prob lem. Killing off this connection..."

I use injected EntityManagerFactory for scheduled operation:
#PersistenceUnit
private EntityManagerFactory entityManagerFactory;
#Scheduled(cron="0 0/10 * * * *")
private void scheduledOperation() {
int rows = 0;
try {
EntityManager em = entityManagerFactory.createEntityManager();
em.getTransaction().begin();
rows = em.createNativeQuery("UPDATE table SET ...").executeUpdate();
em.getTransaction().commit();
} catch (Exception ex) {
logger.error("Exception while scheduledOperation. Details: " + ex.getMessage());
}
DateTime now = new DateTime(DateTimeZone.UTC);
logger.info("Scheduled operation completed. Rows affected: {}. UTC time: {}", rows, now);
}
When the application is started, scheduled operation runs every 10 minutes. So first several times operation works as well, but after some time this gone with error:
ERROR - ConnectionHandle - Database access problem. Killing off this
connection and all remaining connections in the connection pool. SQL State = 08S01
Whats happens? How I can keep connection, or take working connection for each scheduled operation?
That's because you don't ever close the EntityManager and the associated connections might hang indefinitely.
Change your code to this instead:
EntityManager em = null;
try {
em = entityManagerFactory.createEntityManager();
em.getTransaction().begin();
rows = em.createNativeQuery("UPDATE table SET ...").executeUpdate();
em.getTransaction().commit();
} catch (Exception ex) {
logger.error("Exception while scheduledOperation. Details: " + ex.getMessage());
em.getTransaction().rollback();
} finally {
if(em != null) {
em.close();
}
}
And always call rollback on failure. Don't assume transactions will rollback automatically because this is a database specific implementation.

JPA multithreaded inserts

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

ldap transaction protocol error

I’m writing in order to get some help.
To be short, I’m trying to use com.unboundid.ldap.sdk (but it is not necessary - the same problem i get if i use oracle's javax.naming.ldap.*) to handle with ldap transactions, and I get the following error:
Exception in thread "Main Thread" java.lang.AssertionError: Result EndTransactionExtendedResult(resultCode=2 (protocol error), diagnosticMessage='protocol error') did not have the expected result code of '0 (success)'.
at com.unboundid.util.LDAPTestUtils.assertResultCodeEquals(LDAPTestUtils.java:1484)
at pkg.Main.main(Main.java:116)
My program is the following ( I’m using simple example from https://www.unboundid.com/products/ldap-sdk/docs/javadoc/com/unboundid/ldap/sdk/extensions/StartTransactionExtendedRequest.html ) :
public class Main {
public static void main( String[] args ) throws LDAPException {
LDAPConnection connection = null;
try {
connection = new LDAPConnection("***", ***, "***", "***");
} catch (LDAPException e1) {
e1.printStackTrace();
}
// Use the start transaction extended operation to begin a transaction.
StartTransactionExtendedResult startTxnResult;
try
{
startTxnResult = (StartTransactionExtendedResult)
connection.processExtendedOperation(
new StartTransactionExtendedRequest());
// This doesn't necessarily mean that the operation was successful, since
// some kinds of extended operations return non-success results under
// normal conditions.
}
catch (LDAPException le)
{
// For an extended operation, this generally means that a problem was
// encountered while trying to send the request or read the result.
startTxnResult = new StartTransactionExtendedResult(
new ExtendedResult(le));
}
LDAPTestUtils.assertResultCodeEquals(startTxnResult, ResultCode.SUCCESS);
ASN1OctetString txnID = startTxnResult.getTransactionID();
// At this point, we have a transaction available for use. If any problem
// arises, we want to ensure that the transaction is aborted, so create a
// try block to process the operations and a finally block to commit or
// abort the transaction.
boolean commit = false;
try
{
// do nothing
}
finally
{
// Commit or abort the transaction.
EndTransactionExtendedResult endTxnResult;
try
{
endTxnResult = (EndTransactionExtendedResult)
connection.processExtendedOperation(
new EndTransactionExtendedRequest(txnID, commit));
}
catch (LDAPException le)
{
endTxnResult = new EndTransactionExtendedResult(new ExtendedResult(le));
}
LDAPTestUtils.assertResultCodeEquals(endTxnResult, ResultCode.SUCCESS);
}
}
}
As you can see, I do nothing with the transaction: just start and rolling back, but it still not working.
Connection is ok, and I receive transaction id = F10285501E20C32AE040A8C0070F7502 BUT IT ALWAYS THE SAME - is it all wrigth???
If “// do nothing” replace with some action exception: unwilling to perform.
I’m starting to think that it is OID problem, but I just can’t figure out what is wrong…
OID is on a WebLogic server and it’s version is :
Version Information
ODSM 11.1.1.6.0
OID 11.1.1.6.0
DB 11.2.0.2.0
All ideas will be appreciated.

Categories