I am doing exception check while saving data in the following way:
try {
session.beginTransaction();
session.update(person);
session.getTransaction().commit();
}
catch(Exception e) {
session.getTransaction().rollback();
log.error("Error saving person to database", e);
}
nevertheless I have my application terminated with an exception somewhere later.
(I did this check in order to avoid of Data truncation errors. May be there is a way to check data truncation without causing an exception in Hibernate?)
When a Hibernate exception is thrown, you cannot use the session, which caused the exception, any more. A rollback is done automatically.
If you want to continue after the exception, you have two possibilities:
Throw away the old session and create a new one.
Use a StatelessSession instead of a session. A StatelessSession can be used after an exception. In a StatelessSession you have to do the rollback manually.
Normally you'll do solution 1.
Solution 2. is useful if you intentionally provoke an exception and you have a special way to react on it. (For example an index violation exception in an insert operation, and after the exception you do an update instead of an insert.)
Related
I'm prety new to hibernate and I have a question about the error handling and persistance workflow. I have the following piece of legacy code.
public void doPersist(Contact out){
contactValidator.validationOne(out);
entityManager.persist(out);
contactValidator.validationTwo(out);
contactValidator.validationThree(out);
}
ContactValidator is a class used to validate the Contact, it throws a multiple business exceptions in every validation method. I don't like how the method doPersist is constructed. Why it first calls entityManager.persist and then validate the object? In case of exception in some of the validation methods the data should be rollbacked. How Hibernate will rollback the data when persist is already called?
I personally don't care for how the doPersist method is written either because I can see several cleaner alternatives that avoid having such a superfluous method.
To answer your specific question, the magic of rollback happens because of how transactions work. A transaction is nothing more than a series of operations that are performed as a single unit of work that must adhere to being atomic, consistent, isolated, and durable (e.g. ACID).
While the transaction is active and has yet to be committed, if an exception is thrown, then the exception handling tells the transaction to forget about what it was told to do.
Session session = sessionFactory.openSession();
try {
session.getTransaction().begin();
// do whatever work you want to do here
session.getTransaction().commit();
}
catch ( Throwable t ) {
if ( session.getTransaction().isActive() ) {
session.getTransaction().rollback();
}
throw t;
}
finally {
session.close();
}
So in this code, even if the transaction is attempting to commit and an exception is thrown, the catch clause sees that it is active and rolls the transaction back, thus telling the database to throw away all the work it was just asked to perform.
Now I want to touch on your ContactValidator.
My guess is that your ContactValidator closely aligned with how Bean Validation. It basically looks at the bean's state and makes sure that there aren't any inconsistent expectations and if so, assert with an exception.
When using hibernate-validator in conjunction with hibernate-core, you get bean validation automatically for free because Hibernate will plug into the validator framework and perform validate operations for the following events
PrePersistEvent
PreUpdateEvent
PreRemoveEvent
As you can see, there isn't any post event support out of the box. That makes sense because you generally want to satisfy constraints before you actually save or update a database row. This is why I find your second and third contact validation methods strange.
That aside, if you really need some post-insert or post-update validation, you can easily tie into the existing bean validator listener for these operations too with a custom Hibernate listener which you register on the PostInsertEvent and PostUpdateEvent groups to call bean validation.
When the program encounter an exception, The RollBackOnly goes to True.
How can I "Set" this RollBack To False Even it is encountering an exception.
#Resource
protected SessionContext context;
Public void creerNewEntity(final Entity myEntity) {
try {
this.em.persist(myEntity);
this.em.flush();
} catch (final EntityExistsException e) {
System.out.println((this.context.getRollbackOnly())); // It s has a true value
throw new MyException("TODO BLABLA", e);
}
}
When the program throw this Exception "MyException", I change the object myEntity by setting for example a new Id then I call again creerNewEntity().
Unfortunately, it doesn't work, I got this exception "javax.persistence.PersistenceException: org.hibernate.HibernateException: proxy handle is no longer valid", I think because the RollBack has a true value, How can I change the rollback to make this works ?
Thanks.
There probably isn't a simple way to do this since the whole point of the EJB design is that you don't care about such things. The first error inside of a transaction makes it invalid -> rollback. That's the rule.
If you want something special, then get yourself a database connection from the session and use plain SQL instead of EJB to modify the data. That way, you can try to INSERT a new instance and handle all exceptions yourself. When the insert succeeds, you can use EJB to load the newly created object to add it to the session.
That said, I'm not sure what you're trying to achieve with the code above. Just ignoring when you can't create a new instance in the database feels like "I don't care about quality of my product." Maybe your attempt to work around an error is just a symptom of a bad design of your application. Take a step back and consider what you're doing and why. Maybe if you told us more about the reasons why you want to ignore all errors (even the really, really deadly ones), we would be able to point out a better solution.
EDIT So you get javax.persistence.EntityExistsException which means you tried to save the same entity twice. That can mean any number of things:
You loaded the bean in a different session and now you try to save it in a second one. Since the new session can't know if the bean exists, it tries to create it again.
Instead of loading the bean from the session like you should, you cheated and create a new instance manually. Of course, the session manager now thinks this is a new bean.
The correct solution depends on what you need to achieve. If you modified myEntity and need to save the changes, use em.merge(). The EM will then check if the object already exists and if it does, it will do an SQL UPDATE instead of an INSERT
If you just want to give some other class a valid entity, then you need to get it from the database. If the database returns null, you need to create a new instance and persist it and then return it.
See also: JPA EntityManager: Why use persist() over merge()?
EntityExistsException is PersistenceException
when jpa throws it, ejb CMT is marked for rollback
http://piotrnowicki.com/2013/03/jpa-and-cmt-why-catching-persistence-exception-is-not-enough/
as aaron suggested, you could use merge()
you can also contain transaction boundary by using RequiresNew
#TransactionAttribute(REQUIRES_NEW)
http://docs.oracle.com/javaee/6/tutorial/doc/bncij.html
I'm trying to save data using Hibernate. Everything happens within the same session. The logic is following :
1) Begin a transaction and try to save :
try
{
session.getTransaction().begin();
session.save(result);
session.getTransaction().commit();
}
catch (Exception e)
{
session.getTransaction().rollback();
throw e;
}
2) If a new record violates integrity constraint catch an exception in the outer wrapper method, open another transaction and query more data
catch (ConstraintViolationException e)
{
if ("23000".equals(e.getSQLException().getSQLState()))
{
...
session.getTransaction().begin();
Query query = session.createQuery("from Appointment a where a.begin >= :begin and a.end <= :end");
query.setDate("begin", date);
query.setDate("end", DateUtils.addDays(date, 1));
List results = query.list();
session.getTransaction().commit();
The problem is when the second transaction performs query.list it throws an exception that should'be been linked with the previous transaction.
SQLIntegrityConstraintViolationException: ORA-00001: unique constraint
Should I query data from another session or what's the other way to isolate two transactions from each other?
Thanks!
You should not use the same session if you get an exception, you have to close the session and use a different session for your second operation. This is given in hibernate documentation:
13.2.3 Exception Handling
If the Session throws an exception, including any SQLException,
immediately rollback the database transaction, call Session.close()
and discard the Session instance. Certain methods of Session will not
leave the session in a consistent state. No exception thrown by
Hibernate can be treated as recoverable. Ensure that the Session will
be closed by calling close() in a finally block.
Hope the Exception class is base for all types of exceptions, hence if it is placed before it will be catched always and rest of the exception handling is isolated.
Can you try to replce session.save() with session.persist method, hope this might resolve your problem. Refer following link for more details about both methods.
What's the advantage of persist() vs save() in Hibernate?
I got a minor problem with catching exceptions. I've got code like this:
Role r=new Role("default");
r.setId(Role.DEFAULT_ID);
u.getRoles().add(r); // u is instance of entity which is in relation manytomany with r
try{
em.persist(u);
}catch(Exception e){
System.out.println(e.getClass().getName()+" - default role not found, creating...");
em.persist(r);
em.persist(u);
}
Hope the point of this is clear. If the default role does not yet exist an exception is supposed to be catched, the role is created and then it's given another shot. However I can't catch any exception.
The error log of first two exceptions thrown is:
[org.hibernate.util.JDBCExceptionReporter] (http-127.0.0.1-8080-5) could not insert collection: [Comic.model.User.roles#5] [insert into USER_ACCOUNT_ROLE (USER_ACCOUNT_uid, roles_rid) values (?, ?)]
java.sql.SQLIntegrityConstraintViolationException: ...blabla you dont follow constraints
.
ERROR [org.hibernate.event.def.AbstractFlushingEventListener] (http-127.0.0.1-8080-5) Could not synchronize database state with session
org.hibernate.exception.ConstraintViolationException: could not insert collection: [Comic.model.User.roles#5]
I guess I can't catch any exception since it's thrown outside my try block right? Any suggestions what could I do about this?
For your User entity I think your relationship to Role can/should be ManyToMany.
You should not need to manually be managing persisting the object graph, as you are doing in the catch block, if you place a CascadeType.PERSIST on that relationship as well.
If em.persist() throws and Exception. Then it seems strange that you would call the method again (twice) in your catch statement.
Either you need to add another try catch, inside your catch statement to deal with a second exception.
Or you need to change your logic to check for the need to persist the role first rather than handling it with Exception catching.
I have a DAO where I need to catch an unique constraint exception. To do this, the only working solution is to flush my EntityManager after the persist. Only then I come into a catch block where I have to filter out the exception. And, my DAO method needs to be wrapped in a transaction (REQUIRES_NEW) otherwise I have the RollBackException.
Am I doing something wrong?
try {
em.persist(myObject);
em.flush();
} catch (PersistenceException ex) {
if (ex.getCause() != null) {
String cause = ex.getCause().toString();
if (cause != null) {
if (cause.contains("org.hibernate.exception.ConstraintViolationException")) {
logger
.error("org.hibernate.exception.ConstraintViolationException: possible unique constraint failure on name");
throw ex;
}
}
}
}
Am I doing something wrong?
EntityManager#persist() won't trigger an immediate insert (unless you are using an IDENTITY strategy). So if you want to actually write the in memory changes to the database and get a chance to catch a constraint violation, you have to flush() "manually" (although even doing so doesn't strictly guarantee anything, your database could be configured to use deferred constraints).
In other words, what you're doing is IMO just the right way to go.
I have a service method with a #Transactional on it (REQUIRED) called addNewObject(). In this method some stuff happens that might throw an exception (and thus rollback the transaction). One of these calls is a call to the DAO method addObject. This one might fail because of the unique constraint. Now, if I do flush, the object will be persisted if there is no unique violation. However, within the service there might still be something else that causes the method to throw an exception.
I think that you are confusing flush and commit. If something goes wrong outside addObject and throws an unrecoverable exception (but inside the same transaction), the whole transaction will be rolled back, including the INSERT statements corresponding to the persist() calls. To sum up, flush != commit.