Archive for the ‘emf’ tag
Hibernate Dynamic “import.sql”
Hello,
I felt the need to generate a dynamic import sql for a project that was based on hibernate.
The task proved to be a bit more advanced then a trivial thing. So here I share the way I hacked it to convince hibernate to use an import script generated on the fly.
Requirement, the project I am currently working on is a “service provision application” which designates the application instances by the domain requests are destined to. The application therefore requires to fill the db in with some generated data as it kicks off for the first time. The solution is also handy any scenario that needs to alter import.sql based on some conditions.
First thing is to create a manager class to undertake the task. In my instance it was a spring based application therefore I needed to extend LocalSessionFactoryBean provided by spring. If you need to create a standalone / adhoc hibernate container you may embed the necessary code to your session factory class or so called HibernateUtil class.
Since we will be using our own SchemaExport, we must block hibernate.hbm2ddl.auto somehow in the properties file and set it to none. In my case since I was using spring, I just put hardcoded hibernate.hbm2ddl.auto = none in my applicationContext.xml file.
Then comes the generation of the sql on the fly. My sql was not a huge file therefore I chose to regenerate it with a FileStream accessed via BufferedReader and appended the altered SQL lines to my StringBuffer.
The next thing is to create a SchemaExport and set the importSQL. Here hibernate implementation wierdly only allows classpath relative import.sql references and removes the leading “/” which makes it impossible to refer a generated file residing somewhere in the file system (err, in the tmp path of the application server, runtime or OS, to be a good boy). What we do here is to replace the thread’s classloader with a fake classloader. We feed in a fake name for the import script and as we see the fake name back in the classloader requested as a stream via getResourceAsStream, we just return an input stream that reads whatever (or wherever) we prepared the sql.
By issuing a create request to SchemExport, we are done. And we cleanup by restoring the original classloader of the thread we are executing on.
Below is the code that I used. By modifying as required, it should be good to use in stand alone / ad hoc / Teneo driven, etc environments. By the way, for those that are not aware of Eclipse EMF and Eclipse Teneo, with the new sister project Texo, I highly recommend you get your hands on EMF and Teneo, I am sure you will love it.
package com.example.session;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.hibernate.HibernateException;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.classic.Session;
import org.hibernate.tool.hbm2ddl.SchemaExport;
import org.hibernate.util.ConfigHelper;
import org.springframework.orm.hibernate3.LocalSessionFactoryBean;
public class ExampleSessionFactory extends LocalSessionFactoryBean {
private static final String FAKE_SCRIPT = "__auto_generated_import.sql__"; //$NON-NLS-1$
private StringBuffer sql;
/*
* (non-Javadoc)
*
* @see org.springframework.orm.hibernate3.LocalSessionFactoryBean#newSessionFactory(org.hibernate.cfg.Configuration)
*/
@Override
protected SessionFactory newSessionFactory(Configuration config)
throws HibernateException {
final SessionFactory sessionFactory = config.buildSessionFactory();
final String httpPort = CommonUtils.getHTTPPort();
if ( this.schemaInitRequired(sessionFactory) ) {
this.prepareSchema(config, httpPort);
}
return sessionFactory;
}
private void prepareSchema(Configuration config, final String httpPort) {
try {
final InputStream is = ConfigHelper.getResourceAsStream("/import.sql"); //$NON-NLS-1$
final InputStreamReader importFileReader = new InputStreamReader(is);
this.sql = new StringBuffer();
this.logger.info("Preparing import script..."); //$NON-NLS-1$
final BufferedReader reader = new BufferedReader(importFileReader);
for ( String raw = reader.readLine(); raw != null; raw = reader.readLine() ) {
final String replaced1 = raw.replaceAll("%host%", InetAddress.getLocalHost().getHostName()); //$NON-NLS-1$
final String replaced2 = replaced1.replaceAll("%port%", httpPort); //$NON-NLS-1$
this.sql.append(replaced2);
this.sql.append("\n"); //$NON-NLS-1$
}
final ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
this.swapClassLoader(oldClassLoader);
try {
final SchemaExport export = new SchemaExport(config);
export.setImportFile(ExampleSessionFactory.FAKE_SCRIPT);
export.create(false, true);
}
finally {
Thread.currentThread().setContextClassLoader(oldClassLoader);
}
}
catch ( final Exception e ) {
this.logger.fatal("Error while initializing database", e);
}
}
private boolean schemaInitRequired(SessionFactory factory) {
final Session session = factory.openSession();
try {
final Connection connection = session.connection();
try {
final Statement statement = connection.createStatement();
try {
final ResultSet resultSet = statement.executeQuery("SELECT * from APPLICATION");
try {
return ( !resultSet.next() );
}
finally {
resultSet.close();
}
}
finally {
statement.close();
}
}
catch ( final SQLException e ) {
return true;
}
}
finally {
session.close();
}
}
/**
* Swapping with a *fake* class loader to trick hibernate
*
* @param oldClassLoader
*/
private void swapClassLoader(ClassLoader oldClassLoader) {
Thread.currentThread().setContextClassLoader(new ClassLoader() {
@Override
public InputStream getResourceAsStream(String name) {
if ( ExampleSessionFactory.FAKE_SCRIPT.equals(name) ) {
return new ByteArrayInputStream(ExampleSessionFactory.this.sql.toString().getBytes());
}
return super.getResourceAsStream(name);
}
});
}
}
Enjoy your dynamic import.sql…
Popularity: 79%
EMF Build Manager 0.1-Beta Released
The EMF Builder 0.1-beta has been released and I wish everyone to install and give it a test drive.
Update Site:
http://emf-ebm.svn.sourceforge.net/svnroot/emf-ebm/update/
Project Proposal:
http://ebm.batoo.org
Upon installation, right-clicking on the project and Configure -> Add EMF Nature will enable the functionality. In case you experience problems with it you can turn off the nature the same way you enable it.
It should not be dangerous for your source as it uses the EMF and GMF code generation framework. The worst case scenario it will not trigger builds.
Here are the highlights:
* It supports full list of EMF resources by file extension
* It supports transitive dependencies upwards and downwards
- Downwards, if there is an error on a resource, the resources dependent
on this one will be suspended
- Upwards, resources watch for changes in the dependencies and rebuild
when necessary
* It detects the underlying EMF version and regenerates on a change
detection
* Dependency state is preserved across eclipse sessions
* Takes advantage of the Jobs Framework as much as possible with detailed
progress reporting through progress view.
* It provides three extension points
- An extension point to register validators, built in EMF Validator is
registered with this extension
- An extension point to register generators, built in EMF Generator is
registered with this extension. And also a complementary GMF generator uses
this extension points
- An extension point to register content types with the infrastructure,
GMF Gen content type is registered using this extension point
* Full error / warning reporting via “Problems View” marker support
* Quick fix support
* Trace support (For bug reports I would appreciate trace turned on)
Popularity: 6%