剖析javax.persistence.Table.indexes()異常緣由

1.異常內容

我遇到的具體現象是,將工程發佈到tomcat能夠正常運行,可是經過junit跑測試,就會以下錯誤: java

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: spring

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的方法。 api

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類。 tomcat

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這玩意 app


這廝的jar包裏果真有個沒有indexes方法的Table類,再仔細看看jar包的名字"javax.persistence_2.0.4",原來這是JPA2.0的,而Table的indexes在JPA2.1中才有的,而junit在運行的時候,恰巧是先走了這個jar包中的類。 ide

3.總結分析

Hibernate在進行註解處理的過程當中,既處理了JPA標準的註解,也處理了hibernate特有的註解,hibernate4.3處理註解是基於JPA2.1版本的,因此,我推想,其餘遇到這個exception的朋友,頗有可能也是使用的jar包中有舊版本的Table類。不妨用JarSearch搜索下,就能夠發現問題。 工具

相關文章
相關標籤/搜索