如何从外部JAR

时间:2015-08-25 22:10:15

标签: java hibernate

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

  1. 配置hibernate

    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);
    }
    
  2. 所以实体应该从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
    
    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.
      }
      
    2. 外部jar中的
    3. persistence.xml(META-INF目录)

      <persistence-unit name="ProgramInfoPersistenceUnit">
          <class>com.antara.modules.programinfo.db.model.PrograminfoDataEntity</class>
      </persistence-unit>
      

    4. 使用上述实体用法查询

          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);
      
    5. 引发了例外:

      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)
      

      问题是为什么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 ....
      

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

      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
      

      改变是:将配置传递给每个&#34;模块&#34;这是在jar内部并添加Annotated Class(通过模块我的意思是SPI服务与启动时调用的方法)

      @Override
      public void configureDB(Configuration configuration) {
          configuration.addAnnotatedClass(PrograminfoDataEntity.class);
      }
      

3 个答案:

答案 0 :(得分:3)

经过3天的试验,我找到了解决办法:Hibernate使用ContextClassLoader的反射机制来破坏类

Thread.currentThread().getContextClassLoader();

所以我将ContextClassLoader设置为PrograminfoDataEntity的ClassLoader

Thread.currentThread().setContextClassLoader(PrograminfoDataEntity.class.getClassLoader());

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

答案 1 :(得分:2)

根据javadoc and implementation code Hibernate只在Configuration.addJar方法中读取* .hbm.xml

我认为由于JPA规范中的限制,Hibernate不会自动扫描jar文件

我已经在hibernate中对jar进行了自动扫描,扩展了扫描仪并在其上添加了jar。但你应该使用JPA api。类似的东西:

Map<String, Object> map = new HashMap<>();
map.put("hibernate.connection.url", getConnectionString());
map.put("hibernate.connection.username", "user");
map.put("hibernate.connection.password", "pass");
map.put("hibernate.connection.driver_class", "org.hsqldb.jdbc.JDBCDriver");
map.put("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");
map.put("hibernate.archive.autodetection", "class,hbm");
map.put("exclude-unlisted-classes", "false");
map.put("hibernate.hbm2ddl.auto", "update");
//Property to change scanner
map.put("hibernate.ejb.resource_scanner", "me.janario.MyScanner");

EntityManagerFactory emf = Persistence.createEntityManagerFactory("ProgramInfoPersistenceUnit", map);
SessionFactory sessionFactory = ((HibernateEntityManagerFactory) emf).getSessionFactory();

扫描仪就像:

public class MyScanner extends StandardScanner {
    @Override
    public ScanResult scan(PersistenceUnitDescriptor persistenceUnit, ScanOptions scanOptions) {
        try {
            persistenceUnit.getJarFileUrls()
                    .add(new URL("file:/path/my.jar"));
            return super.scan(persistenceUnit, scanOptions);
        } catch (MalformedURLException e) {
            throw new IllegalStateException(e);
        }
    }
}

答案 2 :(得分:0)

您正在设置 Hibernate 引导程序。以下是文档:https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#bootstrap

这里基本上有两条路线可以走。您正在以特定于 Hibernate 的方式执行此操作。我通常更喜欢 JPA,因为它是最自动和最简单的一个:您将一个 persistence.xml 文件放入一个 jar 中,该 jar 将使用适当的实体注释扫描类。然后 JPA 将注入实体管理器(和工厂),然后您可以使用以下方法将其转换为本机 Hibernate 会话:

Session s = (Session) em.getDelegate();

反过来,如果需要,您应该可以访问非 JPA 功能。