2013-09-30 68 views
0

這裏的情況是:SqlExceptionHelper - 重複條目的唯一鍵使用Spring數據JPA

  1. 我有3個表 - 假人,A和B - 其中A和B具有一個一對多 關係,虛擬是獨立的
  2. 這些表在數據層中具有相應的JPA實體。我是 使用存儲庫設計模式,因此通過其相應的 服務實現訪問這些實體。
  3. 我撥打電話的確切順序向這些實體:
  4. 獲取一個實體ID = XXX
  5. 顯示實體ID和姓名(或其他)
  6. 更新使用entity.setField場(YYY)
  7. 推回使用DB: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) 

,我更新不是唯一的領域。這些實體具有完全相同的結構,並在這裏和那裏附加了一些列。

這裏是一個虛擬實體代碼:

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); 

重複完全相同的步驟,剩下的實體A和B.

那我做錯了嗎?任何指針將不勝感激。

更新:

@erencan - 是的,我仔細檢查了。這裏是我在這裏提出問題後觀察到的。表A和B(麻煩的)有這樣的問題:

  • 當我問庫的服務來回報我一個實例爲表A和B的給定的ID,它返回他們正常的
  • 時的變化是使用setXXX()調用的,並且調用updateEntity()或saveEntity()(如使用上面的演示實體代碼所示),save/update會在表中插入一個新的實體,其中的實體屬性值與舊的一個,但隨着新的變化(這通過去除表A和B上的唯一鍵約束來觀察)。後來,當我在JPA/Java代碼中使用它們的ID查詢這些新創建的實體並執行完全相同的步驟(更改某個屬性並調用存儲庫中的save/update)時,這些新創建的實體數據庫表)按照預期的方式進行更新。
  • 因此,似乎原來的實體(行)以某種方式'鎖定',並防止更新。因此,JPA保存/更新調用只是嘗試創建一個新的調用;而且由於新的實體仍然具有相同的屬性值集,任何UNIQUE鍵約束會開始抱怨(當然)
  • 我沒有對現有的表的一些測試(ETL'd進入DB),並發現這個行爲是一致的:如果實體不是由JPA創建的,那麼JPA可以讀取數據,但不能更新它們;如果實體是由JPA創建的,那麼JPA可以讀取並更新它們。

不知道這是怎麼回事(但)。下面是表A模式和B:

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'不是唯一的,但DB希望它是唯一的。你能發表表格定義和實體映射嗎? – erencan

+0

而你是如何存儲實體數據庫(你的資料庫,即調用哪個方法?)。請發佈完整的堆棧跟蹤,而不是一個片段。 –

+0

@ M.Deinum - 數據使用ETL腳本加載。數據是使用Spring Data JPA在Java中間件中訪問的。使用存儲庫服務訪問實體;請在原始帖子中查看我的虛擬實體代碼。保存/更新的資料庫界面上保存,更新 - 至極內部確定的實體應保持或合併。 – Kingz

回答

0

好了,問題就解決了。在嘗試了各種方法來隔離問題後,我發現手動添加或通過ETL添加的行將被選擇性地處理以解決此異常。 Spring Data JPA/Java代碼添加的任何行都可以正常工作。

因此,問題出在手動插入行。然後我意識到VERSION字段在那裏用那些手動插入行的NULL值。當我將值設置爲0時,手動插入的行被JPA接受。

另一種選擇是根本沒有版本字段在你的表中。

希望這有助於遇到同樣問題的人。