引言: 在基于SpringData/JPA来快速开发若干功能过程中,碰到了table is not Mapped问题,经过一番辛苦的调试测试之后,才发现了一个@Entity的属性name的妙用。
1. 问题的提出
场景描述: 在开发中,做几个功能类似的模块,但代码需要独立,方便后续的独立部署。故出现了很多包路径不同,但是类的名称类似的类。在Model中定义了很多名称相同的实体类,都是以@Entity来定义的。
@Entity @Table(name="table1") public class Model1 { ........ }
在项目加载过程中,出席那了以下错误信息:
SEVERE: Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListener org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [META-INF/spring-hibernate.xml]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: [PersistenceUnit: myPersistenceUnit] Unable to build EntityManagerFactory at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1513) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:521) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:458) at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:293) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:290) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:191) at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1117) at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:922) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:479) at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:410) at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:306) at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:112) at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4135) at org.apache.catalina.core.StandardContext.start(StandardContext.java:4630) at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045) at org.apache.catalina.core.StandardHost.start(StandardHost.java:785) at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045) at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:445) at org.apache.catalina.startup.Embedded.start(Embedded.java:825) at org.codehaus.mojo.tomcat.AbstractRunMojo.startContainer(AbstractRunMojo.java:558) at org.codehaus.mojo.tomcat.AbstractRunMojo.execute(AbstractRunMojo.java:255) at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:132) at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:208) at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153) at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:145) at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:116) at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:80) at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build(SingleThreadedBuilder.java:51) at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:120) at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:347) at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:154) at org.apache.maven.cli.MavenCli.execute(MavenCli.java:582) at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:214) at org.apache.maven.cli.MavenCli.main(MavenCli.java:158) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:289) at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:229) at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:415) at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:356) Caused by: javax.persistence.PersistenceException: [PersistenceUnit: myPersistenceUnit] Unable to build EntityManagerFactory at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:924) at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:899) at org.hibernate.ejb.HibernatePersistence.createContainerEntityManagerFactory(HibernatePersistence.java:76) at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:290) at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:310) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1572) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1510) ... 42 more Caused by: org.hibernate.AnnotationException: Use of the same entity name twice: OrderEntity at org.hibernate.cfg.annotations.EntityBinder.bindEntity(EntityBinder.java:403) at org.hibernate.cfg.AnnotationBinder.bindClass(AnnotationBinder.java:602) at org.hibernate.cfg.Configuration$MetadataSourceQueue.processAnnotatedClassesQueue(Configuration.java:3568) at org.hibernate.cfg.Configuration$MetadataSourceQueue.processMetadata(Configuration.java:3522) at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1379) at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1784) at org.hibernate.ejb.EntityManagerFactoryImpl.<init>(EntityManagerFactoryImpl.java:96) at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:914) ... 48 more Caused by: org.hibernate.DuplicateMappingException: duplicate import: OrderEntity refers to both com.creditease.bsettle.pay.model.OrderEntity and com.creditease.bsettle.expense.model.OrderEntity (try using auto-import="false") at org.hibernate.cfg.Configuration$MappingsImpl.addImport(Configuration.java:2674) at org.hibernate.cfg.annotations.EntityBinder.bindEntity(EntityBinder.java:396) ... 55 more
2. 问题分析
在项目代码中,的确定义了2个OrderEntity的class,但是他们是处于不同的项目包中的,所以从java类层次上说,是不会冲突的。但是日志中,明确提示了冲突,且标识为重复import,就是说OrderEntity被导入了2次。更进一步说,两个Entity,我们的编译器无法分辨谁是谁,路径区分在这里不好用了。
经过观察分析,发现在JPA中是利用@Entity来定义标识的, 故推理可知如下信息:
1. @Entity标识的实体类是JPA中进行管理和映射的Entity, 其在JPA中默认的名字为class name首字母小写。比如AccountEntity,其默认的实体名称为accountEntity.
2. 相同的类名在JPA中,默认的实体名称相同,故无法正确识别,这个就是问题的来源。
3. 问题修正
于是将相同类名的Model类中Entity进行了重新命名,即@Entity(name="entity name"), 确保Entity中的名称不同即可。
4. 遇到新的错误信息
在修改完毕之后,重新启动系统,然后碰到了新的问题如下:
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.creditease.bsettle.pay.repository.OrderEntityRepository com.creditease.bsettle.pay.service.impl.OrderServiceImpl.orderEntityRepository; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'orderEntityRepositoryImpl': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: com.creditease.bsettle.pay.repository.OrderEntityRepository com.creditease.bsettle.pay.repository.OrderEntityRepositoryImpl.orderEntityRepository; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'payOrderEntityRepo': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Validation failed for query for method public abstract com.creditease.bsettle.pay.model.OrderEntity com.creditease.bsettle.pay.repository.OrderEntityRepository.findByOuterOrderId(java.lang.String)! at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:517) at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87) at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:286) ... 54 more Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'orderEntityRepositoryImpl': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: com.creditease.bsettle.pay.repository.OrderEntityRepository com.creditease.bsettle.pay.repository.OrderEntityRepositoryImpl.orderEntityRepository; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'payOrderEntityRepo': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Validation failed for query for method public abstract com.creditease.bsettle.pay.model.OrderEntity com.creditease.bsettle.pay.repository.OrderEntityRepository.findByOuterOrderId(java.lang.String)! at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:289) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1147) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:519) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:458) at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:293) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:290) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:191) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:283) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:191) at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:917) at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:860) at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:775) at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:489) ... 56 more Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: com.creditease.bsettle.pay.repository.OrderEntityRepository com.creditease.bsettle.pay.repository.OrderEntityRepositoryImpl.orderEntityRepository; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'payOrderEntityRepo': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Validation failed for query for method public abstract com.creditease.bsettle.pay.model.OrderEntity com.creditease.bsettle.pay.repository.OrderEntityRepository.findByOuterOrderId(java.lang.String)! at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:517) at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87) at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:286) ... 69 more Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'payOrderEntityRepo': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Validation failed for query for method public abstract com.creditease.bsettle.pay.model.OrderEntity com.creditease.bsettle.pay.repository.OrderEntityRepository.findByOuterOrderId(java.lang.String)! at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1513) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:521) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:458) at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:293) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:290) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:191) at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:917) at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:860) at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:775) at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:489) ... 71 more Caused by: java.lang.IllegalArgumentException: Validation failed for query for method public abstract com.creditease.bsettle.pay.model.OrderEntity com.creditease.bsettle.pay.repository.OrderEntityRepository.findByOuterOrderId(java.lang.String)! at org.springframework.data.jpa.repository.query.SimpleJpaQuery.validateQuery(SimpleJpaQuery.java:84) at org.springframework.data.jpa.repository.query.SimpleJpaQuery.<init>(SimpleJpaQuery.java:54) at org.springframework.data.jpa.repository.query.JpaQueryFactory.fromMethodWithQueryString(JpaQueryFactory.java:65) at org.springframework.data.jpa.repository.query.JpaQueryFactory.fromQueryAnnotation(JpaQueryFactory.java:48) at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$DeclaredQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:115) at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$CreateIfNotFoundQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:166) at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$AbstractQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:69) at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.<init>(RepositoryFactorySupport.java:320) at org.springframework.data.repository.core.support.RepositoryFactorySupport.getRepository(RepositoryFactorySupport.java:169) at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.initAndReturn(RepositoryFactoryBeanSupport.java:224) at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.afterPropertiesSet(RepositoryFactoryBeanSupport.java:210) at org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean.afterPropertiesSet(JpaRepositoryFactoryBean.java:92) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1572) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1510) ... 81 more Caused by: java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException: OrderEntity is not mapped [from OrderEntity entity where entity.outerOrderId = :outerOrderId] at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1374) at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1310) at org.hibernate.ejb.AbstractEntityManagerImpl.createQuery(AbstractEntityManagerImpl.java:294) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:366) at $Proxy48.createQuery(Unknown Source) at org.springframework.data.jpa.repository.query.SimpleJpaQuery.validateQuery(SimpleJpaQuery.java:78) ... 94 more Caused by: org.hibernate.hql.internal.ast.QuerySyntaxException: OrderEntity is not mapped [from OrderEntity entity where entity.outerOrderId = :outerOrderId] at org.hibernate.hql.internal.ast.util.SessionFactoryHelper.requireClassPersister(SessionFactoryHelper.java:180) at org.hibernate.hql.internal.ast.tree.FromElementFactory.addFromElement(FromElementFactory.java:110) at org.hibernate.hql.internal.ast.tree.FromClause.addFromElement(FromClause.java:93) at org.hibernate.hql.internal.ast.HqlSqlWalker.createFromElement(HqlSqlWalker.java:325) at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.fromElement(HqlSqlBaseWalker.java:3544) at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.fromElementList(HqlSqlBaseWalker.java:3433) at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.fromClause(HqlSqlBaseWalker.java:708) at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.query(HqlSqlBaseWalker.java:564) at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.selectStatement(HqlSqlBaseWalker.java:301) at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.statement(HqlSqlBaseWalker.java:249) at org.hibernate.hql.internal.ast.QueryTranslatorImpl.analyze(QueryTranslatorImpl.java:250) at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:185) at org.hibernate.hql.internal.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:138) at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:104) at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:79) at org.hibernate.engine.query.spi.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:168) at org.hibernate.internal.AbstractSessionImpl.getHQLQueryPlan(AbstractSessionImpl.java:222) at org.hibernate.internal.AbstractSessionImpl.createQuery(AbstractSessionImpl.java:200) at org.hibernate.internal.SessionImpl.createQuery(SessionImpl.java:1703) at org.hibernate.ejb.AbstractEntityManagerImpl.createQuery(AbstractEntityManagerImpl.java:291) ... 101 more
5. 问题的二次分析
这次的错误信息与上次不同,重点是OrderEntity在JQL中无法进行映射,OrderEntity这个类不是已经正确配置到数据库表的映射了吗?那问题出现在哪里呢?
忽然回想起在之前的@Entity中的实体名已经被重新命名了,不再是缺省的名称了。
想到这里,就知道问题在哪里了,就是讲JQL中的OrderEntity修改为@Entity(name="xxx")中重新命名的实体名称就可以了。
重新启动一下系统,错误信息消失了。
6. @Entity中name作用
其name所标住的就是实体类的同名信息,可以在JQL中使用。如无特别指定新名称,则默认为类名。