Search code examples
javapostgresqlhibernatehibernate-toolshbm2java

How can one get Hibernate to use UserType for hbm2java code generation?


My situation is that I have a Spring Boot application, a PostgreSQL database, and use the hbm2java hibernate Maven plugin to generate the entity classes. The database uses types for which JPA/Hibernate do not have a matching builtin type (example: ltree), so my idea is to use a UserType.

I have created an LTreeType.java class implementing the UserType interface. How can I get hbm2java to use it for every database column of type ltree?

EDIT:

I found out so far that I have to either create a reveng.xml file or subclass DefaultReverseEngineeringStrategy. I have no idea how to tell the plugin to use the former (if it even still exists - this whole mess is completely undocumented), or how to actually get my subclass to work with the Maven plugin.

For the former, <revengFile> does seem to work, although I do end up with the error Execution entity-generation of goal org.hibernate.tool:hibernate-tools-maven:6.2.1.Final:hbm2java failed: Could not resolve named type.

So if anyone knows how to tell either Maven or this darned plugin to add my normal source folder to the classpath, this might potentially solve the problem.

As a final word of advice: stay away as far as possible from hibernate-tools if you have the choice. It's an undocumented mess.


Solution

  • So, I finally managed to do it. It's complicated, to say the least.

    First, you have to create your user type - make sure to implement both UserType<> and JdbcType - in this case, I used UserType<LTree> and created an LTree class to hold the value, but you could also just use String.

    Then you need to subclass DelegatingStrategy as well as whatever SQLDialect you are using - in my example, it's PostgreSQLDialect.

    ExampleReverseEngineeringStrategy:

    public class ExampleReverseEngineeringStrategy extends DelegatingStrategy {
      public ExampleReverseEngineeringStrategy(RevengStrategy delegate) {
        super(delegate);
      }
    
      @Override
      public String columnToHibernateTypeName(TableIdentifier table, String columnName, int sqlType, int length, int precision, int scale, boolean nullable, boolean generatedIdentifier) {
        if(columnName.equals("path")) {
          return "ltree";
        } else {
          return super.columnToHibernateTypeName(table, columnName, sqlType, length, precision, scale, nullable, generatedIdentifier);
      }
    }
    

    ExamplePostgreSQLDialect:

    public class ExamplePostgreSQLDialect extends PostgreSQLDialect {
      @Override
      public void contributeTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
        super.contributeTypes(typeContributions, serviceRegistry);
        typeContributions.getTypeConfiguration().getBasicTypeRegistry().register(LTreeType.INSTANCE, "ltree");
      }
    }
    

    Finally, I used the Maven antrun plugin. This is by far the longest code block, so beware:

          <plugin>
            <artifactId>maven-antrun-plugin</artifactId>
            <version>3.1.0</version>
            <dependencies>
              <dependency>
                <groupId>org.hibernate.tool</groupId>
                <artifactId>hibernate-tools-ant</artifactId>
                <version>${hibernate.version}</version>
              </dependency>
              <dependency>
                <groupId>org.hibernate.tool</groupId>
                <artifactId>hibernate-tools-orm</artifactId>
                <version>${hibernate.version}</version>
              </dependency>
              <dependency>
                <groupId>org.postgresql</groupId>
                <artifactId>postgresql</artifactId>
                <version>42.6.0</version>
              </dependency>
            </dependencies>
            <executions>
              <execution>
                <id>entity-generation-ant</id>
                <phase>generate-sources</phase>
                <configuration>
                  <target>
                    <echo message="Ant: Running Hibernatetool with ${project.build.sourceDirectory}" />
                    <property name="maven_compile_classpath"
                    refid="maven.compile.classpath" />
                    <property name="maven_test_classpath"
                    refid="maven.test.classpath" />
                    <path id="hibernatetool.path">
                      <pathelement path="${maven_compile_classpath}" />
                      <pathelement path="${maven_test_classpath}" />
                      <pathelement path="${project.build.sourceDirectory}" />
                      <pathelement path="${project.build.outputDirectory}" />
                    </path>
                    <taskdef name="hibernatetool"
                    classname="org.hibernate.tool.ant.HibernateToolTask"
                    classpathref="hibernatetool.path" />
                    <hibernatetool destdir="${project.build.directory}/generated-sources">
    
                      <classpath>
                        <path refid="hibernatetool.path" />
                        <path location="${project.build.outputDirectory}" />
                      </classpath>
                      <jdbcconfiguration propertyfile="src/main/resources/application.properties"
                      packagename="com.example.db"
                      reversestrategy="com.example.SparsanaReverseEngineeringStrategy">
    
                        <!-- revengfile="src/main/resources/hibernate.reveng.xml" > -->
                        <fileset dir="${project.build.sourceDirectory}" />
                      </jdbcconfiguration>
                      <hbm2java jdk5="true" ejb3="true" />
                    </hibernatetool>
                  </target>
                </configuration>
                <goals>
                  <goal>run</goal>
                </goals>
              </execution>
            </executions>
          </plugin>
    

    It might also work with the "normal" hbm2java Maven plugin, I didn't test that yet, I didn't have the nerve yet. Hopefully this will help someone in as dire a situation as I was.