I am working on play framework using jpa, I have a field with an unique constraint, after "try" to persist an entity with a repeated value, the framework shows an error page like this:
error page
When I try to catch this exception...
try{
JPA.em().persist(nArtist);
}catch(Exception e){
form.reject("username","user already exist");
return badRequest(create_artist.render(form));
}
The page still shows the message... ( I tried already with rollback exception ).
Pdta: That JPA.em() is the only time I called the em.
The call to EntityManager.persist does not guarantee changes to be flushed to the database immediately (which is the point at which constraint violations would emerge). If you want to force a flush, call EntityManager.flush right after persist
Do not use exceptions to handle conditions that could normally occur in your application and, above all, do not use the generic java.lang.Exception. The exceptions thrown from the persistence layer at persist time could mean a lot more things than the specific constraint violation that you're after
Related
My code looks something like this:
#Transactional
public void save(Citizen citizen){
this.saveCitizen(citizen);
}
private void saveCitizen(Citizen citizen){
try{
citizenReposiory.save(citizen);
} catch(DataIntegrityViolationException exception){
//Exception on the line below
Citizen existingCitizen = citizenReposiory.findById(citizen.getId());
exisitingCitizen.setAge(50);
}
}
I'm first trying to save the citizen. If the exception is thrown it's because the citizen already exists in the database. In this case I want to update the existing row instead. However, in the code above I will get another exception when calling citizenReposiory.findById(citizen.getId());. Here's a snippet of the terminal:
[26-04-2020 00:35] WARN [o.h.engine.jdbc.spi.SqlExceptionHelper] - SQL Error: 1062, SQLState: 23000
[26-04-2020 00:35] ERROR [o.h.engine.jdbc.spi.SqlExceptionHelper] - Duplicate entry '10-2020-1' for key 'UKe4wgjj1wdqag5qhbcgnxhbvuj'
[26-04-2020 00:35] ERROR [org.hibernate.AssertionFailure] - HHH000099: an assertion failure occurred
(this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session):
org.hibernate.AssertionFailure: null id in dk.rsyd.mature.entities.WeeklyCare entry (don't flush the
Session after an exception occurs)
org.hibernate.AssertionFailure: null id in dk.rsyd.mature.entities.WeeklyCare entry (don't flush the
Session after an exception occurs)
What is happening here? Is it not possible to continue with an transaction after catching an exception? I have tried to add #Transactional(noRollbackFor = DataIntegrityViolationException.class) but that didn't help.
A different approach could be used. That is, you could first perform the findByID, and verify that the findByID returns a value, if it returns a value, and therefore it already exists, you can carry out the setAge operation, otherwise you can save the citizen. In this way you will always do a preliminary check and avoid saving an object that does not exist by going in exception.
If "The Citizen Object" that you submit to citizenReposiory.save() already have the primary key inside. Maybe you can just call saveOrUpdate() simply.
private void saveCitizen(Citizen citizen){
citizenReposiory.saveOrUpdate(citizen);
}
FYI
Hibernate saveOrUpdate behavior
Hibernate save() and saveOrUpdate() methods
I read that JPA caches SQL instructions to improve performance:
JPA providers like Hibernate can cache the SQL instructions they are
supposed to send to the database, often until you actually commit the
transaction. For example, you call em.persist(), Hibernate remembers
it has to make a database INSERT, but does not actually execute the
instruction until you commit the transaction.
I have a Java EE 6 application deployed to a Glassfish cluster with two instances. In the application there is a race condition where two Singletons do some expensive queries and then cache the results in a database table. They're doing the same work and trying to write the same record, so I sometimes get an exception:
java.sql.SQLIntegrityConstraintViolationException: ORA-00001: unique constraint (SOMESCHEMA.SOMETABLE_PK) violated
I decided the easiest way to deal with this would be to catch and ignore the exception:
// In a EJB with container-managed transactions.
public Entity getExpensiveEntity(int entityId) {
Entity entity = entityManager.find(Entity.class, entityId);
if (entity == null) {
try {
result = expensiveQueries();
entityManager.persist(result);
entityManager.flush();
} catch (SQLIntegrityConstraintViolationException ex) {
// The other instance already created the result, so get it.
result = jpa.find(result.getId());
}
}
return result;
}
I think the call to flush is necessary because otherwise the SQLIntegrityConstraintViolationException won't occur until the transaction ends somewhere up the EJB call stack, past catching and ignoring. Am I correct, is this a valid use case for flush? Is there a better way to handle this?
Reference
Correct use of flush() in JPA/Hibernate
A beginner’s guide to flush strategies in JPA and Hibernate
JPA and CMT -- Why Catching Persistence Exception is Not Enough?
JPA - create-if-not-exists entity? (see top-voted answer)
I'm using Spring Boot and Spring Data.
In the Service Layer, which is better, try to insert the record and catch the "Already Inserted" Exception by the unique key and than translate it into the business exception or use the repository to find the record and than throw the business exception directly?
Database PK is the best approach to maintain uniqueness constraint, if you try approach of querying and checking for PK then you could get in race condition where it will pass the unique check but fails in insert, so any way SQL exception thrown should be handled.
So it is better to handle via Exception and translate to meaning full business error.
I'm running certain process that inserts new elements in DB from a for loop. I'm using JPA (Eclipselink), and sometimes there's a problem with the transation status. This is the case:
One of the INSERTS doesn't work (Primary Key duplicated)
After that, all the inserts will fail (Exception Description: Transaction is currently active).
for (Element l:e){
try{
//Should I add: if(!em.getTransaction().isActive())
em.getTransaction().begin();
em.createNativeQuery("INSERT INTO...").executeUpdate();
em.getTransaction().commit();
}
catch(Exception ep)
{
//right now I don't do anything here
}
}
I get that what is happening is that, since the commit in 1) didn't work, the transaction didn't finish, so the next em.getTransaction().begin() will find an already active transaction.
I have two ideas:
A) Before em.getTransaction().begin(), check if the transcation is active, and only if it is not, call begin(); otherwise, create query and commit.
B) Do something within the catch block. And here's my doubt... Should I call clear()? flush()? close()?
Which one looks better?
Thanks!
An exception thrown by ElementManager.Query does not rollback the active transaction. I see two options here:
Rollback the transaction by yourself within the catch clause with use of em.getTransaction().rollback().
Instead of inserting data with query use the preferred way based on EntityManager.persist whose exceptions cause an automatic rollback (in your particular case this will lead to javax.persistence.EntityExistsException).
I have an application that does:
void deleteObj(id){
MyObj obj = getObjById(id);
if (obj == null) {
throw new CustomException("doesn't exists");
}
em.remove(obj);//em is a javax.persistence.EntityManager
}
I haven't explicitly configure optimistic locking with version field.However, if two request are running in parallel, trying to delete the same object, then I get sometimes an HibernateOptimisticLockingFailureException and other times the "CustomException".
Is it normal to get HibernateOptimisticLockingFailureException without explicitly setting optimistic locking ? Does hibernate a default optimistic locking for detached objects ?
What are you doing to handle this HibernateOptimisticLockingFailureException ? Retry or inform to the user with a default message like "server busy" ?
First of all, HibernateOptimisticLockingFailureException is a result of Spring's persistence exception translation mechanism. It's thrown in response to StaleStateException, whose javadoc says:
Thrown when a version number or timestamp check failed, indicating that the Session contained stale data (when using long transactions with versioning). Also occurs if we try delete or update a row that does not exist.
From the common sense, optimistic lock exception occurs when data modification statement returns unexpected number of affected rows. It may be caused by mismatch of version value as well as by absence of the row at all.
To make sure that entity was actually removed you can try to flush the context by em.flush() right after removing and catch an exception thrown by it (note that it should be subclass of PersistenceException having StaleStateException as a cause).