I'm currently setting up a new Hibernate project and finally getting around to trying out JPA annotations instead of using any Hibernate configuration or mapping files-- what the hell took me so long!!??! Anyhow, among the many gotchas I came across, one was how to deal with using Interfaces with Hibernate entity beans and associations.
When you declare an association to a Hibernate Entity with an Interface without using the correct annotation syntax to point Hibernate at the implementing class, you will likely see exceptions like this while running JUnit 4 tests in Eclipse:
-
java.lang.IllegalStateException: Failed to load ApplicationContext
-
at org.springframework.test.context.TestContext.getApplicationContext(TestContext.java:201)
-
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:109)
-
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:75)
-
at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:255)
-
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:111)
-
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.invokeTestMethod(SpringJUnit4ClassRunner.java:148)
-
at org.junit.internal.runners.JUnit4ClassRunner.runMethods(JUnit4ClassRunner.java:51)
-
at org.junit.internal.runners.JUnit4ClassRunner$1.run(JUnit4ClassRunner.java:44)
-
at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:27)
-
at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:37)
-
at org.junit.internal.runners.JUnit4ClassRunner.run(JUnit4ClassRunner.java:42)
-
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:97)
-
at org.junit.internal.runners.CompositeRunner.runChildren(CompositeRunner.java:33)
-
at org.junit.runners.Suite.access$000(Suite.java:26)
-
at org.junit.runners.Suite$1.run(Suite.java:93)
-
at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:27)
-
at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:37)
-
at org.junit.runners.Suite.run(Suite.java:91)
-
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:38)
-
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
-
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
-
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
-
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
-
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
-
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'geoSessionFactory' defined in class path resource [com/peerdigital/spring/applicationContext.xml]: Invocation of init method failed; nested exception is org.hibernate.AnnotationException: @OneToOne or @ManyToOne on com.peerdigital.geolocation.model.DefaultCountryEntityImpl.regionEntity references an unknown entity: com.peerdigital.geolocation.model.DefaultRegionEntityImpl
-
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1338)
-
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:473)
-
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory$1.run(AbstractAutowireCapableBeanFactory.java:409)
-
at java.security.AccessController.doPrivileged(Native Method)
-
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:380)
-
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:264)
-
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
-
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:261)
-
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:185)
-
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:164)
-
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:423)
-
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:728)
-
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:380)
-
at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:84)
-
at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:42)
-
at org.springframework.test.context.TestContext.loadApplicationContext(TestContext.java:173)
-
at org.springframework.test.context.TestContext.getApplicationContext(TestContext.java:197)
-
... 23 more
-
Caused by: org.hibernate.AnnotationException: @OneToOne or @ManyToOne on com.peerdigital.geolocation.model.DefaultCountryEntityImpl.regionEntity references an unknown entity: com.peerdigital.geolocation.model.DefaultRegionEntityImpl
-
at org.hibernate.cfg.ToOneFkSecondPass.doSecondPass(ToOneFkSecondPass.java:81)
-
at org.hibernate.cfg.AnnotationConfiguration.processEndOfQueue(AnnotationConfiguration.java:456)
-
at org.hibernate.cfg.AnnotationConfiguration.processFkSecondPassInOrder(AnnotationConfiguration.java:438)
-
at org.hibernate.cfg.AnnotationConfiguration.secondPassCompile(AnnotationConfiguration.java:309)
-
at org.hibernate.cfg.Configuration.buildMappings(Configuration.java:1148)
-
at org.springframework.orm.hibernate3.LocalSessionFactoryBean.buildSessionFactory(LocalSessionFactoryBean.java:673)
-
at org.springframework.orm.hibernate3.AbstractSessionFactoryBean.afterPropertiesSet(AbstractSessionFactoryBean.java:211)
-
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1369)
-
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1335)
-
... 39 more
Most of the solutions I saw made you create a duplicate persistence.xml that had all of the same information in it which seemed silly to me. Eventually with the help of Google I came across the answer somewhere (which I no longer have around) which was to modify the field annotation to point at the class which implements the Interface:
-
@ManyToOne(targetEntity = DefaultRegionEntityImpl.class)
-
@JoinColumn(name = "region_code_fk", referencedColumnName = "region_code_pk", insertable = false, updatable = false)
-
private RegionEntity regionEntity;
One other thing to check-- make sure your @Entity annotation is importing the JPA annotation (javax.persistence.Entity), and NOT the Hibernate annotation (org.hibernate.annotations.Entity). If you import the Hibernate @Entity annotation it will not find the entity bean.
Hopefully that's of some help to someone. Just a quick post-- back to work I go!
One Response
Stay in touch with the conversation, subscribe to the RSS feed for comments on this post.
Good Explanation. Thanks