Слой службы не фиксирует исключения уровней DAO / Repositary для запросов DML (вставка) - PullRequest
0 голосов
/ 17 января 2019

Я использую Spring + Hibernate JPA в своем приложении для вставки записи в базу данных.

Контроллер -> Сервис -> DAO (Репозиторий) -> БД

У меня есть служба, у которой есть аннотированный метод @Transactional, и она вызывает метод DAO для вставки записи в БД. Когда происходит вставка дублирующейся записи, я вижу, что DataIntegrityViolation перехватывается контроллером, а не службой.

Помогите выяснить, почему исключение во время процесса БД не перехватывается на уровне службы, хотя метод имеет все исключения внутри блока try-catch.

Когда я отлаживал сервис, я обнаружил, что фиксация происходит только в конце вызова метода сервиса. Пожалуйста, найдите журналы, объясняющие, что фиксация происходит только после завершения вызова метода Service внутри контроллера.

Я хочу знать, почему фиксация не происходит в аннотированном методе @Transactional на сервисном уровне.

Мой контроллер:

@Controller
@RequestMapping(value = "/test")
public class MyController {

    @Autowired
    MyService myService;

    @GetMapping
    public ModelAndView getTestData() {

       try {

       } catch(MyDefinedException e) {

       } catch(Exception e)
             // DataIntegrityViolation is caught only in this block
             LOGGER.error("Exception caught in controller"+e);
       }        
       return mv;
     }
 }

Реализация моей службы:

@Service
public class MyServiceImpl implements MyService  {

    @Autowired
    private MyDao myDao;

    @Transactional
    @Override
    public String myServiceMethod(Object obj) throws myDefinedException {
          LOGGER.debug("----Entering into service method----");
          MyTableEntity myTableEntity = obj;
          try {
                   myDao.myDaoMethod(myTableEntity);
          } catch (EntityExistsException ee) {
               LOGGER.error("ServiceEntityExistsException caught"+ee,ee);
               throw MyDefinedException("ServiceEntityExistsException caught", new Exception(ee));
          } catch(PersistenceException pe) {
               LOGGER.error("Service PersistenceException caugth"+pe, pe);
               throw MyDefinedException("PersistenceException caught", new Exception(pe));
          } catch(ConstraintViolationException e1) {
               LOGGER.error("ConstraintViolationException caught"+e1, e1);
              throw MyDefinedException("ConstraintViolationException caught", new Exception(e1));
          } catch(IllegalArgumentException iae) {
              LOGGER.error("Service IllegalArgumentException caugtht"+iae, iae);
              throw MyDefinedException("IllegalArgumentException caught", new Exception(iae));
          }  catch(DataIntegrityViolationException dive) {
              LOGGER.debug("Service DataIntegrityViolationException caugth"+dive); 
              throw MyDefinedException("DataIntegrityViolationException caught", new Exception(dive));          
          } catch(JDBCConnectionException ex) {
              LOGGER.error("Service JDBCConnectionException caught"+ex, ex);
              throw MyDefinedException("JDBCConnectionException caught", new Exception(ex));
          } catch(DataAccessResourceFailureException darfe) {
              LOGGER.error("Service DataAccessResourceFailureException caught"+darfe, darfe);
              throw MyDefinedException("DataAccessResourceFailureException caught", new Exception(darfe));
          } catch(Exception e) {
              LOGGER.error("Exception caught"+e, e);
              throw MyDefinedException("Exception caught", new Exception(e));
          }
    }
    LOGGER.debug("----Leaving from service method----");
    return "success";
}

MyDao:

Моя реализация DAO / Repositary:

@Repositary
public class MyDaoImpl implements MyDao {

    @PersistenceContext(unitName = "MYSCHEMA")
    private EntityManager entityManager;

    @Override
    public String myDaoMethod(MyTableEntity myTableEntity) {
       LOGGER.debug("----Entering into DAO method----");
       entityManager.persist(myTableEntity);
       LOGGER.debug("----Leaving from DAO method----");
    }
}

Журнал: Я мог найти через лог, что фиксация происходит только в конце вызова метода Service

[STDOUT] 18:54:43.197 [qtp10408676-64] TRACE o.h.s.i.AbstractServiceRegistryImpl - Initializing service [role=org.hibernate.stat.spi.StatisticsImplementor]
[STDOUT] 18:54:43.203 [qtp10408676-64] DEBUG o.h.s.internal.StatisticsInitiator - Statistics initialized [enabled=false]
[STDOUT] 18:54:43.205 [qtp10408676-64] TRACE org.hibernate.internal.SessionImpl - Opened session at timestamp: 15477476831
[STDOUT] 18:54:43.212 [qtp10408676-64] TRACE org.hibernate.internal.SessionImpl - Setting flush mode to: AUTO
[STDOUT] 18:54:43.213 [qtp10408676-64] TRACE org.hibernate.internal.SessionImpl - Setting cache mode to: NORMAL
[STDOUT] 18:54:43.213 [qtp10408676-64] DEBUG o.s.orm.jpa.JpaTransactionManager - Opened new EntityManager [org.hibernate.jpa.internal.EntityManagerImpl@9d02de] for JPA transaction
[STDOUT] 18:54:43.216 [qtp10408676-64] DEBUG o.h.e.t.internal.TransactionImpl - begin
[STDOUT] 18:54:43.450 [qtp10408676-64] TRACE o.h.r.j.i.AbstractLogicalConnectionImplementor - Preparing to begin transaction via JDBC Connection.setAutoCommit(false)
[STDOUT] 18:54:43.450 [qtp10408676-64] TRACE o.h.r.j.i.AbstractLogicalConnectionImplementor - Transaction begun via JDBC Connection.setAutoCommit(false)
[STDOUT] 18:54:43.450 [qtp10408676-64] TRACE o.h.r.t.b.j.i.JdbcResourceLocalTransactionCoordinatorImpl - ResourceLocalTransactionCoordinatorImpl#afterBeginCallback
[STDOUT] 18:54:43.452 [qtp10408676-64] DEBUG o.s.orm.jpa.JpaTransactionManager - Exposing JPA transaction as JDBC transaction [org.springframework.orm.jpa.vendor.HibernateJpaDialect
$HibernateConnectionHandle@80326e]
[STDOUT] 18:54:43.452 [qtp10408676-64] TRACE o.s.t.s.TransactionSynchronizationManager - Bound value [org.springframework.jdbc.datasource.ConnectionHolder@6efafe] for key [org.apach
e.commons.dbcp.BasicDataSource@157d1a0] to thread [qtp10408676-64]
[STDOUT] 18:54:43.452 [qtp10408676-64] TRACE o.s.t.s.TransactionSynchronizationManager - Bound value [org.springframework.orm.jpa.EntityManagerHolder@fc93a7] for key [org.springfram
ework.orm.jpa.LocalContainerEntityManagerFactoryBean@41b972] to thread [qtp10408676-64]
[STDOUT] 18:54:43.453 [qtp10408676-64] TRACE o.s.t.s.TransactionSynchronizationManager - Initializing transaction synchronization
[STDOUT] 18:54:43.453 [qtp10408676-64] TRACE o.s.t.i.TransactionInterceptor - Getting transaction for [org.mypackage.service.implementation.MyServiceImpl.myServiceMethod]
[STDOUT] 18:54:43.453 [qtp10408676-64] DEBUG c.b.a.c.b.s.i.MyServiceImpl - ----Entering into service method----
[STDOUT] 18:54:43.453 [qtp10408676-64] DEBUG c.b.a.c.b.d.i.MyDaoImpl - ----Entering into DAO method----
[STDOUT] 18:54:43.454 [qtp10408676-64] TRACE o.s.t.s.TransactionSynchronizationManager - Retrieved value [org.springframework.orm.jpa.EntityManagerHolder@fc93a7] for key [org.spring
framework.orm.jpa.LocalContainerEntityManagerFactoryBean@41b972] bound to thread [qtp10408676-64]
[STDOUT] 18:54:43.483 [qtp10408676-64] TRACE o.h.engine.spi.IdentifierValue - ID unsaved-value strategy UNDEFINED
[STDOUT] 18:54:43.483 [qtp10408676-64] TRACE o.h.e.i.AbstractSaveEventListener - Transient instance of: org.mypackage.model.MyTableEntity
[STDOUT] 18:54:43.487 [qtp10408676-64] TRACE o.h.e.i.DefaultPersistEventListener - Saving transient instance
[STDOUT] 18:54:43.502 [qtp10408676-64] DEBUG o.h.e.i.AbstractSaveEventListener - Generated identifier: component[column1]{column1=valueOfColumn1}, using strategy: org.hibernate.id.CompositeNestedGeneratedValueGenerator
[STDOUT] 18:54:43.536 [qtp10408676-64] TRACE o.h.e.i.AbstractSaveEventListener - Saving [org.mypackage.model.MyTableEntity#component[column1]{column1=valueOfColumn1}]
[STDOUT] 18:54:43.559 [qtp10408676-64] TRACE org.hibernate.engine.spi.ActionQueue - Adding an EntityInsertAction for [org.mypackage.model.MyTableEntity] object
[STDOUT] 18:54:43.560 [qtp10408676-64] TRACE org.hibernate.engine.spi.ActionQueue - Adding insert with no non-nullable, transient entities: [EntityInsertAction[org.mypackage.model.MyTableEntity#(column1=valueOfColumn1)]]
[STDOUT] 18:54:43.560 [qtp10408676-64] TRACE org.hibernate.engine.spi.ActionQueue - Adding resolved non-early insert action.
[STDOUT] 18:54:43.584 [qtp10408676-64] DEBUG c.b.a.c.b.d.i.MyDaoImpl - ----Leaving from DAO method----
[STDOUT] 18:54:43.584 [qtp10408676-64] DEBUG c.b.a.c.b.s.i.MyServiceImpl - ----Leaving from service method----
[STDOUT] 18:54:43.584 [qtp10408676-64] TRACE o.s.t.i.TransactionInterceptor - Completing transaction for [org.mypackage.service.implementation.MyServiceImpl.myServiceMethod]
[STDOUT] 18:54:43.584 [qtp10408676-64] TRACE o.s.orm.jpa.JpaTransactionManager - Triggering beforeCommit synchronization
[STDOUT] 18:54:43.584 [qtp10408676-64] TRACE o.s.orm.jpa.JpaTransactionManager - Triggering beforeCompletion synchronization
[STDOUT] 18:54:43.584 [qtp10408676-64] DEBUG o.s.orm.jpa.JpaTransactionManager - Initiating transaction commit
[STDOUT] 18:54:43.584 [qtp10408676-64] DEBUG o.s.orm.jpa.JpaTransactionManager - Committing JPA transaction on EntityManager [org.hibernate.jpa.internal.EntityManagerImpl@9d02de]
[STDOUT] 18:54:43.585 [qtp10408676-64] DEBUG o.h.e.t.internal.TransactionImpl - committing
[STDOUT] 18:54:43.585 [qtp10408676-64] TRACE o.h.r.t.b.j.i.JdbcResourceLocalTransactionCoordinatorImpl - ResourceLocalTransactionCoordinatorImpl#beforeCompletionCallback
[STDOUT] 18:54:43.585 [qtp10408676-64] TRACE org.hibernate.internal.SessionImpl - SessionImpl#beforeTransactionCompletion()
[STDOUT] 18:54:43.585 [qtp10408676-64] TRACE org.hibernate.internal.SessionImpl - Automatically flushing session
[STDOUT] 18:54:43.585 [qtp10408676-64] TRACE o.h.e.i.AbstractFlushingEventListener - Flushing session
[STDOUT] 18:54:43.586 [qtp10408676-64] DEBUG o.h.e.i.AbstractFlushingEventListener - Processing flush-time cascades
[STDOUT] 18:54:43.586 [qtp10408676-64] TRACE o.hibernate.engine.internal.Cascade - Processing cascade ACTION_PERSIST_ON_FLUSH for: org.mypackage.model.MyTableEntity
[STDOUT] 18:54:43.587 [qtp10408676-64] TRACE o.hibernate.engine.internal.Cascade - Done processing cascade ACTION_PERSIST_ON_FLUSH for: org.mypackage.model.MyTableEntity
[STDOUT] 18:54:43.587 [qtp10408676-64] DEBUG o.h.e.i.AbstractFlushingEventListener - Dirty checking collections
[STDOUT] 18:54:43.587 [qtp10408676-64] TRACE o.h.e.i.AbstractFlushingEventListener - Flushing entities and processing referenced collections
[STDOUT] 18:54:43.588 [qtp10408676-64] TRACE o.h.e.i.AbstractFlushingEventListener - Processing unreferenced collections
[STDOUT] 18:54:43.588 [qtp10408676-64] TRACE o.h.e.i.AbstractFlushingEventListener - Scheduling collection removes/(re)creates/updates
[STDOUT] 18:54:43.589 [qtp10408676-64] DEBUG o.h.e.i.AbstractFlushingEventListener - Flushed: 1 insertions, 0 updates, 0 deletions to 1 objects
[STDOUT] 18:54:43.589 [qtp10408676-64] DEBUG o.h.e.i.AbstractFlushingEventListener - Flushed: 0 (re)creations, 0 updates, 0 removals to 0 collections
[STDOUT] 18:54:43.591 [qtp10408676-64] DEBUG o.h.internal.util.EntityPrinter - Listing entities:
[STDOUT] 18:54:43.592 [qtp10408676-64] DEBUG o.h.internal.util.EntityPrinter - org.mypackage.model.MyTableEntity{column1 = valueOfColumn1, column2 = valueOfColumn2, column3 = valueOfColumn3}
[STDOUT] 18:54:43.594 [qtp10408676-64] TRACE o.h.e.i.AbstractFlushingEventListener - Executing flush
[STDOUT] 18:54:43.601 [qtp10408676-64] TRACE o.h.v.i.m.a.BeanMetaDataImpl - Members of the default group sequence for bean org.mypackage.model.MyTableEntity are: [interface javax.validation.groups.Default].
[STDOUT] 18:54:43.602 [qtp10408676-64] TRACE o.h.v.i.m.a.BeanMetaDataImpl - Members of the default group sequence for bean java.lang.Object are: [interface javax.validation.groups.Default].
[STDOUT] 18:54:43.602 [qtp10408676-64] TRACE o.h.p.entity.AbstractEntityPersister - Inserting entity: [org.mypackage.model.MyTableEntity#component[column1]{column1=valueOfColumn1}]
[STDOUT] 18:54:43.607 [qtp10408676-64] TRACE o.h.s.i.AbstractServiceRegistryImpl - Initializing service [role=org.hibernate.engine.jdbc.batch.spi.BatchBuilder]
[STDOUT] 18:54:43.611 [qtp10408676-64] TRACE o.h.s.i.AbstractServiceRegistryImpl - Initializing service [role=org.hibernate.jmx.spi.JmxService]
[STDOUT] 18:54:43.611 [qtp10408676-64] TRACE o.h.e.j.b.internal.BatchBuilderImpl - Building batch [size=1]
[STDOUT] 18:54:43.650 [qtp10408676-64] DEBUG org.hibernate.SQL - 
     insert
     into
     MY_SCHEMA.MY_TABLE
     (COLUMN1, COLUMN2, COLUMN3)
     values
        (?,?,?)
[STDOUT] 18:54:43.654 [qtp10408676-64] TRACE o.h.r.j.i.ResourceRegistryStandardImpl - Registering statement [oracle.jdbc.driver.OraclePreparedStatementWrapper@49b38d]
[STDOUT] 18:54:43.714 [qtp10408676-64] TRACE o.h.p.entity.AbstractEntityPersister - Dehydrating entity: [org.mypackage.model.MyTableEntity#componentcomponent[column1]{column1=valueOfColumn1}]
[STDOUT] 18:54:43.816 [qtp10408676-64] TRACE o.h.type.descriptor.sql.BasicBinder - binding parameter [1] as [VARCHAR] - [valueOfColumn1]
[STDOUT] 18:54:43.816 [qtp10408676-64] TRACE o.h.type.descriptor.sql.BasicBinder - binding parameter [2] as [VARCHAR] - [valueOfColumn2]
[STDOUT] 18:54:43.816 [qtp10408676-64] TRACE o.h.type.descriptor.sql.BasicBinder - binding parameter [3] as [VARCHAR] - [valueOfColumn3]
[STDOUT] 18:54:43.989 [qtp10408676-64] DEBUG o.h.e.jdbc.spi.SqlExceptionHelper - could not execute statement [n/a]
[STDOUT] java.sql.SQLIntegrityConstraintViolationException: ORA-00001: unique constraint (MY_SCHEMA.MY_TABLE).
[STDOUT]
[STDOUT]        at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:450) ~[ojdbc7-12.1.0.1.jar:12.1.0.2.0]
[STDOUT]        at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:399) ~[ojdbc7-12.1.0.1.jar:12.1.0.2.0]
[STDOUT]        at oracle.jdbc.driver.T4C8Oall.processError(T4C8Oall.java:1059) ~[ojdbc7-12.1.0.1.jar:12.1.0.2.0]

Ожидается: Я хочу перехватывать все виды исключений БД на своем уровне обслуживания, поскольку у меня есть куча транзакций, выполняемых как UOW, и в случае сбоя мне нужно преобразовать это в свое собственное исключение, добавив конкретные причины.

Факт: Исключения при работе с БД перехватываются внутри блока перехвата метода Controller

1 Ответ

0 голосов
/ 27 января 2019

Создание / обновление не будет зафиксировано, пока не будет возвращено из метода @Transactional. Если создание / обновление передается в базу данных до этого, исключение обнаруживается в методе, но в моем случае оно не сбрасывается до фиксации.

@Repositary
public class MyDaoImpl implements MyDao {

    @PersistenceContext(unitName = "MYSCHEMA")
    private EntityManager entityManager;

    @Override
    public String myDaoMethod(MyTableEntity myTableEntity) {
       LOGGER.debug("----Entering into DAO method----");
       entityManager.persist(myTableEntity);
       entityManager.flush(); // I can catch exceptions at Service after fluhing it.
       LOGGER.debug("----Leaving from DAO method----");
    }
}
...