1.异常内容
我遇到的具体现象是,将工程发布到tomcat可以正常运行,但是通过junit跑测试,就会如下错误:
Caused by: java.lang.NoSuchMethodError: javax.persistence.Table.indexes()[Ljavax/persistence/Index;
at org.hibernate.cfg.annotations.EntityBinder.processComplementaryTableDefinitions(EntityBinder.java:936) ~[hibernate-core-4.3.5.Final.jar:4.3.5.Final]
at org.hibernate.cfg.AnnotationBinder.bindClass(AnnotationBinder.java:824) ~[hibernate-core-4.3.5.Final.jar:4.3.5.Final]
at org.hibernate.cfg.Configuration$MetadataSourceQueue.processAnnotatedClassesQueue(Configuration.java:3788) ~[hibernate-core-4.3.5.Final.jar:4.3.5.Final]
at org.hibernate.cfg.Configuration$MetadataSourceQueue.processMetadata(Configuration.java:3742) ~[hibernate-core-4.3.5.Final.jar:4.3.5.Final]
at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1410) ~[hibernate-core-4.3.5.Final.jar:4.3.5.Final]
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1844) ~[hibernate-core-4.3.5.Final.jar:4.3.5.Final]
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:850) ~[hibernate-entitymanager-4.3.5.Final.jar:4.3.5.Final]
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:843) ~[hibernate-entitymanager-4.3.5.Final.jar:4.3.5.Final]
at org.hibernate.boot.registry.classloading.internal.ClassLoaderServiceImpl.withTccl(ClassLoaderServiceImpl.java:397) ~[hibernate-core-4.3.5.Final.jar:4.3.5.Final]
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:842) ~[hibernate-entitymanager-4.3.5.Final.jar:4.3.5.Final]
at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:60) ~[spring-orm-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:341) ~[spring-orm-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:318) ~[spring-orm-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1627) ~[spring-beans-4.1.1.RELEASE.jar:4.1.1.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1564) ~[spring-beans-4.1.1.RELEASE.jar:4.1.1.RELEASE]
... 25 more
如果按照网上的一些解决办法,将@Table(name="t_user")改写为@Entity(name="t_user")这种形式,问题依然无法解决的,会出现如下的Exception:
Caused by: org.hibernate.hql.internal.ast.QuerySyntaxException: User is not mapped
at org.hibernate.hql.internal.ast.util.SessionFactoryHelper.requireClassPersister(SessionFactoryHelper.java:189)
at org.hibernate.hql.internal.ast.tree.FromElementFactory.addFromElement(FromElementFactory.java:109)
at org.hibernate.hql.internal.ast.tree.FromClause.addFromElement(FromClause.java:95)
at org.hibernate.hql.internal.ast.HqlSqlWalker.createFromElement(HqlSqlWalker.java:331)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.fromElement(HqlSqlBaseWalker.java:3633)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.fromElementList(HqlSqlBaseWalker.java:3522)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.fromClause(HqlSqlBaseWalker.java:706)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.query(HqlSqlBaseWalker.java:562)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.selectStatement(HqlSqlBaseWalker.java:299)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.statement(HqlSqlBaseWalker.java:247)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.analyze(QueryTranslatorImpl.java:278)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:206)
... 42 more
2.原因定位
顺着异常提示信息,打开hibernate-core-4.3.5.Final.jar:4.3.5.Final这个jar包(我用的反编译工具),一路追到这个类org.hibernate.cfg.AnnotationBinder,在bindClass方法中,可以发现在最后面连续调用了三次EntityBinder类的processComplementaryTableDefinitions方法,也就是异常信息中最后抛出Exception的方法。
public static void bindClass(XClass clazzToProcess, Map<XClass, InheritanceState> inheritanceStatePerClass, Mappings mappings)
throws MappingException
{
//此处省略N行代码
mappings.addClass(persistentClass);
mappings.addSecondPass(new SecondaryTableSecondPass(entityBinder, propertyHolder, clazzToProcess));
entityBinder.processComplementaryTableDefinitions((org.hibernate.annotations.Table)clazzToProcess.getAnnotation(org.hibernate.annotations.Table.class));
entityBinder.processComplementaryTableDefinitions((Tables)clazzToProcess.getAnnotation(Tables.class));
entityBinder.processComplementaryTableDefinitions(tabAnn);
}
再打开EntityBinder类,可以发现有两个同名的processComplementaryTableDefinitions方法,两个方法中都调用了table.indexes(),两个方法的参数虽然都是table,但是类型是不同的。一个是JPA api中的Table类,另一个是hibernate的Table类。
public void processComplementaryTableDefinitions(javax.persistence.Table table) {
if (table == null) return;
TableBinder.addIndexes(this.persistentClass.getTable(), table.indexes(), this.mappings);
}
public void processComplementaryTableDefinitions(org.hibernate.annotations.Table table) {
if (table == null) return;
String appliedTable = table.appliesTo();
Iterator tables = this.persistentClass.getTableClosureIterator();
org.hibernate.mapping.Table hibTable = null;
while (tables.hasNext()) {
org.hibernate.mapping.Table pcTable = (org.hibernate.mapping.Table)tables.next();
if (pcTable.getQuotedName().equals(appliedTable))
{
hibTable = pcTable;
break;
}
hibTable = null;
}
if (hibTable == null)
{
for (Join join : this.secondaryTables.values()) {
if (join.getTable().getQuotedName().equals(appliedTable)) {
hibTable = join.getTable();
break;
}
}
}
if (hibTable == null) {
throw new AnnotationException("@org.hibernate.annotations.Table references an unknown table: " + appliedTable);
}
if (!BinderHelper.isEmptyAnnotationValue(table.comment())) hibTable.setComment(table.comment());
TableBinder.addIndexes(hibTable, table.indexes(), this.mappings);
}
回头再看看异常信息,指向的是
javax.persistence.Table这个类的indexes方法。首先想到的是打开
hibernate-jpa-2.1-api-1.0.0.Final.jar这个jar包,找到
javax.persistence.Table,一探究竟。结果两个类都是有indexes这个方法的,不免让人挠头,莫非有鬼。
后来仔细一想,javax.persistence.Table是JPA标准api类,不一定就只有那个jar包里有啊,赶紧打开Java Build Path看了一下,发现了EclipseLink这玩意
这厮的jar包里果然有个没有indexes方法的Table类,再仔细看看jar包的名字"javax.persistence_2.0.4",原来这是JPA2.0的,而Table的indexes在JPA2.1中才有的,而junit在运行的时候,恰巧是先走了这个jar包中的类。
3.总结分析
Hibernate在进行注解处理的过程中,既处理了JPA标准的注解,也处理了hibernate特有的注解,hibernate4.3处理注解是基于JPA2.1版本的,所以,我推想,其他遇到这个exception的朋友,很有可能也是使用的jar包中有旧版本的Table类。不妨用JarSearch搜索下,就可以发现问题。
来源:oschina
链接:https://my.oschina.net/u/166585/blog/539095