Wednesday, October 28, 2009

How To Generate DDL Scripts from Hibernate

One of the nice things about using Hibernate in your persistence layer is that it can automatically make updates to your database schema for you. This is nice in development, but oftentimes you need to have the ddl script file. Lucky for us, Hibernate ships with the SchemaExport class. This is what hibernate uses to make updates to your database. I'll show you how we can hijack it and use it for our own purposes as well.

Here we go. As it seems a lot of developers are migrating towards Hibernate annotations and EJB JPA, the following code assumes your classes are configured with annotations. This simple class takes the name of the package where you have your domain objects stored and generates ddl for mysql, oracle and hsql.

package com.jandrewthompson;

import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.tool.hbm2ddl.SchemaExport;

/**
* @author john.thompson
*
*/
public class SchemaGenerator
{
private AnnotationConfiguration cfg;

public SchemaGenerator(String packageName) throws Exception
{
cfg = new AnnotationConfiguration();
cfg.setProperty("hibernate.hbm2ddl.auto","create");

for(Class<Object> clazz : getClasses(packageName))
{
cfg.addAnnotatedClass(clazz);
}
}

/**
* Method that actually creates the file.
* @param dbDialect to use
*/
private void generate(Dialect dialect)
{
cfg.setProperty("hibernate.dialect", dialect.getDialectClass());

SchemaExport export = new SchemaExport(cfg);
export.setDelimiter(";");
export.setOutputFile("ddl_" + dialect.name().toLowerCase() + ".sql");
export.execute(true, false, false, false);
}

/**
* @param args
*/
public static void main(String[] args) throws Exception
{
SchemaGenerator gen = new SchemaGenerator("org.jthompson.myapp.domain");
gen.generate(Dialect.MYSQL);
gen.generate(Dialect.ORACLE);
gen.generate(Dialect.HSQL);
}

/**
* Utility method used to fetch Class list based on a package name.
* @param packageName (should be the package containing your annotated beans.
*/
private List getClasses(String packageName) throws Exception
{
List classes = new ArrayList();
File directory = null;
try
{
ClassLoader cld = Thread.currentThread().getContextClassLoader();
if (cld == null) {
throw new ClassNotFoundException("Can't get class loader.");
}
String path = packageName.replace('.', '/');
URL resource = cld.getResource(path);
if (resource == null) {
throw new ClassNotFoundException("No resource for " + path);
}
directory = new File(resource.getFile());
} catch (NullPointerException x) {
throw new ClassNotFoundException(packageName + " (" + directory
+ ") does not appear to be a valid package");
}
if (directory.exists()) {
String[] files = directory.list();
for (int i = 0; i < files.length; i++) {
if (files[i].endsWith(".class")) {
// removes the .class extension
classes.add(Class.forName(packageName + '.'
+ files[i].substring(0, files[i].length() - 6)));
}
}
} else {
throw new ClassNotFoundException(packageName
+ " is not a valid package");
}

return classes;
}

/**
* Holds the classnames of hibernate dialects for easy reference.
*/
private static enum Dialect
{
ORACLE("org.hibernate.dialect.Oracle10gDialect"),
MYSQL("org.hibernate.dialect.MySQLDialect"),
HSQL("org.hibernate.dialect.HSQLDialect");

private String dialectClass;
private Dialect(String dialectClass)
{
this.dialectClass = dialectClass;
}
public String getDialectClass()
{
return dialectClass;
}
}
}


It should be pretty easy to modify this code to suit your needs. If you are using hibernate xml configuration, just swap out the AnnotationConfiguration class for org.hibernate.cfg.Configuration.

Thursday, October 22, 2009

Event Driven Programming with Java - Monsoon

I've recently been involved on an Adobe Flex project at work. One of the things that I've really come to enjoy about Flex is its powerful event framework. For instance, component A can say that it throws an OnSave event. Component B says it listens for OnSave events. Neither component has to know anything about each other, but when component A throws the event, B will automatically handle it.

If you've been around Java for any length of time, you may have done some AWT or Swing development. Here, we have a similar concept of events, but each listener has to register itself with whatever object might throw that event. In the end, you end up with a tightly coupled web of connections and dependencies that, if not managed correctly, can quickly become a nightmare to maintain.

Enter Monsoon. I know that there are other event frameworks available, but could not find any that did exactly what I wanted (granted I didn't search that hard). I also thought this would be an interesting challenge. The idea is that all of the event plumbing is handled automatically via Java 1.5 Annotations. An 'event' in this system can be any java object (POJO). Methods that dispatch and listen for events both have the @Event annotation. The return object from a dispatch method is used as the method parameter of the listening method. For Example:

@Event(name="saveUser", type=EventType.DISPATCHER)
public User save()
{
  return user;
}

@Event(name="saveUser", type=EventType.LISTENER)
public void onSave(User user)
{
  //do something with user
}


If you're using Spring, configuration is trivial. Just include an 'EventBeanPostProcessor' bean in your spring config.

<bean class="org.jthompson.monsoon.spring.EventBeanPostProcessor">
  <property name="managedBeanNames">
    <list>
      <value>dispatcher</value>
      <value>listener</value>
    </list>
  </property>
</bean>


Otherwise, you'll need to use the included ObjectFactory to generate your objects if you aren't using spring.


Dispatcher dispatcher = (Dispatcher)factory.generateProxy(new Dispatcher());


Monsoon will automatically discover the connections between dispatchers and listeners for you. If you register three object that listen for a particular event, they will all be triggered automatically.

This is still in an early phase of development and there is a lot of work yet to do. However, if you find it useful or interesing in the least please let me know!

You can get the latest version of Monsoon via maven here:
http://monsoon-events.googlecode.com/svn/trunk/repo

You can also browse the source code at my googlecode page:
http://code.google.com/p/monsoon-events/

Wednesday, October 21, 2009

Alyssa's Bed

I've recently started working on my latest project; a toddler bed for our little girl. I didn't really want to spend a ton of money on a plastic/toy like bed that would only last a short time. Plus, I thought it would be nice to have an heirloom piece that might get passed down through the family.



I usually start out a new design on graph paper the old fasioned way. You know, with a pencil, ruler, t-square and compass. I'll then transfer any curves and such that I may need to full-scale cardboard templates. This time I decided to use CAD software to help with the design. I discovered that I can print full-scale patterns on my printer in sections, then tape then together. Man, that was easy! I'll post the files here later if anyone is interested.

As of now, I've got my plans and my cut list complete. I also rough sketched out each part on the lumber I plan to use to make the most efficient use of the stock (maple). Pics coming soon...

alyssa's bed.pdf
alyssa's bed.dwg