Reputation: 41909
Within my Spring-managed class (@Transactional
and @Service
annotations), I have the following method:
@Transactional
@Service
public class Foo {
@Autowired
private MyService service; // MyService has @Service and @Transactional annotations
public void save(...) {
try {
service.save(...); // gets a Hibernate Session & calls `merge(...)`
} catch(DataIntegrityViolationException e) {
logMessage("save failed with exception: " + e);
}
}
In my corresponding table (for which Foo#save
does the work of saving), I have a unique constraint
.
When the above Foo#save
code executes in such a way that the unique constraint is violated, I see in my log that a DataIntegrityViolationException
is thrown, but it's not being caught by my Foo#save
's catch
block.
org.springframework.orm.jpa.JpaTransactionManager 891 doRollbackOnCommitException -
Initiating transaction rollback after commit exception
org.springframework.dao.DataIntegrityViolationException: Could not execute
JDBC batch update; SQL [insert into MY_TABLE ...; constraint
[MY_CONSTRAINT]; nested exception is
org.hibernate.exception.ConstraintViolationException: Could not execute
JDBC batch update
Confused that the DataIntegrityViolationException
was not being caught, I added another catch for all RuntimeException
's:
public save(...) {
try {
service.save(...); // `service` is marked with
// @Service and @Transactional
} catch(DataIntegrityViolationException e) {
logMessage("save failed with exception: " + e);
} catch(RuntimeException e) {
logMesssage("runtime exception e: " + e);
}
}
But, again, when I ran the same code that would violate the unique constraint violation, I did not see the log message from the catch(RuntimeException e)
block.
My incomplete understanding is that, since I'm using @Transactional
, a proxy of Foo
(created by Spring) will perform the save
method call.
But, is it possible for me to catch the DataIntegrityViolationException
called by service.save(...)
?
Upvotes: 3
Views: 1268
Reputation: 201
Method session.merge(...)
does not immediatelly executes an insert/update SQL query, but put this action in a queue. SQL query will be executed when session flushed. In your case session will be flushed in transactional advice after your method executes, and exception will be thrown there.
So to catch exception in your method add session.flush()
after session.merge(...)
like so:
public class MyService {
public void save(Object o) {
Session session = //get session somehow
session.merge(o);
session.flush(); // In case of data integrity violation Exception will be thrown here
}
}
Upvotes: 1