我打算从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
答案 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列作为标识列。