简体   繁体   中英

JPA many-to-many with extra columns using @IdClass fails in springTestContextPreparation Hibernate AnnotationException “has no persistent id property”

I am trying to represent a bidirectional relationship with some entities using the @IdClass annotation as is shown in this answer: https://stackoverflow.com/a/32920550/8977519

I have a solution setup that follows the linked answer exactly and the application runs fine. I am able to use all of my CRUD end points and the changes are reflected in the DB. However, when I try to execute my tests the springTestContextPrepareTestInstance step fails.

Composite Id Class

@Getter
@Setter
@EqualsAndHashCode
public class CompositeId implements Serializable {
    private static final long serialVersionUID = ...;

    @Type(type = "uuid-char")
    private UUID entity1;
    @Type(type = "uuid-char")
    private UUID entity2;
}

Bidirectional Entity Class

@Entity
@Table(name = "bidirectional_entity")
@NoArgsConstructor
@Getter
@Setter
@IdClass(CompositeId.class)
public class BidirectionalEntity {
    @Id
    @ManyToOne
    @JoinColumn(name = "entity1_id", referencedColumnName = "id")
    private Entity1 entity1;

    @Id
    @ManyToOne
    @JoinColumn(name = "entity2_id", referencedColumnName = "id")
    private Entity2 entity2;
    ...
}

Entity Classes

@Entity
@Table(name = "entity1")
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@ToString
public class Entity1 {
    @Id
    @GenericGenerator(name = "uuid2", strategy = "uuid2")
    @GeneratedValue(generator = "uuid2")
    @Column(name = "id", updatable = false)
    @Type(type = "uuid-char")
    private UUID id;

    @OneToMany(mappedBy = "entity1",  orphanRemoval=true, cascade = CascadeType.ALL)
    @Setter(AccessLevel.NONE)
    private List<BidirectionalEntity> entities = new ArrayList<>();
    ...
}

@Entity
@Table(name = "entity2")
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@ToString
public class Entity2 {
    @Id
    @GenericGenerator(name = "uuid2", strategy = "uuid2")
    @GeneratedValue(generator = "uuid2")
    @Column(name = "id", updatable = false)
    @Type(type = "uuid-char")
    private UUID id;

    @OneToMany(mappedBy = "entity2",  orphanRemoval=true, cascade = CascadeType.ALL)
    @Setter(AccessLevel.NONE)
    private List<BidirectionalEntity> entities = new ArrayList<>();
    ...
}

The error given is:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is org.hibernate.AnnotationException: {CompositeId Class} has no persistent id property
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1778)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:593)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1105)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:775)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:316)
at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:127)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:99)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:117)
at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:108)
at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:190)
at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:132)
at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:246)
at org.springframework.test.context.testng.AbstractTestNGSpringContextTests.springTestContextPrepareTestInstance(AbstractTestNGSpringContextTests.java:145)
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:498)
at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:124)
at org.testng.internal.MethodInvocationHelper.invokeMethodConsideringTimeout(MethodInvocationHelper.java:59)
at org.testng.internal.Invoker.invokeConfigurationMethod(Invoker.java:458)
at org.testng.internal.Invoker.invokeConfigurations(Invoker.java:222)
at org.testng.internal.Invoker.invokeConfigurations(Invoker.java:142)
at org.testng.internal.TestMethodWorker.invokeBeforeClassMethods(TestMethodWorker.java:168)
at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:105)
at org.testng.TestRunner.privateRun(TestRunner.java:648)
at org.testng.TestRunner.run(TestRunner.java:505)
at org.testng.SuiteRunner.runTest(SuiteRunner.java:455)
at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:450)
at org.testng.SuiteRunner.privateRun(SuiteRunner.java:415)
at org.testng.SuiteRunner.run(SuiteRunner.java:364)
at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:84)
at org.testng.TestNG.runSuitesSequentially(TestNG.java:1208)
at org.testng.TestNG.runSuitesLocally(TestNG.java:1137)
at org.testng.TestNG.runSuites(TestNG.java:1049)
at org.testng.TestNG.run(TestNG.java:1017)
at org.testng.IDEARemoteTestNG.run(IDEARemoteTestNG.java:73)
at org.testng.RemoteTestNGStarter.main(RemoteTestNGStarter.java:123)
Caused by: org.hibernate.AnnotationException: {CompositeId Class} has no persistent id property
at org.hibernate.cfg.AnnotationBinder.bindIdClass(AnnotationBinder.java:2858)
at org.hibernate.cfg.AnnotationBinder.mapAsIdClass(AnnotationBinder.java:1053)
at org.hibernate.cfg.AnnotationBinder.bindClass(AnnotationBinder.java:781)
at org.hibernate.boot.model.source.internal.annotations.AnnotationMetadataSourceProcessorImpl.processEntityHierarchies(AnnotationMetadataSourceProcessorImpl.java:250)
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess$1.processEntityHierarchies(MetadataBuildingProcess.java:231)
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:274)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:904)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:935)
at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:57)
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:365)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:390)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:377)
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:341)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1837)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1774)
... 45 more

Update

After doing some novice debugging, in the Hibernate Core (5.3.9) AnnotationBinder class -> bindIdClass method the componentId object properties list is empty when performing setup for test(obviously, it's what is checked to throw the error). However, when running the program normally, there are two properties of value org.hibernate.mapping.SimpleValue([org.hibernate.mapping.Column(entity*_id)]) with name entity*. Matching up with the BidirectionalEntity class as it should be.

I have figured out the issue. Along with my service tests I was also writing tests to verify constraint validation annotations on POJOs. I forgot to add 'Test' to the end of the CompositeId test class name ( CompositeIdTest ). It seems that in the spring test context checking the test class was getting pulled in and checked for ID properties by Hibernate. Once I changed the test class name, tests starting running fine.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM