Я использую 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