Reputation: 830
I am trying to load entities from several jar files. What I managed to do is
configure 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);
}
so the entities should be loaded from moduleFiles array. In logs I can see:
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
The entity in external 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.
}
persistence.xml in external jar (META-INF directory)
<persistence-unit name="ProgramInfoPersistenceUnit">
<class>com.antara.modules.programinfo.db.model.PrograminfoDataEntity</class>
</persistence-unit>
Query with above entity usage
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);
which threw an exception:
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)
The question is why hibernate didn't loaded the annotated entity from jar? The exception is thrown not only by named query, but any other operation with entity. There are no errors before usage of this entity. Local entities are loaded properly.
EDIT:
After some changes I managed to recognize entity by 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 ....
But when I try to use the entity I still get exception:
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
The change was: passing Configuration to each "module" that is inside jar and add Annotated Class (by module I mean SPI service with method invoked at startup)
@Override
public void configureDB(Configuration configuration) {
configuration.addAnnotatedClass(PrograminfoDataEntity.class);
}
Upvotes: 5
Views: 6221
Reputation: 11691
You are setting up a Hibernate bootstrap process. Here are the docs: https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#bootstrap
There are essentially two routes to go down here. You are doing it the Hibernate-specific way. I prefer the JPA one usually because it's the most automatic and simplistic one: you put a persistence.xml file into a jar and that jar will get scanned for classes with the appropriate Entity annotation. JPA will then inject the entity manager (and factory), which you can then in turn transform into the native Hibernate session using:
Session s = (Session) em.getDelegate();
That in turn, should give you access to non-JPA capabilities if need be.
Upvotes: 0
Reputation: 830
After 3 days of trials I have found the solution: Hibernate loades classes using reflection mechanism by ContextClassLoader
Thread.currentThread().getContextClassLoader();
so I set ContextClassLoader to ClassLoader of PrograminfoDataEntity
Thread.currentThread().setContextClassLoader(PrograminfoDataEntity.class.getClassLoader());
and it solved all NoClassDefFound, ClassCastException and similar errors
Upvotes: 5
Reputation: 41
According to javadoc and implementation code Hibernate only read *.hbm.xml at Configuration.addJar method
I guess Hibernate doesn't auto scan jars because of restrictions in JPA Specification
I've done auto scan of jars in hibernate extending the scanner and adding the jars on it. But you should use JPA api to that. Something like:
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();
And the scanner something like:
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);
}
}
}
Upvotes: 2