views:

915

answers:

3

Hi,

I´m busy converting an existing project from an Ant build to one using Maven. Part of this build includes using the hibernate hbm2java tool to convert a collection of .hbm.xml files into Java. Here's a snippet of the Ant script used to do this:

<target name="dbcodegen" depends="cleangen" description="Generate Java source from Hibernate XML"> <hibernatetool destdir="${src.generated}"> <configuration>
<fileset dir="${src.config}"> <include name="**/*.hbm.xml"/> </fileset> </configuration>
<hbm2java jdk5="true"/> </hibernatetool>
</target>

I've had a look around on the internet and some people seem to do this (I think) using Ant within Maven and others with the Maven plugin. I'd prefer to avoid mixing Ant and Maven. Can anyone suggest a way to do this so that all of the .hbm.xml files are picked up and the code generation takes place as part of the Maven code generation build phase?

Thanks!

Adam.

+7  A: 

Well, there is the Maven Hibernate3 Plugin if you don't want to mix Ant and Maven (which is a good idea here IMO). It has a hbm2java goal which is bound by default to the generate-sources phase. Refer to the website of the Mojo for more details but the setup of the plugin might looks like something like this:

  <plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>hibernate3-maven-plugin</artifactId>
    <version>2.2</version>
    <executions>
      <execution>
        <phase>generate-sources</phase>
        <goals>
          <goal>hbm2java</goal>
        </goals>
      </execution>
    </executions>
    <configuration>
      <components>
        <component>
          <name>hbm2java</name>
          <implementation>configuration</implementation>
          <outputDirectory>target/generated-sources/hibernate3</outputDirectory>
        </component>
      </components>
      <componentProperties>
        <drop>true</drop>
        <jdk5>true</jdk5>
        <configurationfile>/src/main/resources/hibernate.cfg.xml</configurationfile>
      </componentProperties>
    </configuration>
  </plugin> 

EDIT: The plugin actually looks for .hbm.xml in target/classes to generate the java source files. So, if you put your mapping files in src/main/resources, they will get copied into target/classes during the process-resources phase which is invoked by the plugin and things will just work. I've just tested this with the following sample project:

maven-hibernate3-testcase
|-- pom.xml
`-- src
    |-- main
    |   |-- java
    |   `-- resources
    |       |-- Person.hbm.xml
    |       `-- hibernate.cfg.xml
    `-- test
        `-- java

The pom.xml is almost empty, it just contains the plugin configuration seen above and a junit dependency. The hibernate.cfg.xml contains:

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
    "-//Hibernate/Hibernate Configuration DTD//EN"
    "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"&gt;
<hibernate-configuration>
  <session-factory>
    <!-- Database connection settings -->
    <property name="connection.driver_class">org.apache.derby.jdbc.ClientDriver</property>
    <property name="connection.url">jdbc:derby://localhost:1527/mydatabase</property>
    <property name="connection.username">app</property>
    <property name="connection.password">app</property>

    <!-- JDBC connection pool (use the built-in) -->
    <property name="connection.pool_size">1</property>

    <!-- SQL dialect -->
    <property name="dialect">org.hibernate.dialect.DerbyDialect</property>

    <!-- Echo all executed SQL to stdout -->
    <property name="show_sql">false</property>

    <!-- Mapping files -->
    <mapping resource="Person.hbm.xml" />
  </session-factory>
</hibernate-configuration>

And Person.hbm.xml looks as follow:

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-mapping
   PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN"
   "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"&gt;

<hibernate-mapping>

  <class name="Person" table="person">
    <id name="id" type="int">
      <generator class="increment" />
    </id>

    <property name="name" column="cname" type="string" />
  </class>

</hibernate-mapping>

With this configuration, running mvn install generates Person.java as shown below:

$ cat target/generated-sources/hibernate3/Person.java 
// default package
// Generated Dec 14, 2009 2:19:22 PM by Hibernate Tools 3.2.2.GA



/**
 * Person generated by hbm2java
 */
public class Person  implements java.io.Serializable {


     private int id;
     private String name;

    public Person() {
    }

    public Person(String name) {
       this.name = name;
    }

    public int getId() {
        return this.id;
    }

    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }




}

Everything works as described.

Pascal Thivent
Thanks Pascal! I think that this is a good start as it clearly gives an "all Maven" solution. The only remaining problem that I can see is that it refers to a single configuration file ("hibernate.cfg.xml"). I have a set of configuration files, referred to in the Ant script using the pattern "**/*.hbm.xml". Does anyone know how to do this using Maven and the plugin that Pascal suggests?
Adam
I've updated my answer to cover that part.
Pascal Thivent
Many thanks! Please see my further comments below.
Adam
A: 

Pascal, thanks again for your help! Your solution works well.

A couple of other things I encountered while working on this. The first relates to the fact that this is a fairly big project and so I've split it into multiple Maven modules to mirror the original ant multi-directory build. The module that contains the generated classes doesn't actually do any database access and so the hibernate.cfg.xml file need not, and in this case should not, contain any DB connection information. I've tried this out and it works just fine with a cut down version of the file provided by Pascal, with all of the properties tags removed.

With this in place, the build worked fine from the command line. However, try as I might, I couldn't persuade the other modules to pick up the generated classes when run from Eclipse. For the time being, the solution I have to this is to add the following line into the POM under configuration/components/component:

<outputDirectory>/src/main/java</outputDirectory>

This forces the files to be generated in a place that eclipse can pick them up for the other modules. Once this is done you must do a build on the command line and then request that Eclipse refresh the contents of the source directory to pick up the new files. As yet, I don't know of a cleaner way to handle this....

Adam
Glad it was helpful. I have a couple of remarks though. First, it's not a best practice to generate code in `src/main/java`, generated code should really go in `target`. The main reason behind that is that we want `clean` to clean them. Then, about Eclipse, the directory that contains the generated code has indeed to be added as "source folder". If you are using the m2eclipse plugin, this will be be done automatically if you right-click on the project and then *Maven > Update Project Configuration* (see http://docs.codehaus.org/display/M2ECLIPSE/Project+FAQ#ProjectFAQ-generated).
Pascal Thivent
And if you are using the maven-eclipse-plugin, invoking `mvn eclipse:eclipse` will generate a `.classpath` containing a `classpathentry` with `target/hibernate3/generated-sources` out of the box too. So, in both case, there is no need to generate sources under `src/main/java` (which is a good thing). Finally, regarding the content of my hibernate.cfg.xml, it was of course just an example :) Good luck with your migration process!
Pascal Thivent
Thanks once again! I am indeed using m2eclipse as a plugin and the Update Project Configuration did the trick and sorted out the problem. Also, I agree with your point about keeping the generated code at arm's length in a place that can be cleaned up with the classes.
Adam
A: 

Excellent post. However I can't find a way to specify the package name. Maven plugin seems to be generating the Java classes using the default package. Is there a way of specifying the package name? Thanks

sebastianr
You can configure the package name. Please post this as a new question and I'll answer :)
Pascal Thivent
Just in case, I posted an [answer](http://stackoverflow.com/questions/2848567/how-to-configure-hbm2java-and-hbm2dao-to-add-packagename-to-generated-classes/2852173#2852173) recently demonstrating this.
Pascal Thivent