Search code examples
javamysqlspringjpageojson

Can not resolve 'ST_GeomFromText' in serviceImpl class


I am trying to save my geometry data to MySQL table through JPA.

georepository.save("id", ST_GeomFromText('POLYGON((0 1, 2 5, 2 7, 0 7, 0 1))') ); 

but I am getting:

Can not resolve 'ST_GeomFromText' in serviceImpl class.

Here is my model

public class Mygeo {

   @Id
   @Column(name = "id")
   private String id;

   @Column(name = "geodata", nullable = false)
   private Geometry geodata;
}

This is how I am saving manually in DB. what will be the JPA alternate way to save this.

INSERT INTO mygeo (id, geodata)VALUES ("ID",ST_GeomFromText('POLYGON((0 1, 2 5, 2 7, 0 7, 0 1))'));

Note : I do have access of geometry string like this

System.out.println(geometryObject.toString());
{"coordinates":[[[0,1],[2,5],[2,7],[0,7],[0,1]]],"type":"Polygon"}

Solution

  • The way to save the Geometry data into database while using the Spring Data JPA is given below in details (step - by - step):


    I will explain with the help of a project.

    The project structure :

    enter image description here

    MyGeo entity :

    package com.solve.problemssolve;
    
    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.Id;
    
    import org.locationtech.jts.geom.Geometry;
    
    import lombok.Data;
    
    @Data
    @Entity
    public class MyGeo {
    
        @Id
        @Column(name = "id")
        private String id;
    
        @Column(name = "geodata", nullable = false)
        private Geometry geodata;
    }
    

    MyGeoRepository class

    package com.solve.problemssolve;
    
    import org.springframework.data.jpa.repository.JpaRepository;
    
    public interface MyGeoRepository extends JpaRepository<MyGeo, String> {
    
    }
    

    application.properties :

    spring.datasource.url=jdbc:mysql://localhost:3306/test
    spring.datasource.username=root
    spring.datasource.password=Anish@123
    spring.jpa.hibernate.ddl-auto=update
    spring.jpa.properties.hibernate.dialect=org.hibernate.spatial.dialect.mysql.MySQL5SpatialDialect
    

    Note: You have to enable org.hibernate.spatial.dialect.mysql.MySQL5SpatialDialect in your spring boot application to support spatial features.

    According to MySQL5SpatialDialect Hibernate docs :

    It is a dialect for MySQL 5 using InnoDB engine, with support for its spatial features.

    pom.xml :

    <?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>2.7.4</version>
            <relativePath /> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.solve</groupId>
        <artifactId>problems-solve</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>problems-solve</name>
        <description>Demo project for Spring Boot</description>
        <properties>
            <java.version>1.8</java.version>
        </properties>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-jpa</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-devtools</artifactId>
                <scope>runtime</scope>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <scope>runtime</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-configuration-processor</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.hibernate</groupId>
                <artifactId>hibernate-spatial</artifactId>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <configuration>
                        <excludes>
                            <exclude>
                                <groupId>org.projectlombok</groupId>
                                <artifactId>lombok</artifactId>
                            </exclude>
                        </excludes>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    
    </project>
    

    In MySQL DB (Table structure) :

    enter image description here

    Note: This table is automatically generated by Hibernate as I have kept ddl-auto=update but you can create table manually also.

    Resource class :

    @RestController
    public class Resource {
    
        @Autowired
        private MyGeoRepository myGeoRepository;
        
        @GetMapping("/save")
        public void save() throws ParseException {
            WKTReader wktReader = new WKTReader();
            Geometry geometry = wktReader.read("POLYGON((0 1, 2 5, 2 7, 0 7, 0 1))");
            System.out.println(geometry);
            MyGeo geo = new MyGeo();
            geo.setGeodata(geometry);
            geo.setId("ID");
            myGeoRepository.save(geo); 
            System.out.println(myGeoRepository.findById("ID"));
        }
            
    }
    

    You have to use WKTReader to save the data via JPA.

    According to WKTReader docs :

    Converts a geometry in Well-Known Text format to a Geometry.

    WKTReader supports extracting Geometry objects from either Readers or Strings. This allows it to function as a parser to read Geometry objects from text blocks embedded in other data formats (e.g. XML).

    A WKTReader is parameterized by a GeometryFactory, to allow it to create Geometry objects of the appropriate implementation. In particular, the GeometryFactory determines the PrecisionModel and SRID that is used.

    The WKTReader converts all input numbers to the precise internal representation.

    According to WKTReader.read(String wellKnownText) docs :

    It reads a Well-Known Text representation of a Geometry from a String.

    This SQL statement ST_GeomFromText('POLYGON((0 1, 2 5, 2 7, 0 7, 0 1))') is equivalent to this line Geometry geometry = wktReader.read("POLYGON((0 1, 2 5, 2 7, 0 7, 0 1))");

    As soon as, I have ran the example, it saved successfully without any issues.

    Server log screenshot :

    enter image description here

    DB row screenshot :

    enter image description here