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.
Related
The following SQL if run in MSSQL will insert the 1st and 3rd rows successfully:
BEGIN TRAN
INSERT ... -- valid data
INSERT ... -- invalid data (e.g. over column width)
INSERT ... -- valid data
COMMIT
Even though the second row fails within the transaction, you can still see the two rows with some valid data after the commit in the table.
However, when trying something similar in Hibernate, it rollbacks the whole transaction. Is there a way to tell Hibernate not to rollback on failed rows and commit the rest as same as how MSSQL does it?
e.g.
EntityTransaction transaction = em.getTransaction();
transaction.begin();
em.persist(new MyEntity("good"));
em.persist(new MyEntity("too long"));
em.persist(new MyEntity("good"));
transaction.commit();
This is not possible within the same transaction. Hibernate simply doesn't allow this. An error in a statement leads to an exception, which Hibernate cannot recover from. From the manual:
If the JPA EntityManager or the Hibernate-specific Session throws an exception, including any JDBC SQLException, you have to immediately rollback the database
transaction and close the current EntityManager or Session.
Certain methods of the JPA EntityManager or the Hibernate Session will not leave the Persistence Context in a consistent state. As a rule of thumb, no exception thrown by Hibernate can be treated as recoverable. Ensure that the Session will be closed by calling the close() method in a finally block.
Now this is a restriction (design decision) of Hibernate and not of the underlying JDBC or database stack. So what you want is perfectly possible using JDBC directly. If it is really important for you to get that behaviour, you might consider using JDBC calls for this section of the code. There you can do it exactly like in the SQL client: open transaction, issue statements, catching any exceptions manually and "ignoring" them, and at the end committing the transaction.
Example code:
Session session = em.unwrap(Session.class);
session.doWork(connection -> {
// manual commit mode
connection.setAutoCommit(false);
executeInsertIgnoringError(connection, new Object[]{123, null, "abc"});
executeInsertIgnoringError(connection, new Object[]{....});
...
connection.commit();
});
private void executeInsertIgnoringError(Connection connection, Object[] values) {
try (PreparedStatement stmt =
connection.prepareStatement("INSERT INTO MY_ENTITY VALUES (?, ?, ?, ...)")) {
for (int i = 0; i < values.length; i++) {
// PreparedStatement is indexed from 1
stmt.setObject(i+1, values[i]);
}
stmt.executeUpdate();
} catch (Exception e) {
log.warn("Error occurred, continuing.");
}
}
The way i did it is to divide your logic into diferent functions, and open the transaction inside the persisting function instead of the main one.
The main problem I see in your code is that you're defining a block transaction insead of opening a transaction for each operation.
Here's my snippet:
persistEntity(new MyEntity("good"));
persistEntity(new MyEntity("bad"));
persistEntity(new MyEntity("good"));
...
private void persistEntity(MyEntity entity){
EntityTransaction transaction = em.getTransaction();
transaction.begin();
em.persist(entity);
transaction.commit();
}
This way it will rollback just for the bad entity and keep going with the other. You can also add a try catch inside the persistEntity method, if you want to log the exception.
Fun fact, If you're using Spring you could create another #Component for the persist operations and only add #Transactional to the persisting method, this way you don't have to manage the transactions yourself.
Don't do so, that is idiomatically wrong, at first just review the real scope of your transactions.
You could write the code to run one statement at a time with autocommit on and not use #Transactional... Then perhaps catch any exceptions and throw them away as you go. But pretty much everything in that sentence is troublesome to even think about as a responsible developer and it would affect your entire app. Flavius's post would be a little more granular in doing something similar with explicitly smaller transactions and is a good way to go about it too.
As others have been commenting it's not a long term great plan and goes against so many ways to write programs correctly and the benefits and purpose of transactions. Perhaps if you plan to only use this as a one off data ingestion plan you could but again be very wary of using these patterns in a production grade app.
Having been sufficiently alarmed, you can read more about auto commit here and also be sure to read through the post links on why you probably shouldn't use it.
Spring JPA - No transaction set autocommit 'true'
You can do that by adding below property in hibernate config xml file
<property name="hibernate.connection.autocommit" value="true"/>
If you could use #Transactional annotation then
#Transactional(dontRollbackOn={SQLException.class, NOResultException.class})
Then I would suggest one some change in your code. It's better if you add your entities in a loop and catch exception on each transaction.
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 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.)
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.