简体   繁体   中英

Handling Exceptions within @Transactional Class

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(...) ?

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
      }
  }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM