剖析javax.persistence.Table.indexes()异常原因

折月煮酒 提交于 2019-12-03 17:52:23

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搜索下,就可以发现问题。



易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!