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.
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.