2013-09-30 67 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接受。

另一种选择是根本没有版本字段在你的表中。

希望这有助于遇到同样问题的人。