graalvm-reachability-metadata icon indicating copy to clipboard operation
graalvm-reachability-metadata copied to clipboard

Hibernate spatial with PostGIS failing when running in native

Open loonis opened this issue 1 year ago • 1 comments

Describe the bug

Hibernate spatial is failing when running in native image.

To Reproduce

Create a spring boot 3.3.1 project using hibernate-spatial

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.3.1</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>

	<groupId>com.test</groupId>
	<artifactId>test</artifactId>


	<version>0.0.1-SNAPSHOT</version>
	<name>test</name>
	<description>Demo project for Spring Boot</description>


	<properties>
		<java.version>21</java.version>
		<org.mapstruct.version>1.5.5.Final</org.mapstruct.version>
		<graphql-java-extended-scalars.version>22.0</graphql-java-extended-scalars.version>
		<hibernate.version>6.5.2.Final</hibernate.version>
	</properties>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>com.google.cloud</groupId>
				<artifactId>spring-cloud-gcp-dependencies</artifactId>
				<version>5.4.1</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<dependencies>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-graphql</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-validation</artifactId>
		</dependency>

		<dependency>
			<groupId>org.postgresql</groupId>
			<artifactId>postgresql</artifactId>
			<scope>runtime</scope>
		</dependency>

		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>

		<dependency>
			<groupId>com.graphql-java</groupId>
			<artifactId>graphql-java-extended-scalars</artifactId>
			<version>${graphql-java-extended-scalars.version}</version>
		</dependency>

		<dependency>
			<artifactId>mapstruct</artifactId>
			<groupId>org.mapstruct</groupId>
			<version>${org.mapstruct.version}</version>
		</dependency>

		<dependency>
			<groupId>org.hibernate.orm</groupId>
			<artifactId>hibernate-spatial</artifactId>
			<version>${hibernate.version}</version>
		</dependency>

		<dependency>
			<groupId>com.google.cloud</groupId>
			<artifactId>spring-cloud-gcp-starter-sql-postgresql</artifactId>
		</dependency>


<!--		TESTS-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webflux</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.graphql</groupId>
			<artifactId>spring-graphql-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>

			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>${maven-compiler-plugin.version}</version>
				<executions>
					<execution>
						<id>default-compile</id>
						<phase>compile</phase>
						<goals>
							<goal>compile</goal>
						</goals>
					</execution>
					<execution>
						<id>default-testCompile</id>
						<phase>test-compile</phase>
						<goals>
							<goal>testCompile</goal>
						</goals>
					</execution>
				</executions>
				<configuration>
					<annotationProcessorPaths>
						<path>
							<artifactId>mapstruct-processor</artifactId>
							<groupId>org.mapstruct</groupId>
							<version>${org.mapstruct.version}</version>
						</path>
						<path>
							<artifactId>lombok</artifactId>
							<groupId>org.projectlombok</groupId>
							<version>${lombok.version}</version>
						</path>

						<path>
							<groupId>org.hibernate.orm</groupId>
							<artifactId>hibernate-jpamodelgen</artifactId>
							<version>${hibernate.version}</version>
						</path>


					</annotationProcessorPaths>
					<source>${java.version}</source>
					<target>${java.version}</target>
				</configuration>
			</plugin>

			<plugin>
				<groupId>io.github.deweyjose</groupId>
				<artifactId>graphqlcodegen-maven-plugin</artifactId>
				<version>1.61.0</version>
				<executions>
					<execution>
						<id>dgs-codegen</id>
						<phase>generate-sources</phase>
						<goals>
							<goal>generate</goal>
						</goals>
						<configuration>
							<schemaPaths>
								<param>src/main/resources/graphql</param>
							</schemaPaths>
							<packageName>com.kc.dto</packageName>
							<addGeneratedAnnotation>true</addGeneratedAnnotation>
							<generateClientApiV2>false</generateClientApiV2>
							<generateDataTypes>true</generateDataTypes>

							<onlyGenerateChanged>false</onlyGenerateChanged>
							<addGeneratedAnnotation>true</addGeneratedAnnotation>
						</configuration>
					</execution>
				</executions>
			</plugin>

			<plugin>
				<groupId>org.codehaus.mojo</groupId>
				<artifactId>build-helper-maven-plugin</artifactId>
				<executions>
					<execution>
						<id>add-dgs-source</id>
						<phase>generate-sources</phase>
						<goals>
							<goal>add-source</goal>
						</goals>
						<configuration>
							<sources>
								<source>${project.build.directory}/generated-sources</source>
							</sources>
						</configuration>
					</execution>
				</executions>
			</plugin>

			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<executions>
					<execution>
						<goals>
							<goal>build-image</goal>
						</goals>
					</execution>
				</executions>



				<configuration>
					<excludes>
						<exclude>
							<groupId>org.projectlombok</groupId>
							<artifactId>lombok</artifactId>
						</exclude>
					</excludes>
				</configuration>
			</plugin>

			<plugin>
				<groupId>org.hibernate.orm.tooling</groupId>
				<artifactId>hibernate-enhance-maven-plugin</artifactId>
				<version>${hibernate.version}</version>
				<executions>
					<execution>
						<configuration>
							<failOnError>true</failOnError>
							<enableLazyInitialization>true</enableLazyInitialization>
						</configuration>
						<goals>
							<goal>enhance</goal>
						</goals>
					</execution>
				</executions>
			</plugin>

			<plugin>
				<groupId>org.graalvm.buildtools</groupId>
				<artifactId>native-maven-plugin</artifactId>
			</plugin>

		</plugins>
	</build>
</project>
spring:
  jpa:
    properties:
      hibernate:
        format_sql=true:
    defer-datasource-initialization: true
    hibernate:
      ddl-auto: none

One of my entity contains a field org.locationtech.jts.geom.Point; which is a PostGIS GEOGRAPHY(POINT, 4326),

I filter my entities based on these gps coordinates

      org.locationtech.jts.geom.GeometryFactory geometryFactory = new GeometryFactory();
      org.locationtech.jts.geom.Point refPoint = geometryFactory.createPoint(new Coordinate(latitude, longitude));
      refPoint.setSRID(4326);

      jakarta.persistence.criteria.Predicate distancePredicate = cb.equal(cb.literal(true), cb.function(
          "ST_DWithin",
          Boolean.class,
          root.get("location"),
          cb.literal(refPoint),
          cb.literal(radiusMeter)
      ));

The native image generation is working, but then running the executable will generate these error log bellow.

Error 1

Caused by: java.lang.NoClassDefFoundError: Could not initialize class org.geolatte.geom.crs.CrsRegistry
        at org.geolatte.geom.jts.JTS.determineCrsFromCoordinatesAndSrid(JTS.java:163) ~[na:na]
        at org.geolatte.geom.jts.JTS.from(JTS.java:126) ~[na:na]
        at org.hibernate.spatial.JTSGeometryJavaType.unwrap(JTSGeometryJavaType.java:102) ~[na:na]
        at org.hibernate.spatial.JTSGeometryJavaType.unwrap(JTSGeometryJavaType.java:37) ~[na:na]

Error2

Caused by: java.lang.InstantiationException: org.geolatte.geom.codec.PostgisWkbDecoder
        at [email protected]/java.lang.Class.newInstance(DynamicHub.java:719) ~[com.kc.KnightclubbersApplication:na]
        at org.geolatte.geom.codec.Wkb.createInstance(Wkb.java:222) ~[na:na]
        ... 64 common frames omitted
Caused by: java.lang.NoSuchMethodException: org.geolatte.geom.codec.PostgisWkbDecoder.<init>()
        at [email protected]/java.lang.Class.checkMethod(DynamicHub.java:1078) ~[com.kc.KnightclubbersApplication:na]
        at [email protected]/java.lang.Class.getConstructor0(DynamicHub.java:1241) ~[com.kc.KnightclubbersApplication:na]
        at [email protected]/java.lang.Class.newInstance(DynamicHub.java:706) ~[com.kc.KnightclubbersApplication:na]
        ... 65 common frames omitted

Error 3

Caused by: java.lang.IllegalStateException: Can't find spatial_ref_sys definitions.
        at org.geolatte.geom.crs.CrsRegistry.createReader(CrsRegistry.java:80) ~

I followed the indication given here: https://github.com/oracle/graalvm-reachability-metadata/issues/233#issuecomment-2152227329 which solves some problems, but not theses ones.

System Info

OS: Windows 11 GraalVM Version 22 Java Version Java 21 CE Plugin version: graalvm-reachability-metadata-0.10.2-repository.zip

loonis avatar Jun 26 '24 20:06 loonis

You need to create :

  1. src/main/resources/META-INF/native-image/resource-config.json inside your project and add the following content:
{
  "resources": [
    {
      "pattern": "spatial_ref_sys.txt"
    }
  ]
}
  1. src/main/resources/META-INF/native-image/reflect-config.json inside your project and add the following content: [ { "condition": { "typeReachable": "org.hibernate.spatial.integration.SpatialInitializer" }, "name":"org.geolatte.geom.codec.PostgisWkbDecoder", "methods":[{"name":"<init>","parameterTypes":[] }] }, { "condition": { "typeReachable": "org.hibernate.spatial.integration.SpatialInitializer" }, "name":"org.geolatte.geom.codec.PostgisWktEncoder", "methods":[{"name":"<init>","parameterTypes":[] }] }, { "condition": { "typeReachable": "org.hibernate.spatial.integration.SpatialInitializer" }, "name": "org.geolatte.geom.crs.CrsRegistry", "allDeclaredConstructors": true, "allDeclaredMethods": true }, { "condition": { "typeReachable": "org.hibernate.spatial.integration.SpatialInitializer" }, "name": "org.geolatte.geom.crs.CoordinateReferenceSystems", "allDeclaredConstructors": true, "allDeclaredMethods": true }, { "condition": { "typeReachable": "org.hibernate.spatial.integration.SpatialInitializer" }, "name":"org.locationtech.jts.geom.Point" }, { "name": "org.hibernate.spatial.HSMessageLogger_$logger", "condition": { "typeReachable": "org.hibernate.spatial.integration.SpatialInitializer" }, "methods": [ { "name": "<init>", "parameterTypes": [ "org.jboss.logging.Logger" ] } ] } ]

jma-9code avatar Jun 30 '24 16:06 jma-9code

Thanks for answering the question. @mhalbritter do we need to update the metadata here?

vjovanov avatar Nov 13 '25 12:11 vjovanov

I've never heard of hibernate-spatial before. I guess there's metadata missing for org.hibernate.orm:hibernate-spatial.

And there's also missing metadata for the library which contains org.geolatte.geom.*.

I don't think this belongs into the existing hibernate metadata.

mhalbritter avatar Nov 13 '25 12:11 mhalbritter