Hibernate 5将已经存在于DB中的id指定为preinitialization - org.springframework.dao.DataIntegrityViolationException

时间:2016-01-26 10:09:00

标签: spring hibernate hsqldb

我打算从Spring 4.2.1.RELEASe + Hibernate 4.2.19.Final迁移到4.2.1.RELEASe + Hibernate 5.0.7.Final。

我创建了一个简单的项目available on Github来测试新的Hibernate版本并遇到了一个问题:在我的集成测试中,我使用sql文件中的两行初始化HSQLDB。关键是当我的Hibernate版本是4.2.19时,来自ExampleIntegrationTRTest的所有测试都通过了。但是对于5.0.7.Final,第一个测试(testExampleInsertWithRollback)失败了:

脚本/ initData.sql

INSERT INTO TFOEMPLE(FOEMPLE_ID, FOEMPLE_NAME) values(1, 'Louis');
INSERT INTO TFOEMPLE(FOEMPLE_ID, FOEMPLE_NAME) values(2, 'Maria');

ExampleIntegrationTRTest.testExampleInsertWithRollback :此测试以4.1传递,但由于5.0.7.Final中的javax.persistence.EntityExistsException而失败。显然它试图创建新的FooEmployee,为它分配一个id,它已作为初始化的一部分存在于DB中。

@ContextConfiguration({ "/spring/test/test-context.xml" })
@Test
public class ExampleIntegrationTRTest extends AbstractTransactionalTestNGSpringContextTests {

    @Transactional
public void testExampleInsertWithRollback() {

    FooEmployee response = null;
    response = fooUserService.getEmployee(1);
    //response is FooEmpleado ( BaseEntity ( FooEmployee@590013c7    id = 1     )    id = 1    nombre = Louis     )

    FooEmployee newFoo = new FooEmployee();
    newFoo.setName("James");
    createdFooWithRollback = fooUserService.createEmployee(newFoo).getId();
    //The line above throws the exception

    assertNotNull(createdFooWithRollback);
    EntityManagerHolder holder = (EntityManagerHolder)TransactionSynchronizationManager.getResource(entityManagerFactory);
    EntityManager em = holder.getEntityManager();
    em.flush();

    em.clear();

    response = null;
    response = fooUserService.getEmployee(createdFooWithRollback);

    assertNotNull(response);
    assertEquals(response.getId(), createdFooWithRollback);
    assertEquals(response.getName(), "James");

}

堆栈跟踪

org.springframework.dao.DataIntegrityViolationException: A different object with the same identifier value was already associated with the session : [com.codependent.s4h5.entity.FooEmployeeEntity#1]; nested exception is javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session : [com.codependent.s4h5.entity.FooEmployeeEntity#1]
    at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:410)
    at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:223)
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:417)
    at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:59)
    at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:147)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:131)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
    at com.sun.proxy.$Proxy52.save(Unknown Source)
    at com.codependent.s4h5.service.impl.FooUserServiceImpl.createEmployee(FooUserServiceImpl.java:36)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:302)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
    at com.sun.proxy.$Proxy57.createEmployee(Unknown Source)
    at com.codependent.s4h5.ExampleIntegrationTRTest.testExampleInsertWithRollback(ExampleIntegrationTRTest.java:47)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:85)
    at org.testng.internal.MethodInvocationHelper$1.runTestMethod(MethodInvocationHelper.java:200)
    at org.springframework.test.context.testng.AbstractTestNGSpringContextTests.run(AbstractTestNGSpringContextTests.java:175)
    at org.testng.internal.MethodInvocationHelper.invokeHookable(MethodInvocationHelper.java:212)
    at org.testng.internal.Invoker.invokeMethod(Invoker.java:689)
    at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:882)
    at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:1189)
    at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:124)
    at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:108)
    at org.testng.TestRunner.privateRun(TestRunner.java:767)
    at org.testng.TestRunner.run(TestRunner.java:617)
    at org.testng.SuiteRunner.runTest(SuiteRunner.java:348)
    at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:343)
    at org.testng.SuiteRunner.privateRun(SuiteRunner.java:305)
    at org.testng.SuiteRunner.run(SuiteRunner.java:254)
    at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
    at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:86)
    at org.testng.TestNG.runSuitesSequentially(TestNG.java:1224)
    at org.testng.TestNG.runSuitesLocally(TestNG.java:1149)
    at org.testng.TestNG.run(TestNG.java:1057)
    at org.testng.remote.RemoteTestNG.run(RemoteTestNG.java:111)
    at org.testng.remote.RemoteTestNG.initAndRun(RemoteTestNG.java:204)
    at org.testng.remote.RemoteTestNG.main(RemoteTestNG.java:175)
Caused by: javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session : [com.codependent.s4h5.entity.FooEmployeeEntity#1]
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1664)
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1602)
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1608)
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:1152)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:293)
    at com.sun.proxy.$Proxy47.persist(Unknown Source)
    at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:439)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:483)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:468)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:440)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:61)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)
    ... 49 more

Spring的test-context.xml

<tx:annotation-driven order="0" />

<!-- Drives transactions using datasource transaction manager -->
<bean name="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="mainEntityManagerFactory" />
</bean>

<bean id="mainEntityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="persistenceUnitName" value="mainPersistenceUnit"/>
    <property name="dataSource" ref="mainDataSource"/>
    <property name="packagesToScan" value="com.codependent.s4h5.entity"/>
    <property name="jpaProperties">
        <props>
            <prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop>
            <prop key="hibernate.hbm2ddl.auto">create-drop</prop>
            <prop key="hibernate.show_sql">false</prop>
            <prop key="hibernate.format_sql">true</prop>
            <prop key="hibernate.hbm2ddl.import_files">scripts/initData.sql</prop>
        </props>
    </property>
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
    </property>
    <property name="jpaDialect">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/>
    </property>
</bean>

<import resource="classpath:com/codependent/s4h5/spring/library.xml" />

<jdbc:embedded-database id="mainDataSource" type="HSQL"/>

<jpa:repositories base-package="com.codependent.s4h5.dao" entity-manager-factory-ref="mainEntityManagerFactory"/>

<context:component-scan base-package="com.codependent.s4h5.service.impl" />
<context:component-scan base-package="com.codependent.s4h5.mapper" />
<bean id="dozerMapper" class="org.dozer.spring.DozerBeanMapperFactoryBean"/>

项目pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.codependent.s4h5</groupId>
    <artifactId>spring4-hibernate5</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <description>Test</description>

    <dependencyManagement>
        <dependencies>
            <!-- SPRING DATA -->
            <dependency>
                <groupId>org.springframework.data</groupId>
                <artifactId>spring-data-releasetrain</artifactId>
                <version>${spring-data-releasetrain-version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <!-- SPRING -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring-version}</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring-version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>${spring-version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring-version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${spring-version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>${spring-version}</version>

        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-expression</artifactId>
            <version>${spring-version}</version>

        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring-version}</version>

        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jms</artifactId>
            <version>${spring-version}</version>

        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>${spring-version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-oxm</artifactId>
            <version>${spring-version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>${spring-version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${spring-version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring-version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-jpa</artifactId>
        </dependency>

        <!-- HIBERNATE -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>${hibernate-version}</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>${hibernate-version}</version>
        </dependency>

        <!-- DOZER -->
        <dependency>
            <groupId>net.sf.dozer</groupId>
            <artifactId>dozer</artifactId>
            <version>5.5.1</version>
        </dependency>
        <dependency>
            <groupId>net.sf.dozer</groupId>
            <artifactId>dozer-spring</artifactId>
            <version>5.5.1</version>
        </dependency>

        <!-- log system: slf4j -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j-version}</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>${slf4j-version}</version>
            <optional>true</optional>
            <scope>runtime</scope>
        </dependency>

        <!-- TEST dependencies -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring-version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>${testng-version}</version>
            <type>jar</type>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>junit</groupId>
                    <artifactId>junit</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.powermock</groupId>
            <artifactId>powermock-api-mockito</artifactId>
            <version>${powermock-version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.powermock</groupId>
            <artifactId>powermock-module-testng</artifactId>
            <version>${powermock-version}</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.hsqldb</groupId>
            <artifactId>hsqldb-j5</artifactId>
            <version>2.2.4</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>${log4j-version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>${log4j-version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j-impl</artifactId>
            <version>${log4j-version}</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.jboss.logging</groupId>
            <artifactId>jboss-logging</artifactId>
            <version>${jboss-logging-version}</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.kubek2k</groupId>
            <artifactId>springockito-annotations</artifactId>
            <version>1.0.9</version>
            <scope>test</scope>         
        </dependency>

    </dependencies>


    <properties>
        <spring-version>4.2.1.RELEASE</spring-version>
        <spring-data-releasetrain-version>Gosling-SR1</spring-data-releasetrain-version>
        <hibernate-version>4.2.19.Final</hibernate-version>
        <testng-version>6.8.17</testng-version>
        <powermock-version>1.6.2</powermock-version>
        <slf4j-version>1.7.7</slf4j-version>
        <log4j-version>2.2</log4j-version>
        <jboss-logging-version>3.2.1.Final</jboss-logging-version>
    </properties>

</project>

BaseEntity.java

@SuppressWarnings("serial")
public abstract class BaseEntity<IdType extends Serializable> implements Serializable {

    public abstract IdType getId();

    public abstract void setId(IdType id);

}

FooEmployeeEntity.java

@Entity
@Table(name="TFOEMPLE")
public class FooEmployeeEntity extends BaseEntity<Integer> {

    private static final long serialVersionUID = 2583160744316336577L;

    @Id
    @Column(name="FOEMPLE_ID")
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Integer id ;

    @Column(name="FOEMPLE_NAME")
    private String name;

    @ManyToOne
    @JoinColumn(name="FOEMPLE_FODEPAR_ID")
    private FooDepartmentEntity department;

    //Getters-Setters

FooDepartmentEntity.java

@Entity
@Table(name="TFODEPAR")
public class FooDepartmentEntity extends BaseEntity<Integer> {

    private static final long serialVersionUID = 2583160744316336577L;

    @Id
    @Column(name="FODEPAR_ID")
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Integer id ;

    @Column(name="FODEPAR_NAME")
    private String name;

    @OneToMany(mappedBy="department",cascade={CascadeType.MERGE,CascadeType.REFRESH, CascadeType.PERSIST, CascadeType.DETACH})
    private List<FooEmployeeEntity> employees;

    //Getters-Setters

1 个答案:

答案 0 :(得分:1)

以下是4.2.19.Fina l和5.0.7.Final

生成的表格创建日志

<强> 4.2.19.Final

create table TFODEPAR (
    FODEPAR_ID integer generated by default as identity (start with 1),
    FODEPAR_NAME varchar(255),
    primary key (FODEPAR_ID)
)

<强> 5.0.7.Final

create table TFODEPAR (
    FODEPAR_ID integer not null,
    FODEPAR_NAME varchar(255),
    primary key (FODEPAR_ID)
)

4.2.19.Final中,id列生成为标识列,而在5.0.7.Final中,它生成为普通的非可空列。我怀疑罪魁祸首是@GeneratedValue(strategy=GenerationType.AUTO)选择正确的id生成策略的方式。似乎在版本5之前,它正在挑选GenerationType.IDENTITY,而在版本5中则不是。{

因此,要解决此问题,请将@GeneratedValue(strategy=GenerationType.AUTO)更改为@GeneratedValue(strategy=GenerationType.IDENTITY),以强制告诉hibernate将id列作为标识列。