如何从外部 JAR 加载 Hibernate 实体

ANT*_*ARA 5 java hibernate

我正在尝试从几个 jar 文件加载实体。我设法做的是

  1. 配置休眠

    private void configure(File[] moduleFiles)
    {
    Configuration configuration = new Configuration()
        .setProperty("hibernate.connection.url", getConnectionString())
        .setProperty("hibernate.connection.username", "user")
        .setProperty("hibernate.connection.password", "pass")
        .setProperty("hibernate.connection.driver_class", "org.hsqldb.jdbc.JDBCDriver")
        .setProperty("hibernate.dialect", "org.hibernate.dialect.HSQLDialect")
        .setProperty("hibernate.archive.autodetection", "class,hbm")
        .setProperty("exclude-unlisted-classes", "false")
        .setProperty("hibernate.hbm2ddl.auto", "update");
    
    if (moduleFiles != null) {
        for (File f : moduleFiles) {
            configuration.addJar(f);
        }
    }
    
    ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
    this.sessionFactory = configuration.buildSessionFactory(serviceRegistry);
    }
    
    Run Code Online (Sandbox Code Playgroud)

所以实体应该从 moduleFiles 数组加载。在日志中我可以看到:

    2015-08-25 20:52:12 INFO  Configuration:837 - HHH000235: Searching for mapping documents in jar: ProgramInfo.jar
    2015-08-25 20:52:12 INFO  Configuration:837 - HHH000235: Searching for mapping documents in jar: SampleModule.jar
Run Code Online (Sandbox Code Playgroud)
  1. 外部 jar 中的实体

    @Entity
    @Table(name = "PROGRAMINFO_DATA", schema = "PUBLIC", catalog = "PUBLIC")
    @NamedQueries({@NamedQuery(name = "PrograminfoDataEntity.findByWindowInfo", query = "FROM PrograminfoDataEntity WHERE PROCESSPATH = :pp AND WINDOWTITLE = :wt AND DAY = :d")})
    public class PrograminfoDataEntity implements SVEntity {
        private long id;
        private Date day;
        private String processname;
        private String processpath;
        private String programname;
        private String windowtitle;
    
        // getters setters etc.
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 外部jar中的persistence.xml(META-INF目录)

    <persistence-unit name="ProgramInfoPersistenceUnit">
        <class>com.antara.modules.programinfo.db.model.PrograminfoDataEntity</class>
    </persistence-unit>
    
    Run Code Online (Sandbox Code Playgroud)

  3. 查询上述实体用法

        Session session = openSession();
        Query q = session.getNamedQuery("PrograminfoDataEntity.findByWindowInfo");
        q.setParameter("pp", windowInfo.getProcessPath());
        q.setParameter("wt", windowInfo.getWindowTitle());
        q.setDate("d", date);
    
        PrograminfoDataEntity result = (PrograminfoDataEntity) q.uniqueResult();
        closeSession(session);
    
    Run Code Online (Sandbox Code Playgroud)

抛出异常:

org.hibernate.MappingException: Named query not known: PrograminfoDataEntity.findByWindowInfo
    at org.hibernate.internal.AbstractSessionImpl.getNamedQuery(AbstractSessionImpl.java:177)
    at org.hibernate.internal.SessionImpl.getNamedQuery(SessionImpl.java:1372)
    at com.antara.modules.programinfo.db.dao.PrograminfoDao.findByWindowInfo(PrograminfoDao.java:26)
    at com.antara.modules.programinfo.ProgramInfoImpl.run(ProgramInfoImpl.java:84)
Run Code Online (Sandbox Code Playgroud)

问题是为什么 hibernate 没有从 jar 加载带注释的实体?异常不仅由命名查询引发,而且由实体的任何其他操作引发。在使用这个实体之前没有错误。本地实体已正确加载。

编辑:

经过一些更改后,我设法通过 Hibernate 识别实体

DEBUG AnnotationBinder:601 - Binding entity from annotated class: com.antara.modules.programinfo.db.model.PrograminfoDataEntity
DEBUG QueryBinder:93 - Binding named query: PrograminfoDataEntity.findByWindowInfo => FROM PrograminfoDataEntity ....
Run Code Online (Sandbox Code Playgroud)

但是当我尝试使用实体时,我仍然遇到异常:

ERROR AssertionFailure:61 - HHH000099: an assertion failure occured (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session): java.lang.ClassNotFoundException: com.antara.modules.programinfo.db.model.PrograminfoDataEntity
ERROR Main:114 - PersistentClass name cannot be converted into a Class
...
Caused by: java.lang.ClassNotFoundException: com.antara.modules.programinfo.db.model.PrograminfoDataEntity
Run Code Online (Sandbox Code Playgroud)

更改是:将配置传递给 jar 内的每个“模块”并添加带注释的类(模块是指在启动时调用方法的 SPI 服务)

@Override
public void configureDB(Configuration configuration) {
    configuration.addAnnotatedClass(PrograminfoDataEntity.class);
}
Run Code Online (Sandbox Code Playgroud)

ANT*_*ARA 5

经过 3 天的试验,我找到了解决方案:Hibernate 通过 ContextClassLoader 使用反射机制加载类

Thread.currentThread().getContextClassLoader();
Run Code Online (Sandbox Code Playgroud)

所以我将 ContextClassLoader 设置为 PrograminfoDataEntity 的 ClassLoader

Thread.currentThread().setContextClassLoader(PrograminfoDataEntity.class.getClassLoader());
Run Code Online (Sandbox Code Playgroud)

它解决了所有 NoClassDefFound、ClassCastException 和类似的错误