I use Spring + Hibernate + JPA
I need to handle the list of customers by inserting their orders.
Here is the Unit of work :
for(Customer customer: CustomerList) {
List<Order> orderList = customer.getOrders();
for(Order order: OrderList) {
//1. Insert order into ORDER table
//If insert fails due to Duplicate key then no rollback and I follow steps 2 & 3.
//If insert fails due to any reason except duplicate key then rollback all the previous transactions
//2. select the order record (If any issue during selection then rollbackall the previous transactions)
//3. Update the order If status of the order is different from that in DB (If any issue during update then rollback all the previous transactions)
}
// 4. Update Customer record into CUSTOMER table (If any issue during update then rollback all the previous transactions)
}
Commit is required when all the orders and customer db processes are ok.
Insert order
1.a If duplicate order do not roll back. But select that order from table and update if the status of the order is different in the req compared to the one in db
1.b If any other error during inserting of ORDER then roll back
1.c If no error then proceed inserting orders of the particular Customer
Once orders of the particular Customer is done, then update Customer table
Loop continues..
While handling point 1, 2 and 3 If everything is ok, then commit is required.
If any issues in the middle then all transactions are rolled back
Controller --> Facade layer --> Service --> Repository/Dao
Facade:
#Autowired
MyServiceBean serviceBean;
#Transactional(noRollbackFor = {EntityExistsException.class, PersistException.class, ConstraintViolationException.class, DataIntegrityViolationException.class})
#override
public void facadeMethod(MyReq req) {
List<Customer> custList = req.getCustomers():
for(Customer customer: CustList) {
List<Order> orderList = customer.getOrders();
for(Order order: orderList) {
String dbAction = "";
try {
dbAction = serviceBean.insertOrder(order);
} catch(Exception e) {
// log exception and roll back completely
}
if("handleDupl".equalsTo(dbAction) {
serviceBean.handleDuplOrder(order);
}
}
myService.updateCustomer(customer);
}
}
Service:
#Autowired
MyRepository repo;
#Transactional(propagation = propagation.REQUIRES_NEW)
#override
public String inserOrder() {
String dbAction = "";
try {
repo.insertOrderDao(order);
} catch(all duplicate key exceptions like entityExist, persist, ConstraintVioaltion, DataIntegrity e) {
dbAction = "handleDuplOrder";
} catch(all other Exception except duplicate key e) {
// roll back and throw exception to Facade layer
}
return dbAction;
}
#Transactional(propagation = propagation.REQUIRES_NEW)
#override
public void handleDuplOrder(Order order) {
try {
repo.selectOrderDao(order);
repo.updateOrder(order);
} catch(Exception e) {
// roll back and throw exception to Facade layer
}
}
Repository:
#PersistentContext(unitNmae = "MY_SHCEMA")
EntityManager entityManager;
#Override
public void insertOrderDao(Order order) {
entityManager.persist(order);
entityManager.flush();
}
Problem:
When I send req with One customer who has single order, where the order is duplicate, I see PersistException is caught inside Service method and when it exists from Service method it also throws TransactionSystemException(nested exception is RollbackException: Transaction is marked as rollback only, could not commit JPA transaction) is thrown to Facade layer irrespective of the how I suppress the exception in inner transaction.
Kindly advice If I can achieve Unit of work commit or rollback in this way.
Expected:
I want Spring's #Transactional to ignore Duplicate key exceptions by not rolling back and not affecting the next transaction.
Your outer transaction still fail because you throw ApplicationDuplOrderException() inside your service.
You should setup your services like below:
#Transactional
#Override
public void facadeMethod(MyReq req) {
List<Customer> custList = req.getCustomers():
for(Customer customer: CustList) {
List<Order> orderList = customer.getOrders();
for(Order order: orderList) {
try {
myService.insertOrder(order);
} catch(Exception e) {
// log exception and roll back completely
throw e; // important, you must rethrow
}
}
myService.updateCustomer(customer);
}
}
#Transactional(propagation = propagation.REQUIRES_NEW)
#Override
public void inserOrder() {
try {
repo.insertOrderDao(order);
} catch(all duplicate key exceptions like entityExist, persist, ConstraintVioaltion, DataIntegrity e) {
log.error(xxx); // instead of throwing
} catch(all other Exception except duplicate key e) {
throw e;
}
}
Related
Assume the code is written as below.
#Transactional
public void test() {
try {
someA_insert()
...
thow new Exceptions();
}
catch (Exception e) {
...
thow new Exceptions(); -- 1)
}
finally {
//someB_insert();
test2();
}
#Transactional(value = TxType.REQUIRES_NEW)
public void test2() {
someB_insert();
}
After executing the logic of inserting A (someA_insert), it meets Thow and handles rollback.
And then insert B in finally(someB_insert)
However, not only A, which I thought was rollbacked, but both A and B are rollbacked.
Is there any way to prevent rollback only at finally? Is it physically impossible?
(And if you omit the statement in 1), neither A nor B will be rolled back ;( )
I have a job method in a class-annotated #Transactional. This job method calls inner methods for persistence of individual records. If I simulate an error in the following inner update() method somewhere in the middle of my result set processing, I see that all successful records before/after this exception do not get saved after job completion. Why is that? All outside persistence should remain, with the exception of the individual record that failed. The inner update alone has rollbackFor.
#Service("mailService")
#Transactional
#EnableScheduling
public class MailServiceImpl implements MailService {
#Override
#Scheduled(cron = "${mail.cron.pubmed.autosynch.job}")
public void autoSynchPubMedJob() {
//... Fetch result set
for (Result r: resultset) {
try {
pubService.updatePublication(r);
} catch (Exception e) {
// Silently log and continue
log.error("Error on record: ", e);
}
}
}
The updatePublication method, this is the one with rollbackFor:
#Override
#Transactional(readOnly = false, rollbackFor = Exception.class)
public void updatePublication(Publication publication) throws Exception {
dao.update1(..);
dao.update2(..);
// Simulate exception for a specific record for testing
if (publication.getId() == 123) {
throw new Exception("Test Exception");
}
}
Result: no successful data persisted at all at the end of job completion. There should be partial persistence (for other successful records).
When I remove this Exception simulation, all data is successfully persisted at the end. Also, all data is persisted if I remove the inner call's rollbackFor.
Probaby because it uses existing transaction. Try opening a new one with propagation = REQUIRES_NEW.
Note: New transaction won't be opened if you call the method from the same service. You should use either self-reference call or extract logic to another #Service.
Spring 4.1.4
Hibernate 4.2.0
JDK 1.8
My context: I have a Controller calling --> Service --> calling Dao
The business funcionality is to delete ( in 1 to many DB relation) some child ,but not all child .
Then ,after deleting some child I try to delete the Parent and offcourse I got java.sql.SQLIntegrityConstraintViolationException
But the question is why transaction is market for Rollback ? ( in other words why I don't got the deletion of some child ?)
SQLIntegrityConstraintViolationException is a checked exception and stating Spring documentation the behaviour would be the same of EJB : Note that by default, rollback happens for runtime, unchecked exceptions only. The checked exception does not trigger a rollback of the transaction.
I need to remove some child anf trying to remove the parent if possible, if not I need to commit the transaction maintaining the parent and remaining of childs
Note I tried also to specify in Service and Dao methods the Spring Annotation
#Transactional(noRollbackFor = SQLIntegrityConstraintViolationException.class)
To request explicitly the behaviour expected , but not even like this work for me
Controller code method:
public void delete() {
FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_INFO, "Data deleted.","");
try{
memoTemplateService.delete(memoTemplate);
memoTemplates.remove(memoTemplate);
}
catch (Exception e){
msg=new FacesMessage(FacesMessage.SEVERITY_WARN, "A","B");
}
reset();
FacesContext.getCurrentInstance().addMessage(null, msg);
}
Service method :
#Override
#Transactional(noRollbackFor = {SQLIntegrityConstraintViolationException.class,DBConstraintException.class})
public void delete(MemoTemplate memoTemplate)throws BusinessException {
// deleting some ,not all , child
phaseAndMemoGenerator.deleteMemosForVisibleTimeHorizon(memoTemplate);
try{// some times Template cannot be deleted
memoTemplateDao.delete(memoTemplate);
}
catch (Exception e){
throw new DBConstraintException("Partial Delete", "Template cannot be deleted, Memo in the past are present");
}
}
Dao
#Repository(value = "memoTemplateDao")
public class MemoTemplateDaoImpl extends GenericJpaDaoImpl<MemoTemplate, Long> implements MemoTemplateDao {
#Override
#Transactional(noRollbackFor = SQLIntegrityConstraintViolationException.class)
public void delete(MemoTemplate t) {
super.delete(t);
em.flush();
}
}
Just an Update : it's incredible but I can't catch neither doing the catch in Dao method ,debugger go in catch block but before this still a java.sql.SQLIntegrityConstraintViolationException is fired , incredible !
#Transactional(noRollbackFor = {SQLIntegrityConstraintViolationException.class,PersistenceException.class})
public void tryToDelete(MemoTemplate t)throws Exception {
super.delete(t);
try{
em.flush();
}
catch (Exception e){
throw new Exception("ddddd");
}
}
If there are constraints defined in DB, you won't be able to bypass them by committing without rollback.
I have the following method:
#Override
#Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.SERIALIZABLE)
public void applyLog(int codFilial, List<LogLojaCentralCompactoEntity> items) {
}
which internally calls:
#Override
#Transactional(noRollbackFor = PersistenceException.class)
public void apply(LogCompactoEntity entity) {
}
The second method has a try/catch a PersistenceException. The problem is the transaction rolls back then it reaches PersistenceException.
I know Spring #Transactional defaults to roll back in any unchecked exception, but I am explicitly telling noRollbackFor the PersistenceException.
Why its not working? Any way to threat it?
Thanks.
Edit - the try/catch method inside apply does this:
try {
insert();
}
catch(PersistenceException e)
{
update();
}
Edit2 - log:
Edit3 - exception handling part:
if (acao.equalsIgnoreCase("I")) {
try {
insertQuery.executeUpdate();
}
catch(PersistenceException e) {
int affected = updateQuery.executeUpdate();
if(affected == 0)
throw new LogApplyException("O insert falhou e o update não afetou registros.");
}
}
Edit4 - Some more info:
1) Removing #Transactional from apply, leaving #Transaction just on applyLog results on this exception:
javax.persistence.PersistenceException: org.hibernate.exception.GenericJDBCException: could not execute statement
2) Same as 1, but adding noRollbackFor = PersistenceException.class (on applyLog) results on this exception:
org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly
Edit 5:
#lzagkaretos solution is valid (Thank you), but I want to know how to handle this for future cases.
UPDATE
I think that relying in primary key violation exception in order to find if record should be inserted or updated is not something you should do. Another implementation you can use instead is finding before the execution if record is already saved in the database to perform an update, or not to perform an insert.
For example, if you can use spring data repositories, the code might seem like this.
public void apply(LogCompactoEntity entity) {
LogCompactoEntity logCompactoEntity = (entity.getId() == null) ? new LogCompactoEntity() : logCompactoRepository.findOne(entity.getId());
if (logCompactoEntity == null) {
logCompactoEntity = new LogCompactoEntity();
}
copyProperties(entity, logCompactoEntity);
logCompactoRepository.save(logCompactoEntity);
}
--
Maybe you shouldn't have a try/catch block in apply method and declare it with throws PersistenceException in method signature.
In order for noRollbackFor to work, PersistenceException has to be thrown from apply method. You can try the following.
try {
insert();
}
catch(PersistenceException e) {
update();
throw e;
}
I'm doing an insert + some updates inside a Transaction using JDBCTemplate.
The thing is that I have to set primary key programmatically which I read from a table which keeps track of Primary keys.
public void insert(final COrder cOrder) {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
public void doInTransactionWithoutResult(TransactionStatus status) {
Long cOrderId = insertOrder(cOrder);
insertOrderLines(cOrder, cOrderId);
insertCOrderTaxes(cOrder, cOrderId);
updateMStorage(cOrder);
insertMTransactions(cOrder);
}
});
}
public Long insertOrder(COrder o) {
Long cOrderId = getNextId("C_ORDER");
o.setcOrderId(cOrderId);
...
}
//the above insert methods here
and finally getNextId() which gets the next id.
public synchronized Long getNextId(final String tableName) {
final IdHolder idHolder = new IdHolder(-1l);
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
public void doInTransactionWithoutResult(TransactionStatus status) {
Long nextId = jdbcTemplate.queryForObject("SELECT CURRENTNEXT FROM AD_SEQUENCE WHERE NAME=?",
new String[] { tableName }, Long.class);
jdbcTemplate.update("UPDATE AD_SEQUENCE SET CURRENTNEXT = CURRENTNEXT + 1 WHERE NAME=?",
new Object[] { tableName });
idHolder.setId(nextId);
}
});
return idHolder.getId();
}
Basically I want all these insertions/updates done all or none, but this getNextId() I need to commit regardless of the outer transaction(because of reserving the next primary key).
My question is, is propagation PROPAGATION_REQUIRES_NEW the right one for Transaction which runs in method getNextId()?
Yes. If you use PROPAGATION.REQUIRES_NEW the current transaction, if any, will be paused and a new one will be created. Once the execution of that transaction completes, it will be committed if no error occurs and the outer transaction will be resumed. Regardless of what happens next, the inner transaction is committed and is now independent.
In your code above, make sure that your transactionTemplate has been configured with the right propagation mode, that is:
transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);