views:

161

answers:

3

The following snippet generates create / drop sql for a particular database, whenever there is a modification to JPA entity classes.

How do I perform something equivalent of a 'for' operation where-in the following code can be used to generate sql for all supported databases (e.g. H2, MySQL, Postgres)

Currently I have to modify db.groupId, db.artifactId, db.driver.version everytime to generate the sql files

            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>hibernate3-maven-plugin</artifactId>
                <version>${hibernate3-maven-plugin.version}</version>

                <executions>
                    <execution>
                        <id>create schema</id>
                        <phase>process-test-resources</phase>
                        <goals>
                            <goal>hbm2ddl</goal>
                        </goals>
                        <configuration>
                            <componentProperties>
                                <persistenceunit>${app.module}</persistenceunit>
                                <drop>false</drop>
                                <create>true</create>
                                <outputfilename>${app.sql}-create.sql</outputfilename>
                            </componentProperties>
                        </configuration>
                    </execution>
                    <execution>
                        <id>drop schema</id>
                        <phase>process-test-resources</phase>
                        <goals>
                            <goal>hbm2ddl</goal>
                        </goals>
                        <configuration>
                            <componentProperties>
                                <persistenceunit>${app.module}</persistenceunit>
                                <drop>true</drop>
                                <create>false</create>
                                <outputfilename>${app.sql}-drop.sql</outputfilename>
                            </componentProperties>
                        </configuration>
                    </execution>
                </executions>

                <dependencies>
                    <dependency>
                        <groupId>org.hibernate</groupId>
                        <artifactId>hibernate-core</artifactId>
                        <version>${hibernate-core.version}</version>
                    </dependency>

                    <dependency>
                        <groupId>org.slf4j</groupId>
                        <artifactId>slf4j-api</artifactId>
                        <version>${slf4j-api.version}</version>
                    </dependency>

                    <dependency>
                        <groupId>org.slf4j</groupId>
                        <artifactId>slf4j-nop</artifactId>
                        <version>${slf4j-nop.version}</version>
                    </dependency>

                    <dependency>
                        <groupId>${db.groupId}</groupId>
                        <artifactId>${db.artifactId}</artifactId>
                        <version>${db.driver.version}</version>
                    </dependency>
                </dependencies>

                <configuration>
                    <components>
                        <component>
                            <name>hbm2cfgxml</name>
                            <implementation>annotationconfiguration</implementation>
                        </component>
                        <component>
                            <name>hbm2dao</name>
                            <implementation>annotationconfiguration</implementation>
                        </component>
                        <component>
                            <name>hbm2ddl</name>
                            <implementation>jpaconfiguration</implementation>
                            <outputDirectory>src/main/sql</outputDirectory>
                        </component>
                        <component>
                            <name>hbm2doc</name>
                            <implementation>annotationconfiguration</implementation>
                        </component>
                        <component>
                            <name>hbm2hbmxml</name>
                            <implementation>annotationconfiguration</implementation>
                        </component>
                        <component>
                            <name>hbm2java</name>
                            <implementation>annotationconfiguration</implementation>
                        </component>
                        <component>
                            <name>hbm2template</name>
                            <implementation>annotationconfiguration</implementation>
                        </component>
                    </components>
                </configuration>
            </plugin>
A: 

I assume you only have one database active at a time. If this is a valid assumption, you don't really need a loop, instead you need to be able to switch between databases.

You can achieve this by declaring the DB-specific properties in profiles, then activate the relevant profile.

The configuration below shows how to set profiles for the 3 databases you mention, and use a default in case none is specified. You can omit the default and the build will then fail unless a profile is declared.

You'd activate each profile from the command line as follows:

mvn test -P h2
mvn test -P mysql
mvn test -P postgresql

<profiles>
  <profile>
    <id>h2</id>
    <properties>
      <db.groupId>com.h2database</db.groupId>
      <db.artifactId>h2</db.artifactId>
      <db.version>1.1.117</db.version>
    </properties>
  </profile>
  <profile>
    <id>mysql</id>
    <properties>
      <db.groupId>mysql</db.groupId>
      <db.artifactId>mysql-connector-java</db.artifactId>
      <db.version>5.1.6</db.version>
    </properties>
  </profile>
  <profile>
    <id>postgresql</id>
    <properties>
      <db.groupId>postgresql</db.groupId>
      <db.artifactId>postgresql</db.artifactId>
      <db.version>8.3-603.jdbc4</db.version>
    </properties>
  </profile>
</profiles>
...
<!--default database, say mysql-->
<properties>
  <db.groupId>mysql</db.groupId>
  <db.artifactId>mysql-connector-java</db.artifactId>
  <db.version>5.1.6</db.version>
</properties>

You can activate a profile based on the value of an environment variable, for example the configuration below activates the h2 profile if the ACTIVE_DB environment variable is set to "h2".

  <profile>
    <id>h2</id>
    <activation>
      <property>
        <name>ACTIVE_DB</name>
        <value>h2</value>
      </property>
    </activation>
    <properties>
      <db.groupId>com.h2database</db.groupId>
      <db.artifactId>h2</db.artifactId>
      <db.version>1.1.117</db.version>
    </properties>
  </profile>
Rich Seller
Rich - I do have one database active at a time and I use the profiles to switch between them. I don't use the hbm2ddl.auto capability to generate my SQL dynamically, but I use hbm2ddl plugin to generate scripts for all supported databases and pre-load them prior to startup. Hence I would require the capability to generate all create / drop SQL for all supported databases in a single-run. Hope it clarifies.
Joshua
Ok, I see the problem, I've not got a configuration to test this against. Inspecting the plugin code indicates there's no built-in mechanism to do this
Rich Seller
A: 

Some possible directions to take are:

1) call out to an Ant task which can use ant-tools to implement loops (nasty and XML intensive though)

2) write your own Maven plugin (Mojo) that wraps the Hibernate plugin calls within a loop and takes a few parameters.

Take a look at the Better Builds With Maven eBook for more details.

Gary Rowe
A: 

I would go with either an integrated ant solution using maven-antrun-plugin or a custom java class using the Maven Exec Plugin here: http://mojo.codehaus.org/exec-maven-plugin/java-mojo.html

<build>
<plugins>
  <plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>exec-maven-plugin</artifactId>
    <executions>
      <execution>
        <goals>
          <goal>java</goal>
        </goals>
      </execution>
    </executions>
    <configuration>
      <mainClass>com.yourcompany.DocBuilder</mainClass>
      <arguments>
        <argument>propertyFile1.properties</argument>
        <argument>propertyFile2.properties</argument>
        <argument>propertyFile3.properties</argument>
        <argument>propertyFile4.properties</argument>
      </arguments>
    </configuration>
  </plugin>
</plugins>

Then write a java class com.yourcompany.DocBuilder (or whatever) with a main method that takes the array of properties files as parameter. Your java class can embed ant or run ant as an external process (if you do that, you'll probably want to use the exec mojo instead of the java mojo)

Sean

seanizer