哪个 jar 部署了 Hibernate 5.1 class org.hibernate.service.jdbc.dialect.internal.DialectFactoryImpl?

Which jar deploys Hibernate 5.1 class org.hibernate.service.jdbc.dialect.internal.DialectFactoryImpl?

我正在尝试配置 JBoss AS 7.1.3 以支持 Hibernate 5.1.10.Final,因为此版本修复了一个错误在我的申请中。目前不可能(请不要坚持)升级到WildFly。

为版本 5.1.10.Final 创建了一个新的 JBoss 模块,如下所示:

<module xmlns="urn:jboss:module:1.1" name="org.hibernate" slot="5.1.10.Final">
    <resources>
        <resource-root path="hibernate-core-5.1.10.Final.jar"/>
        <resource-root path="hibernate-entitymanager-5.1.10.Final.jar"/>
        <resource-root path="hibernate-infinispan-5.1.10.Final.jar"/>
        <!-- Insert resources here -->
    </resources>

    <dependencies>
        <module name="asm.asm"/>
        <module name="javax.api"/>
        <module name="javax.persistence.api"/>
        <module name="javax.transaction.api"/>
        <module name="javax.validation.api"/>
        <module name="org.antlr"/>
        <module name="org.apache.commons.collections"/>
        <module name="org.dom4j"/>
        <module name="org.infinispan" optional="true"/>
        <module name="org.javassist"/>
        <module name="org.jboss.as.jpa.hibernate" slot="5" optional="true"/>
        <module name="org.jboss.logging"/>
        <module name="org.hibernate.envers" services="import" optional="true"/>
        <module name="org.hibernate.commons-annotations"/>
    </dependencies>
</module>

persistence.xml配置为使用5.1.10.Final版本,如下图:

<persistence-unit name="myunit" transaction-type="JTA">
    <jta-data-source>java:jboss/env/jdbc/SCA</jta-data-source>

    <!-- Many occurences of <class/> -->
    <class>...</class>

    <properties>
        <property name="hibernate.show_sql" value="false" />
        <property name="hibernate.format_sql" value="false" />
        <property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.JBossAppServerJtaPlatform" />
        <property name="hibernate.dialect" value="org.hibernate.dialect.Oracle12cDialect" />
        <property name="jboss.as.jpa.providerModule" value="org.hibernate:5.1.10.Final"/>
    </properties>
</persistence-unit>

而我的pom.xml也被配置为依赖5.1.10.Final版本,如下图:

<dependencies>
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-core</artifactId>
        <version>5.1.10.Final</version>
    </dependency>
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-entitymanager</artifactId>
        <version>5.1.10.Final</version><!--$NO-MVN-MAN-VER$-->
    </dependency>
    <dependency>
        <groupId>com.oracle.jdbc</groupId>
        <artifactId>ojdbc8</artifactId>
        <version>12.2.0.1</version>
    </dependency>
</dependencies>

应用程序被部署为使用 5.1.10.Final 版本感谢文件 jboss-deployment-structure.xml 如下:

<jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <deployment>
        <exclusions>
            <module name="org.hibernate" slot="main" />
        </exclusions>
        <dependencies>
            <module name="org.hibernate" slot="5.1.10.Final" />
        </dependencies>
    </deployment>
</jboss-deployment-structure>

之后,我可以将我的应用程序部署到 JBoss,但在加载期间我收到以下错误:

11:31:29,815 ERROR [org.jboss.msc.service.fail] (ServerService Thread Pool -- 51) MSC00001: Failed to start service jboss.persistenceunit."sca.war#sca": org.jboss.msc.service.StartException in service jboss.persistenceunit."sca.war#sca": javax.persistence.PersistenceException: [PersistenceUnit: sca] Unable to build EntityManagerFactory
    at org.jboss.as.jpa.service.PersistenceUnitServiceImpl.run(PersistenceUnitServiceImpl.java:100) [jboss-as-jpa-7.1.3.Final.jar:7.1.3.Final]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) [rt.jar:1.7.0_79]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) [rt.jar:1.7.0_79]
    at java.lang.Thread.run(Thread.java:745) [rt.jar:1.7.0_79]
    at org.jboss.threads.JBossThread.run(JBossThread.java:122) [jboss-threads-2.0.0.GA.jar:2.0.0.GA]
Caused by: javax.persistence.PersistenceException: [PersistenceUnit: sca] Unable to build EntityManagerFactory
    at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:915)
    at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:890)
    at org.hibernate.ejb.HibernatePersistence.createContainerEntityManagerFactory(HibernatePersistence.java:74)
    at org.jboss.as.jpa.service.PersistenceUnitServiceImpl.createContainerEntityManagerFactory(PersistenceUnitServiceImpl.java:197) [jboss-as-jpa-7.1.3.Final.jar:7.1.3.Final]
    at org.jboss.as.jpa.service.PersistenceUnitServiceImpl.access0(PersistenceUnitServiceImpl.java:57) [jboss-as-jpa-7.1.3.Final.jar:7.1.3.Final]
    at org.jboss.as.jpa.service.PersistenceUnitServiceImpl.run(PersistenceUnitServiceImpl.java:96) [jboss-as-jpa-7.1.3.Final.jar:7.1.3.Final]
    ... 4 more
Caused by: org.hibernate.HibernateException: Could not instantiate dialect class
    at org.hibernate.service.jdbc.dialect.internal.DialectFactoryImpl.constructDialect(DialectFactoryImpl.java:82)
    at org.hibernate.service.jdbc.dialect.internal.DialectFactoryImpl.buildDialect(DialectFactoryImpl.java:64)
    at org.hibernate.engine.jdbc.internal.JdbcServicesImpl.configure(JdbcServicesImpl.java:146)
    at org.hibernate.service.internal.StandardServiceRegistryImpl.configureService(StandardServiceRegistryImpl.java:75)
    at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:159)
    at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:131)
    at org.hibernate.cfg.SettingsFactory.buildSettings(SettingsFactory.java:73)
    at org.hibernate.cfg.Configuration.buildSettingsInternal(Configuration.java:2279)
    at org.hibernate.cfg.Configuration.buildSettings(Configuration.java:2275)
    at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1744)
    at org.hibernate.ejb.EntityManagerFactoryImpl.<init>(EntityManagerFactoryImpl.java:94)
    at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:905)
    ... 9 more
Caused by: java.lang.ClassCastException: org.hibernate.dialect.Oracle12cDialect cannot be cast to org.hibernate.dialect.Dialect
    at org.hibernate.service.jdbc.dialect.internal.DialectFactoryImpl.constructDialect(DialectFactoryImpl.java:73)
    ... 20 more

考虑到 class org.hibernate.dialect.Oracle12cDialectorg.hibernate.dialect.Dialect 的子class(我检查了源代码),在我看来我有一些混合版本问题在这里。转换发生在 org.hibernate.service.jdbc.dialect.internal.DialectFactoryImpl.constructDialect() 方法中,但我不知道哪个 jar 部署了 class 以更正它的版本。

在你的 conf 中看起来一切都很好。Oracle12cDialect、Dialect 和 DialectFactoryImpl 来自同一个 hibernate-core.jar 所以应该没有这样的问题。

但请检查您的类路径中是否存在多个版本的 hibernate-core。

您可以 运行 maven-duplicate-finder-plugin maven 插件来检查 org.hibernate.dialect.Dialect 是否出现两次:

mvn com.ning.maven.plugins:maven-duplicate-finder-plugin:1.0.9:check

org.hibernate.dialect.Oracle12cDialect cannot be cast to org.hibernate.dialect.Dialect

这是废话,因为Oracle12cDialect 实现了Dialect。但是...

问题

……还有希望!我们在一个层次结构中至少有三个 classloader。

               [classloader-base]                            (without hibernate)
                 |            |
 [classloader modul1]     [classloader modul2]               (with hibernate)

Whearat classloader modul1 和 modul2 加载 hibernate-jars(当然是同一个版本),classloader-base dont!

modul1 和 modul2 有自己的方言界面副本,因此它们不兼容。

如果现在,modul1 在 class 中有一个像这样的字段:

       (modul 1)
public Dialect foobarDialect;

但是模块 2 创建的实例是这样的:

       (modul 2)
setFoobarDialect(new Oracle12cDialect());

因为 modul1 有自己的方言版本,加载时与 modul2 的方言版本不同,因此不兼容。

问题很简单-shell

让我们做一个小实验:

public static void main(String[] args) throws Exception {
    File maven = new File("C:/Users/Guest/.m2/repository/");
    String hbm = "org/hibernate/hibernate-core/5.2.1.Final/hibernate-core-5.2.1.Final.jar";
    String jpa = "org/hibernate/javax/persistence/hibernate-jpa-2.0-api/1.0.1.Final/hibernate-jpa-2.0-api-1.0.1.Final.jar";
    String logging = "org/jboss/logging/jboss-logging/3.1.3.GA/jboss-logging-3.1.3.GA.jar";
    String jta = "javax/transaction/jta/1.1/jta-1.1.jar";
    URL[] urls = new URL[] {
        new File(maven, hbm).toURL(), 
        new File(maven, jpa).toURL(),
        new File(maven, logging).toURL(), 
        new File(maven, jta).toURL()
    };
    ClassLoader module1 = new URLClassLoader(urls);
    ClassLoader module2 = new URLClassLoader(urls);
    Class dialect = module1.loadClass("org.hibernate.dialect.Oracle12cDialect");
    Class interfac = module2.loadClass("org.hibernate.dialect.Dialect");
    System.out.println(interfac.toString());
    System.out.println(dialect.toString());
    System.out.println(interfac.isAssignableFrom(dialect));
}

将打印:

class org.hibernate.dialect.Dialect
class org.hibernate.dialect.Oracle12cDialect
false          <- ah, its incompatible.

如何解决?

您可以通过从与第一个模块相同的 classloader 加载接口来解决 nut-shell。

Class interfac = module1.loadClass("org.hibernate.dialect.Dialect");
      ...
class org.hibernate.dialect.Dialect
class org.hibernate.dialect.Oracle12cDialect
true                 <- solved

解决方案

但是JBoss怎么解决呢?只要您不为我们提供对基础架构的完整访问权限,就没有干净的解决方案。这可能会给您带来一些法律问题,请不要提供对项目基础设施的访问。请改用解决方法,直到您可以迁移到 Wildfly。

解决方法

但是有一个简单的解决方法,将hibernate相关的jar复制到JBossjboss/lib/endorsed.

的endorsed/ext文件夹中

这将为整个 jboss 加载一个休眠模式,任何其他模块都不会覆盖此基本加载。

经过进一步挖掘,我在 WildFly documentation 的页面中发现了一些亮点。

因此,我更改了 persistence.xml 文件中的一行 Hibernate 属性,替换为:

<property name="jboss.as.jpa.providerModule" value="org.hibernate:5.1.10.Final"/>

来自

<property name="org.hibernate.jpa.HibernatePersistenceProvider" value="org.hibernate:5.1.10.Final"/>

这使得 JBoss 加载没有任何错误,并且持久性单元已正确构建。