2013-09-30 3 views
0

Вот ситуация:SqlExceptionHelper - запись Дубликат для уникального ключа Использование Spring Data JPA

  1. У меня есть 3 таблицы - пустышки, A и B - где А и В имеют один-к-многим отношения, и Dummy является автономным
  2. Эти таблицы имеют соответствующие объекты JPA в слое данных. Я являюсь с использованием шаблона проектирования репозитория, поэтому обращаюсь к этим объектам через их соответствующие реализации служб .
  3. Я делаю точную последовательность вызовов этих объектов:
  4. Получить объект для ID = ххх
  5. Отображение ID объект и имя (или любой другой)
  6. Update поле с помощью entity.setField (YYY)
  7. толчок его обратно в БД с помощью: entityService.updateEntity (юридическое лицо)

выше последовательность из # 4 # 7 работает как шарм для пустышки таблицы. Но не в состоянии выполнить для A и B. Исключение составляет:

ERROR org.hibernate.engine.jdbc.spi.SqlExceptionHelper - Duplicate entry <The name here> for key 'name_UNIQUE' 
Exception in thread "main" org.springframework.orm.jpa.JpaSystemException: org.hibernate.exception.ConstraintViolationException: could not execute statement; nested exception is javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: could not execute statement 
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:321) 
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:403) 
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:58) 
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213) 

поля, что я обновление не является уникальным. Объекты имеют точно такую ​​же структуру с некоторыми дополнительными столбцами здесь и там.

Вот код для пустышки Entity:

DummyEntity dummyEntity = dummyService.findDummyEntity(16L); 
    System.out.println(">>> Name is: " + dummyEntity.getName() + " with ID: " + dummyEntity.getId()); 
    dummyEntity.setName("New Name"); 
    dummyEntity.setRank(3333333); 
    dummyService.updateDummyEntity(dummyEntity); 

Повторяя те же шаги для остальных субъектов А и Б.

Так что я делаю не так? Любые указатели будут очень благодарны.

UPDATE:

@erencan - да, я проверил это. Вот что я заметил после постановки вопроса здесь. Таблица A и B (хлопотно них) имеют этот вопрос:

  • Когда я прошу службу хранилища вернуть мне экземпляр для данного ID для таблицы А и В, он возвращает их в порядке
  • , когда изменение создается этот экземпляр с использованием setXXX(), и вызывается функция updateEntity() или saveEntity() (как показано с использованием кода демонстрационной сущности выше), сохранение/обновление вставляет новый объект в таблицу с точными значениями атрибутов, как старые один, но с внесенными новыми изменениями (это наблюдалось путем удаления единственного ограничения ключа в таблицах А и В).
  • Позже, когда я запрашиваю эти вновь созданные объекты, используя их ID в коде JPA/Java, и выполняю те же самые шаги (изменяя некоторый атрибут и сохраняя/обновляя данные в репозитории), эти вновь созданные объекты (строки в db table) обновляется точно так, как ожидается.
  • Так что кажется, что исходная сущность (строка) каким-то образом «заблокирована», а обновления предотвращены.Поэтому вызовы сохранения/обновления JPA просто пытаются создать новый; и поскольку новый объект все еще имеет один и тот же набор значений атрибутов, любое ограничение UNIQUE-ключа начнет жаловаться (конечно)
  • Я провел несколько тестов с существующими таблицами (ETL'd в БД) и нашел, что это поведение согласовано : Если объект НЕ создан JPA, JPA может читать данные в порядке, но не может их обновить; если объект IS создан JPA, тогда JPA может читать и обновлять их в порядке.

Не знаете, как это происходит (пока). Вот схема таблицы А и В:

CREATE TABLE IF NOT EXISTS `mydbschema`.`table-B` (
    `id` INT NOT NULL AUTO_INCREMENT COMMENT 'This is PK', 
    `name` VARCHAR(100) NULL, 
    `city` VARCHAR(50) NOT NULL, 
    `state` VARCHAR(30) NOT NULL, 
    `zip` VARCHAR(5) NOT NULL, 
    `country` VARCHAR(50) NOT NULL, 
    `overall_rank` INT NULL, 
    `inserted` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 
    `insert_src_ver_id` INT NULL, 
    `updated` TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP, 
    `update_src_ver_id` INT NULL, 
    `version` INT NULL, 
    PRIMARY KEY (`id`), 
    UNIQUE INDEX `name_UNIQUE` (`name` ASC)) 
ENGINE = InnoDB; 

Другой:

CREATE TABLE IF NOT EXISTS `mydbschema`.`table-A` (
    `id` INT NOT NULL AUTO_INCREMENT COMMENT 'This is PK', 
    `full_name` VARCHAR(200) NULL, 
    `gender` VARCHAR(1) NULL, 
    `year_of_birth` VARCHAR(4) NULL, 
    `title_code` VARCHAR(6) NULL, 
    `business_role` VARCHAR(30) NULL, 
    `graduation_year` VARCHAR(4) NULL, 
    `residency` VARCHAR(500) NULL, 
    `table-B_id` INT NULL, 
    `npi_num` VARCHAR(10) NULL, 
    `upin` VARCHAR(20) NULL, 
    `dea_num` VARCHAR(20) NULL, 
    `dea_expire_date` VARCHAR(10) NULL, 
    `year_started_practicing` VARCHAR(4) NULL, 
    `high_prescriber` VARCHAR(1) NULL, 
    `board_action` VARCHAR(1) NULL, 
    `mdi_qscore` INT NOT NULL DEFAULT 0, 
    `mdi_cscore` INT NOT NULL DEFAULT 0, 
    `aco_id` INT NULL, 
    `npp` INT NULL, 
    `medicaid_id` VARCHAR(50) NULL, 
    `medicaid_state` VARCHAR(2) NULL, 
    `medicare_id` VARCHAR(50) NULL, 
    `medicare_state` VARCHAR(2) NULL, 
    `medicare_provider_flag` VARCHAR(1) NULL, 
    `inserted` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 
    `insert_src_ver_id` INT NULL, 
    `updated` TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP, 
    `update_src_ver_id` INT NULL, 
    `version` INT NULL, 
    PRIMARY KEY (`id`), 
    UNIQUE INDEX `hdsphy_id_UNIQUE` (`id` ASC), 
    UNIQUE INDEX `npi_num_UNIQUE` (`npi_num` ASC), 
    UNIQUE INDEX `dea_num_UNIQUE` (`dea_num` ASC), 
    CONSTRAINT `fk_table-A_table-B` 
    FOREIGN KEY (`table-B_id`) 
    REFERENCES `mydbschema`.`table-B` (`id`) 
    ON DELETE NO ACTION 
    ON UPDATE NO ACTION, 
ENGINE = InnoDB; 

Здесь след полный стек:

2013-09-30 10:20:49,705 [main] ERROR org.hibernate.engine.jdbc.spi.SqlExceptionHelper - Duplicate entry '1568673648' for key 'npi_num_UNIQUE' 
Exception in thread "main" org.springframework.orm.jpa.JpaSystemException: org.hibernate.exception.ConstraintViolationException: could not execute statement; nested exception is javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: could not execute statement 
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:321) 
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:403) 
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:58) 
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213) 
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:163) 
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) 
at org.springframework.data.jpa.repository.support.LockModeRepositoryPostProcessor$LockModePopulatingMethodIntercceptor.invoke(LockModeRepositoryPostProcessor.java:84) 
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) 
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:91) 
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) 
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204) 
at com.sun.proxy.$Proxy60.save(Unknown Source) 
at com.mdinsider.platform.domain.PhysicianServiceImpl_Roo_Service.ajc$interMethod$com_mdinsider_platform_domain_PhysicianServiceImpl_Roo_Service$com_mdinsider_platform_domain_PhysicianServiceImpl$updatePhysician(PhysicianServiceImpl_Roo_Service.aj:48) 
at com.mdinsider.platform.domain.PhysicianServiceImpl.updatePhysician(PhysicianServiceImpl.java:1) 
at com.mdinsider.platform.domain.PhysicianService_Roo_Service.ajc$interMethodDispatch1$com_mdinsider_platform_domain_PhysicianService_Roo_Service$com_mdinsider_platform_domain_PhysicianService$updatePhysician(PhysicianService_Roo_Service.aj) 
at com.mdinsider.platform.mediblip.engine.TestDBSave.saveMDIQualityScore(TestDBSave.java:94) 
at com.mdinsider.platform.mediblip.engine.TestDBSave.main(TestDBSave.java:142) 
Caused by: javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: could not execute statement 
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1387) 
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1310) 
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1316) 
at org.hibernate.ejb.AbstractEntityManagerImpl.merge(AbstractEntityManagerImpl.java:898) 
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) 
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
at java.lang.reflect.Method.invoke(Method.java:606) 
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:241) 
at com.sun.proxy.$Proxy31.merge(Unknown Source) 
at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:345) 
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) 
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
at java.lang.reflect.Method.invoke(Method.java:606) 
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:334) 
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:319) 
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) 
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:96) 
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:260) 
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:94) 
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) 
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:155) 
... 12 more 
Caused by: org.hibernate.exception.ConstraintViolationException: could not execute statement 
at org.hibernate.exception.internal.SQLExceptionTypeDelegate.convert(SQLExceptionTypeDelegate.java:74) 
at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:49) 
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:125) 
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:110) 
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:136) 
at org.hibernate.id.IdentityGenerator$GetGeneratedKeysDelegate.executeAndExtract(IdentityGenerator.java:96) 
at org.hibernate.id.insert.AbstractReturningDelegate.performInsert(AbstractReturningDelegate.java:58) 
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2975) 
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3487) 
at org.hibernate.action.internal.EntityIdentityInsertAction.execute(EntityIdentityInsertAction.java:81) 
at org.hibernate.engine.spi.ActionQueue.execute(ActionQueue.java:377) 
at org.hibernate.engine.spi.ActionQueue.addResolvedEntityInsertAction(ActionQueue.java:214) 
at org.hibernate.engine.spi.ActionQueue.addInsertAction(ActionQueue.java:194) 
at org.hibernate.engine.spi.ActionQueue.addAction(ActionQueue.java:178) 
at org.hibernate.event.internal.AbstractSaveEventListener.addInsertAction(AbstractSaveEventListener.java:321) 
at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:286) 
at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:192) 
at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:125) 
at org.hibernate.ejb.event.EJB3MergeEventListener.saveWithGeneratedId(EJB3MergeEventListener.java:71) 
at org.hibernate.event.internal.DefaultMergeEventListener.saveTransientEntity(DefaultMergeEventListener.java:236) 
at org.hibernate.event.internal.DefaultMergeEventListener.entityIsTransient(DefaultMergeEventListener.java:216) 
at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:154) 
at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:76) 
at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:914) 
at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:898) 
at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:902) 
at org.hibernate.ejb.AbstractEntityManagerImpl.merge(AbstractEntityManagerImpl.java:889) 
... 31 more 
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '1568673648' for key 'npi_num_UNIQUE' 
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) 
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57) 
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) 
at java.lang.reflect.Constructor.newInstance(Constructor.java:526) 
at com.mysql.jdbc.Util.handleNewInstance(Util.java:411) 
at com.mysql.jdbc.Util.getInstance(Util.java:386) 
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1039) 
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3609) 
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3541) 
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2002) 
at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2163) 
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2624) 
at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2127) 
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2427) 
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2345) 
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2330) 
at org.apache.commons.dbcp.DelegatingPreparedStatement.executeUpdate(DelegatingPreparedStatement.java:105) 
at org.apache.commons.dbcp.DelegatingPreparedStatement.executeUpdate(DelegatingPreparedStatement.java:105) 
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:133) 
+0

'name_UNIQUE' не уникален, но БД хочет, чтобы он был уникальным. Можете ли вы отправлять определения таблиц и сопоставления объектов? – erencan

+0

И как вы храните объект в базе данных (т. Е. Какой метод вы вызываете в репозитории?). Пожалуйста, разместите полный стек, а не фрагмент. –

+0

@ M.Deinum - данные загружаются с использованием сценария ETL. Доступ к данным осуществляется в промежуточном программном обеспечении Java с использованием Spring Data JPA. Доступ к объектам осуществляется с помощью служб репозитория; см. мой код Dummy Entity в исходном сообщении. Сохранение/обновления хранятся в интерфейсе хранилища, сохраняя , обновление - внутренне определяет, должны ли объекты сохраняться или объединяться. – Kingz

ответ

0

Хорошо, проблема решена. Проанализировав различные способы изоляции проблемы, я увидел, что любые строки, добавленные вручную или ETL, будут выборочно обработаны для этого исключения. Любые строки, добавленные кодом Spring Data JPA/Java, будут работать нормально.

Следовательно, проблема была с вставленными вручную строками. Затем я понял, что поле VERSION сидит там с NULL значением для этих вставленных вручную строк. Когда я устанавливаю значения в 0, введенные вручную строки становятся приемлемыми для JPA.

Другой альтернативой является отсутствие поля версии в ваших таблицах.

Надеюсь, что это поможет людям, которые сталкиваются с одной и той же проблемой.

Смежные вопросы