How to load Hibernate entities from external JAR - java

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);
}

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

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);
}
}
}

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.

Related

Getting name from hibernate for class

I have a project with supporting XML-config. Now we wanna add hibernate config for entities via annotation. Seems it possible and works (correct me if I am mistaken). The issue is that our system
private String entityName() {
String name = null;
try {
String longName = getDaoFactory().getSessionFactory().getClassMetadata(entityClass).getEntityName();
name = longName.substring(longName.lastIndexOf(".") + 1);
} catch (Exception ex) {
log.error("Exception getting name from hibernate for class: " + entityClass);
name = null;
}
return name;
}
This method works well for entities with xml-config, but how to get a name for an entity which is configurated by annotation?
Thank you in advance for any recommendation and suggestion :)
UPD:
from init session factory method:
Configuration configuration = new Configuration();
configuration.addResource("resources/administrator/queries.hbm.xml");
StandardServiceRegistryBuilder builder = new StandardServiceRegistryBuilder().applySettings(configuration
.getProperties());
SessionFactory sessionFactory = configuration.buildSessionFactory(builder.build());
setSessionFactory(sessionFactory);
return configuration;
I created a session Factory like this.
public static SessionFactory buildSessionFactoryFromAnnotatedClasses(){
StandardServiceRegistry standardRegistry =
new StandardServiceRegistryBuilder().applySettings(// pass org.hibernate.cfg.Configuration object here)
.getProperties()).build(); // Configuration object has properties
MetadataSources sources = new MetadataSources(standardRegistry);
sources.addAnnotatedClass(MyClass.class);// MyClass is your java annotated class
Metadata metaData = sources.getMetadataBuilder().build();
return metaData.getSessionFactoryBuilder().build();
}

How to configure hibernate config file to have multiple instances from same database?

I have multiple instances on db2. How can I configure hibernate config file so that I can use all of them?
You can specify it by schema element while defining table for your entity.
#Table(name="TABLE_NAME", schema="SCHEMA_NAME")
Else, you can use separate EntityManager pointing to respective schema & then use the same entity, as their structure is similar.
You can have separate configuration files for each schema & then build SessionFactory from it, below is some pseudo-code for it.
SessionFactory sf_1 = new Configuration().configure("schema1config.cfg.xml").buildSessionFactory();
SessionFactory sf_2 = new Configuration().configure("schema2config.cfg.xml").buildSessionFactory();
session_1 = sf_1.openSession(); //-- Similarly for other
You can refer this link for further details to map multiple schema, but it isn't hibernate specific.
Credit goes to #Nayan Wadekar
Resource Link:
How to connect to multiple databases in Hibernate
Found solution over link as per my requirement:
Setting properties programmatically in Hibernate
public class HibernateUtil {
private static final SessionFactory sessionFactory;
static {
try {
// Create the SessionFactory from standard (hibernate.cfg.xml)
// config file.
Properties c = new Properties();
c.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQLDialect");
c.setProperty("hibernate.connection.driver_class", "com.mysql.jdbc.Driver");
c.setProperty("hibernate.connection.url", "jdbc:mysql://localhost:3306/mydiscou_billing?zeroDateTimeBehavior=convertToNull");
c.setProperty("hibernate.connection.username", "root");
c.setProperty("hibernate.connection.password", "123");
c.setProperty("hibernate.connection.autoReconnect", "true");
c.setProperty("connection.provider_class", "org.hibernate.connection.C3P0ConnectionProvider");
c.setProperty("c3p0.min_size", "5");
c.setProperty("c3p0.max_size", "20");
c.setProperty("c3p0.timeout", "1800");
c.setProperty("c3p0.max_statements", "100");
c.setProperty("hibernate.c3p0.testConnectionOnCheckout", "true");
sessionFactory = new AnnotationConfiguration().setProperties(c).configure().buildSessionFactory();
} catch (Throwable ex) {
// Log the exception.
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
}

Hibernate full text search using hibernate-search-4.1.1.Final.jar but having error like query builder can not resolved

I have facing error at query builder can not be typed and org.apache.lucene not found.
I will added all jar related to spring-mvc and hibernate
#Repository
public class SearchDAO {
#Autowired
SessionFactory sessionFactory;
#Transactional
public List<SearchVO> viewSeachResult(String searchText) throws Exception
{
try
{
Session s= this.sessionFactory.openSession();
//Session s= HibernateUtil.getSession();
FullTextSession text = Search.getFullTextSession(s);
// create native Lucene query unsing the query DSL
// alternatively you can write the Lucene query using the Lucenquery
// parser
// or the Lucene programmatic API. The Hibernate Search DSL is
// recommended though
QueryBuilder qb = text.getSearchFactory().buildQueryBuilder().forEntity(SearchVO.class).get();
org.apache.lucene.search.Query query= qb.keyWord().onFields("Description","Title","Author","Area").matching(searchText).createQuery();
org.hibernate.Query hibQuery = fullTextSession.createFullTextQuery(query,SearchVO.class);
List<SearchVO> result= hibQuery.list();
return result;
}catch(Exception e)
{
throw e;
}
}
}
I think You need hibernate-search jar.
The jars that required are mentioned here.
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-search</artifactId>
<version>5.3.0.Final</version>
</dependency>
You can manually download from here.
Hope this helps.

How to use SchemaExportTool with JPA and Hibernate 4.3

At Hibernate 4.3 Ejb3Configuration class was removed. This class was commonly used for creating hibernate configuration file from a persistence unit (persistence.xml file) to SchemaExport tool.
As a simple alternative to export schema to .sql file I'm using the following code:
public static void export(String persistenceUnit, String exportFileName) {
Map<String, String> hash = new HashMap<String, String>();
hash.put("hibernate.hbm2ddl.auto", "create-drop");
EntityManagerFactory factory = Persistence.createEntityManagerFactory(
persistenceUnit, hash);
org.hibernate.jpa.internal.EntityManagerFactoryImpl hibFactory = (org.hibernate.jpa.internal.EntityManagerFactoryImpl) factory;
SessionFactoryImpl hibSessionFactory = hibFactory.getSessionFactory();
SchemaExport schema = ReflectUtils.getPrivateFieldValue(
hibSessionFactory, "schemaExport");
schema.setOutputFile(exportFileName);
schema.setFormat(false);
schema.setDelimiter(";");
schema.drop(true, false);
schema.create(true, false);
}
At this piece of code, i'm basically using schemaexport object created by HibernateSessionFactoryImpl. The drawback is that every time it´s executed the database schema is recreated. Is there any other simple way to use SchemaExporTool with Hibernate 4.3 and JPA? It seems that the real question is how to create Hibernate Configuration Object from a persistenceunit?
I ran into the same Problem. I ended up by using the internal PersistenceXmlParser of Hibernate to access information in the persistence.xml file and creating the Configuration object manually:
public static void main(String[] args) {
PersistenceXmlParser parser = new PersistenceXmlParser(new ClassLoaderServiceImpl(), PersistenceUnitTransactionType.RESOURCE_LOCAL);
List<ParsedPersistenceXmlDescriptor> allDescriptors = parser.doResolve(new HashMap<>());
for (ParsedPersistenceXmlDescriptor descriptor : allDescriptors) {
Configuration cfg = new Configuration();
cfg.setProperty("hibernate.hbm2ddl.auto", "create");
cfg.setProperty("hibernate.dialect", "org.hibernate.dialect.Oracle10gDialect");
cfg.setProperty("hibernate.id.new_generator_mappings", "true");
List<String> managedClassNames = descriptor.getManagedClassNames();
for (String className : managedClassNames) {
try {
cfg.addAnnotatedClass(Class.forName(className));
} catch (ClassNotFoundException e) {
System.out.println("Class not found: " + className);
}
}
SchemaExport export = new SchemaExport(cfg);
export.setDelimiter(";");
export.setOutputFile("C:\\dev\\" + descriptor.getName() + "_create_schema.sql");
export.setFormat(true);
export.execute(true, false, false, false);
}
}

How to load hibernate.cfg.xml from different location

I am creating a jar using hibernate. I have encountered a situation where I need to change a setting (url) often, so I would like to load the hibernate.cfg.xml like this
SessionFactory sessionFactory = new Configuration()
.configure("D:\\fax\\hibernate.cfg.xml")
.buildSessionFactory();
But then running the project I am getting this exception
org.hibernate.HibernateException: D:\fax\hibernate.cfg.xml not found
at org.hibernate.util.ConfigHelper.getResourceAsStream(ConfigHelper.java:147)
at org.hibernate.cfg.Configuration.getConfigurationInputStream(Configuration.java:1287)
at org.hibernate.cfg.Configuration.configure(Configuration.java:1309)
at hibernate.LabOrderHelper.getDatabaseSesssion(LabOrderHelper.java:55)
at hibernate.Test.main(Test.java:42)
How can I load hibernate.cfg.xml from a different location than the class path?
There is a method public Configuration configure(File configFile) in class Configuration
Try the following, it should work for sure :)
File f = new File("D:\\fax\\hibernate.cfg.xml");
SessionFactory sessionFactory = new Configuration().configure(f).buildSessionFactory();
The difference is you have used a method configure(String resource) which is expecting a resource in a classpath, but where as configure(File configFile) is expecting a File, so you can pass it.
I need to change the sql setting(url) often
I had the same requirement. For switching just the DB connection properties the approach suggested in the accepted answer, though it works, is a bit of a blunt instrument.
Loading a completely different configuration file just to change a few connection properties? Now all the other properties that are common in both are duplicated, and every time you make a change you need to make it in two places.
A better way is to put all the common properties that don't need to change between environments in the default hibernate.cfg.xml, build your Configuration from that as usual, and use the .addProperties() method to add the properties that are environment-specific on top, in this case the connection url. You can load these extra properties from anywhere you like.
public SessionFactory buildSessionFactory() {
return getConfiguration().buildSessionFactory();
}
private Configuration getConfiguration() {
Configuration config = new Configuration.configure(); // load the base config from the default hibernate.cfg.xml
return config.addProperties(getConnectionProperties()); // add your custom connection props for this environment on top
}
private Properties getConnectionProperties() {
Properties connectionProps = new Properties();
connectionProps.put("hibernate.connection.url", getConnectionUrl());
// possibly add other props like hibernate.connection.username, hibernate.connection.password
return connectionProps;
}
private String getConnectionUrl() {
// get your connection URL from wherever you like
}
Hibernate XML configuration file “hibernate.cfg.xml” is always put at the root of your project classpath, outside of any package. If you place this configuration file into a different directory, you may encounter the following error :
Initial SessionFactory creation failed.org.hibernate.HibernateException:
/hibernate.cfg.xml not found
To ask Hibernate look for your “hibernate.cfg.xml” file in other directory, you can modify the default Hibernate’s SessionFactory class by passing your “hibernate.cfg.xml” file path as an argument into the configure() method:
SessionFactory sessionFactory = new Configuration()
.configure("/com/example/persistence/hibernate.cfg.xml")
.buildSessionFactory();
return sessionFactory;
Full Example in HibernateUtil.java, to load “hibernate.cfg.xml” from directory “/com/example/persistence/“.
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class HibernateUtil {
private static final SessionFactory sessionFactory = buildSessionFactory();
private static SessionFactory buildSessionFactory() {
try {
// load from different directory
SessionFactory sessionFactory = new Configuration().configure(
"/com/example/persistence/hibernate.cfg.xml")
.buildSessionFactory();
return sessionFactory;
} catch (Throwable ex) {
// Make sure you log the exception, as it might be swallowed
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
public static void shutdown() {
// Close caches and connection pools
getSessionFactory().close();
}
}
supplementing the accepted answer,
You can load hibernate.cfg.xml from a different directory (not necessarily the classpath) using the configure(File configFile) method that takes the hibernateConfig File argument.
(note, am using hibernate 4.3.7)
The advantage is, if you have no access to the war file but can get access to the hibernate file in a different directory, say for maintenance.
Like this:
String hibernatePropsFilePath = "/etc/configs/hibernate.cfg.xml";
File hibernatePropsFile = new File(hibernatePropsFilePath);
Configuration configuration = new Configuration();
configuration.configure(hibernatePropsFile);
StandardServiceRegistryBuilder serviceRegistryBuilder = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties());
ServiceRegistry serviceRegistry = serviceRegistryBuilder.build();
SessionFactory sessionFactory = configuration.buildSessionFactory(serviceRegistry);

Categories

Resources