I'm using JPA toplink-essential, building REST web app.
I have a servlet that find one entity and delete it.
Below code I thought I could catch optimistic lock exception in servlet level but its not!
Instead RollbackException is thrown, and that's what documentation says:
But then when I see the Netbean IDE GlassFish log, somewhere, optimisticLockException is thrown. It's just not being caught in my code. (my system print message doesn't get displayed so I'm sure its not going in there.)
I tried to import each packages (one at a time of course) and tested with catch clause but both time, it is not going into the catch block even though log error says "optimistic exception".
import javax.persistence.OptimisticLockException;
import oracle.toplink.essentials.exceptions.OptimisticLockException;
So where the OptimisticLockException is thrown?????
#Path("delete")
#DELETE
#Consumes("application/json")
public Object planDelete(String content) {
try {
EntityManager em = EmProvider.getInstance().getEntityManagerFactory().createEntityManager();
EntityTransaction txn = em.getTransaction();
txn.begin();
jObj = new JSONObject(content);
MyBeany bean = em.find(123);
bean.setVersion(Integer.parseInt(12345));
em.remove(bean);
//here commit!!!!!
em.getTransaction().commit();
}
catch(OptimisticLockException e) { //this is not caught here :(
System.out.pritnln("here");
//EntityTransactionManager.rollback(txn);
return HttpStatusHandler.sendConflict();
}
catch(RollbackException e) {
return HttpStatusHandler.sendConflict();
}
catch(Exception e) {
return HttpStatusHandler.sendServerError(e);
}
finally {
if(em != null) {
em.close();
}
}
Error msg:
[TopLink Warning]: 2011.01.28 05:11:24.007--UnitOfWork(22566987)
--Exception [TOPLINK-5006]
(Oracle TopLink Essentials - 2.0.1 (Build b09d-fcs (12/06/2007))):
oracle.toplink.essentials.exceptions.OptimisticLockException
[TopLink Warning]: 2011.02.01 08:50:15.095--UnitOfWork(681660)--
javax.persistence.OptimisticLockException: Exception [TOPLINK-5006] (Oracle TopLink
Essentials - 2.0.1 (Build b09d-fcs (12/06/2007))):
oracle.toplink.essentials.exceptions.OptimisticLockException
Not 100% sure, but could it be that you're catching javax.persistence.OptimisticLockException (notice the package), but as the thrown exception is oracle.toplink.essentials.exceptions.OptimisticLockException, it's not getting caught? Even though the name of exception-class is the same, they're not the same class.
I would guess that it is thrown in the em.getTransaction().commit(); statement.
Because the java doc of RollbackException if said:
Thrown by the persistence provider when EntityTransaction.commit() fails.
I strongly belive that this is not the code you realy use (it would not compile because of a missing ) in line bean.setVersion(Integer.parseInt(12345);), but i "hope" that the real code has the same problem.
Have you tried calling entityManager.flush(); inside your try/catch block? When JPA flushes is when the OptimisticLock exception is thrown.
Also you need not commit the transaction in the manner you did. You simply could have done txn.commit(); instead of em.getTransaction().commit();.
I have a similar situation where I am able to catch javax.persistence.OptimisticLockException. In my case I made the ReST endpoint a SSB and inject the entity manager. I then call a method on another SSB which is also injected and acts as a controller for this piece of biz logic. This controller performs a flush() and catches the OLEX and rethrows and ApplicationException which the Rest endpoint / SSB catches and retries. Using this pattern you also need to make sure to specify TransactionAttributeType.RequiresNew so that each retry you make occurs in a fresh transaction since the OLEX invalidates the old one.
Related
Is it good practice to put all Java JDBC Select statements in a try-catch block ? Currently I write most of my code without it. However, I do try-catch for insert/update/delete.
Note: Currently using Sprint Boot.
String sqlQuery = "Select productId, productName, productStartDate from dbo.product where productId = 5"
public getProductData() {
....
List<Product> productList = namedJdbcTemplate.query(sqlQuery, new ProductMapper());
Since this question is tagged with spring-boot and you are using JdbcTemplate, I'm giving you a Spring-specific answer.
One of the points of Spring is to avoid boilerplate from developers. If you find yourself adding things repetitively, like putting try-catch blocks around code executing DML, that's cause for suspecting you're not doing something right. Adding your own try-catches in code using Spring isn't always wrong, but it usually is.
In the Spring reference doc https://docs.spring.io/spring-framework/docs/current/reference/html/data-access.html#jdbc there is a table showing what is the developer's responsibility and what is Spring's responsibility. Processing exceptions, handling the transactions, and closing jdbc resources are all shown as being Spring's responsibility.
SpringJdbc takes care of a lot of things for you. It handles closing JDBC resources and returning connections to their pool, and converts exceptions from SQLException to a hierarchy of unchecked DataAccessExceptions. In Spring unchecked exceptions thrown from a method wrapped in a transactional proxy cause the transaction to get rolled back. If you do your own try-catch logic you can prevent rollback from occurring when it needs to, if you catch the exception and the proxy never sees it. Adding try-catch logic can cause problems if you don't understand what Spring is doing.
Exceptions do need to be caught somewhere. In a Spring web application, you can set up an exception handler that catches anything thrown from the controller layer, so that you can log it. That way the action in progress gets broken off cleanly, the current transaction rolls back, and the exception gets handled in a consistent way. If you have other entry points, such as reading messages from a queue, those will need their own exception handler.
Exceptions are thrown in order to escape the current context, which isn't able to deal with the problem, and relocate control to somewhere safe. For most exceptions coming from JDBC, they aren't anything you can fix, you just want to let it be thrown, let the current transaction rollback, then let the central exception handler catch and log it.
First of all, if you're working with raw JDBC API, you should always use PreparedStatement.
Yes, you'll just have to wrap the code with try-catch block at some point, though it's a good practice to catch exceptions just right away or at the point where it's logically suits. In case of SQL queries, you actually should wrap all of them into some Service class that will give you an access to modify your database objects without running through JDBC API every time. For example:
public class UserService {
private static final String CREATE_USER_SQL = "...";
private final Connection jdbcConnection;
public #Nullable User createUser(final String name) {
try (final PreparedStatement stmt = jdbcConnection.prepareStatement(CREATE_USER_SQL)) {
jdbcConnection.setAutoCommit(false);
stmt.setString(1, name);
stmt.executeQuery();
jdbcConnection.commit();
return new User(name);
} catch (final SQLException createException) {
System.out.printf("User CREATE failed: %s\n", createException.getMessage());
try {
jdbcConnection.rollback();
} catch (final SQLException rollbackException) {
System.out.printf("Rollback failed: %s\n", rollbackException.getMessage());
}
return null;
}
}
}
This solves two problems right away:
You won't need to put boilerplate JDBC code everywhere;
It will log any JDBC errors right away, so you won't need to go through a complex debugging process.
Brief explanation:
First of all any resource involving I/O access (database access is I/O access) must always be closed or it will cause a memory leak.
Secondly, it is better to rely on try-with-resources to close any resource as having to call the .close() method manually is always exposed to the risk of not being effectively executed at runtime due to a potential Exception/RuntimeException/Error getting thrown beforehand; even closing the resource in a finally method is not preferable as such block executes at a different phase compared to the try-with-resources - auto closure of try-with-resources happens at the end of the try block, while finally executes at the end of all try/catch block - , in addition to the basic problem that it is not a secure solution as a throw might happen even inside the finally block, preventing it from completing correctly.
This said, you always need to close:
Statement/PreparedStatement/CallableStatement
any ResultSet
the whole Connection when you don't need DB access anymore
Try-catch for DB Layer code is important if you're querying with JDBC.
Think about, what if the connection broke? Or what if Database crashed ? Or some other unfortunate scenario comes up.
For these things, I will recommend you to always keep the DB layer code within try-catch.
It's also recommended for you to have some fallback mechanism in-case of the above events.
You should always handle it with try cactch.
Why: For example you started a connection to db then some exception happened, if you don't rollback your transaction it stay on db and performance will be decreased, and memory leak will be happen.
Imagine, if your connection limit is 100 and 100 exception throwed after transaction started and you didn't rollback it your system will be locked because of you can't create any new connection to your database.
But if you want an alternative for "try catch finally" you can use like this:
EmUtil.consEm(em -> {
System.out.println(em.createNativeQuery("select * from temp").getResultList().size());
});
Source code:
public final class EmUtil {
interface EmCons {
public void cons(EntityManager em);
}
public static void consEm(EmCons t) {
EntityManager em = null;
try {
em = getEmf().createEntityManager();
t.cons(em);
} finally {
if (em != null && em.getTransaction().isActive())
em.getTransaction().rollback();
if (em != null && em.isOpen())
em.close();
}
}
private static EntityManagerFactory getEmf() {
//TODO
}
}
Spring translates those exceptions as DataAccessException (for more detail link). It will be good to catch those exceptions and you can rollback with #Transactional.
I am using oracleDB and elasticsearch for persistence and if anything goes wrong in that method, it throws a custom exception. Now I need to rollback from DB if any thing fails in elastic also.
I have already added #transactional annotation on service classes. And everything I found on web.
#Transactional(rollbackOn = BaseException.class)
public void transaction(ab ab, a a) {
persistenceService.save(a);
persistenceService.updateSignalCountDb(a.abc(), a.bcd(), ab);
elasticService.saveSignal(a);
try {
elasticService.updateSignalCountElastic(a);
} catch (Exception e) {
throw new BaseException(ErrorCodes.FAILED_ELASTIC_SEARCH_UPDATE, e);
}
}
persistenceService.save() method saves in db.
persistenceService.updateSignalCountDb() method update another table in db.
elasticService.saveSignal() method saves in elastic. throws base exception in case of failure.
elasticService.updateSignalCountElastic() method update another index in elastic. It also calls elasticService.delete() method to delete anything saved in elastic in case of failure and throws a base exception.
I expected this would work in case of any failure in the entire method. But if anything fails on elastic, i get a base exception but my data from oracle db doesn't rollback.
At first glance it looks like you are doing everything right. But before you search for the reason why it doesn't work, small change in your code would prevent the problem from occurring in the first place:
#Transactional(rollbackOn = BaseException.class)
public void transaction(ab ab, a a){
try {
elasticService.saveSignal(a);
elasticService.updateSignalCountElastic(a);
persistenceService.save(a);
persistenceService.updateSignalCountDb(a.abc(), a.bcd(), ab);
}catch (Exception e){
throw new BaseException(ErrorCodes.FAILED_ELASTIC_SEARCH_UPDATE, e);
}
}
In this case your exception will occur before you even save in DB and you won't have a problem
If you are using Spring #Transactional, I researched there is only
rollbackFor option but not rollbackOn(I'm not sure this), like
this: #Transactional(rollbackFor = BaseException.class).
Make sure Transactional is enabled in spring configuration: #EnableTransactionManagement
In your case, only BaseException would trigger rollback, other Exception would not.
In my service, have handled DataIntegrityViolationException when calling myCrudRepository.saveAndFlush to handle concurrent persist (insertion) requests. It works and I can catch the exception. After this, I prefer to make sure if the exception is exactly because entity already exists, not due to any other possible unknown issues. So, I call myCrudRepository.exists(entity.getId()) , but DataIntegrityViolationException is thrown again.
Here is my simple code:
private void save(final Employee entity) throws MyAppException {
try{
this.myCrudRepository.saveAndFlush(entity);
}catch (org.springframework.dao.DataIntegrityViolationException e){
// check if the error is really because the entity already exists
// entity.getId() is already generated before any save. it's like National ID
boolean exists = this.myCrudRepository.exists(entity.getId()); // DataIntegrityViolationException is thrown here again!
if (exists)
throw new MyAppException(HttpStatus.CONFLICT, "Entity already exists.");
else
throw e;
}
}
But if I use findOne instead of exists, it works fine. It's somehow strange, but of course it has a technical reason that I'm not good enough to make a guess.
Any idea? Thanks in advance.
The problem is when you are using Transactional method and after the method returns the transaction will auto commit and hibernate will wrap up the exceptions into another one. Actually the exception is occurring when transaction commits and at that time you are out of that method already. To catch the exception from inside the method you can simply use em.flush() after this.myCrudRepository.saveAndFlush(entity);.
I'm not sure if I'm asking it right, but I've got a method that's transactional, and within that I fetch a list of items. Now, while processing one of the items, I encounter an invalid data usage exception[null is passed to a repository, and fine one throws this exception which is fine], and upon getting that exception, I catch it, and mark the item being processed 'failed'. The transactional method ends but the changes do not go through.
#Transactional
public void method(){
try{
List items = itemRepo.getItems(NOT_SUBMITTED);
for(Item item: items){
processItem(item); //this is where the exception happens
}
}
catch(Exception e){
item.setStatus(FAILED) // this doesn't go through to the db
}
}
private void processItem(Item item){
otherRepo.findOne(item.X); //item.X is null, and I get "invalid data usage" exception
}
I think the object somehow is getting detached; not sure why. I did a test throwing an exception myself in the try block, worked perfectly. The status was reflected properly.
I tried grabbing this object again doing itemRepo.findOne(item).setState(FAILED), didn't work. I tried, itemRepo.save(item) - didn't work.
Any pointers?
Thanks
The exception is being thrown by the JPA implementation, which also sets the transaction status to setRollbackOnly. That's why you can save the failed state when it's you throwing the exception.
Once the JPA entityManager throws an exception it's done, you can't use that entityManager anymore. Saving the item status as failed would have to be in a separate transaction using a different entityManager instance.
By working with Java EE, I defined a persistence layer that exposes serval DAOs (extending GenericDAO and by using Hibernate as ORM).
Then, I inject a certail DAO in order to work with it into another class.
#Inject
private MyDAO myDAO;
public void writeSomething(String key, String data) {
try {
myDAO.create(key, data);
} catch (ConstraintViolationException e) {
// BLOCK 1
} catch (Exception e) {
// BLOCK 2
}
}
Now, I'd like to test exceptions, by trying to write an entry duplication.
As expected, the application returns an error, but it isn't a ConstraintViolationException instance, but something else (in fact, EJBException).
11:42:19,065 ERROR [org.jboss.ejb3.invocation] (http-localhost-127.0.0.1-8080-3) JBAS014134: EJB Invocation failed on component MyDAO for method public java.lang.Object my.app.dal.genericdao.GenericDAOImpl.create(java.lang.Object) throws java.lang.IllegalStateException,javax.persistence.PersistenceException,org.hibernate.exception.ConstraintViolationException: javax.ejb.EJBException: javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: Duplicate entry
How can I to catch a specific exception thrown by myDAO according to the situation (in this case a constraint violation)?
You can catch the EJBException and unwrap it using exception.getCause(); to decide on further strategy.
However, I would not let the DAO throw PersistenceException into the application, wrapped or not. It will pollute your code with duplicate boilerplates with error handling on a different abstraction level.
It is advisable to handle the immediate failure inside the DAO and, if no recovery is possible, throw a business-centric application exception.
For cases, where a constraint violation is possible because you are trying to store a duplicate, consider querying for duplicates first and then modify and store the query result if appropriate. Relying on catching Exceptions instead is less readable and less efficient.