How to insert into geo point column in spring data-jpa + mysql 8? - spring-data-jpa

My Environment
mysql 8.0.25
hibernate-core:5.4.32
hibernate-spatial:5.4.32
spring-boot2.5.4
java 8
What I did
application.yml
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/database?serverTimezone=UTC&characterEncoding=UTF-8
username: root
password: password
jpa:
hibernate.ddl-auto: create
generate-ddl: true
database: mysql
properties:
hibernate.dialect: org.hibernate.spatial.dialect.mysql.MySQL56SpatialDialect
logging:
level:
org:
hibernate:
SQL: debug
type: trace
Entity class
import com.example.mypackage.domain.BaseTimeEntity;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.springframework.data.geo.Point;
import javax.persistence.*;
#Getter
#NoArgsConstructor
#Entity
public class Party extends BaseTimeEntity { // BaseTimeEntity adds modifiedAt, createdAt columns
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(columnDefinition = "TEXT")
private String title;
#Column(columnDefinition = "POINT")
private Point coordinate;
#Builder
public Party(Point coordinate, String title, String body) {
this.coordinate = coordinate;
this.title = title;
this.body = body;
}
}
Test
#SpringBootTest
class PartyRepositoryTest {
#Autowired
PartyRepository partyRepository;
#Test
public void register_party() {
// Given
Double x = 127.02558;
Double y = 37.30160;
Point coordinate = new Point(x, y);
partyRepository.save(
Party.builder()
.coordinate(coordinate)
.title("test title")
.build()
);
// When
List<Party> partyList = partyRepository.findAll();
// Then
Party party = partyList.get(0);
assertEquals(x, party.getCoordinate().getX());
assertEquals(y, party.getCoordinate().getY());
}
What I expected
insert row in 'party' table successfully
What actually happened
I got error. Log is as below.
insert into party (created_at, modified_at, body, coordinate, title) values (?, ?, ?, ?, ?)
binding parameter [1] as [TIMESTAMP] - [2021-09-12T14:45:31.018]
binding parameter [2] as [TIMESTAMP] - [2021-09-12T14:45:31.018]
binding parameter [3] as [VARCHAR] - []
binding parameter [4] as [VARBINARY] - [Point [x=127.025580, y=37.301600]]
binding parameter [5] as [VARCHAR] - [test title]
SQL Error: 1416, SQLState: 22001
Data truncation: Cannot get geometry object from data you send to the GEOMETRY field
Question
Please Let me know what am I doing wrong?
does hibernate-spatial supports mysql point?

You are using the wrong spatial type: org.springframework.data.geo.Point is not supported by Hibernate Spatial. Use either org.locationtech.jts.geom.* or org.geolatte.geom.* in your entity class and it should be fine.

After a lot of research, that's how it worked for me:
Language: Kotlin
MySQL version: 8.0.23
build.gradle.kts
...
implementation("org.hibernate:hibernate-spatial:5.6.2.Final")
...
properties.yml
...
spring:
jpa:
properties:
hibernate.dialect: org.hibernate.spatial.dialect.mysql.MySQL8SpatialDialect
database-platform: org.hibernate.spatial.dialect.mysql.MySQL8SpatialDialect
...
entity:
package svns.mono.fad.springcore.data.entity
...
import org.locationtech.jts.geom.Point
...
#Entity
#Table(name = "issue")
data class IssueEntity(
...
val location: Point,
...
)

Related

Micronaut: configure JPA batch operations

I'm trying to configure my Micronaut project to insert/update using batch operations (create one single insert with 50 items included instead of 50 different inserts).
I cannot find a more detailed manual so the configuration that I'm using is this one:
datasources:
default:
url: jdbc:postgresql:///postgres?cloudSqlInstance=uri&socketFactory=com.google.cloud.sql.postgres.SocketFactory&ipTypes=PRIVATE
username: ****
password: ****
driverClassName: org.postgresql.Driver
jpa:
default:
properties:
hibernate:
hbm2ddl:
auto: none
jdbc:
batch_size: 50
order_inserts: true
order_updates: true
batch_versioned_data: true
show_sql: false
This is the repo:
import io.micronaut.data.annotation.Repository;
import io.micronaut.data.repository.GenericRepository;
#Repository
public interface ArticleExportPort extends GenericRepository<ArticleExport, Long> {
void insertMany(Iterable<ArticleExport> articleExports);
List<ArticleExport> findAll();
}
And this is the Entity:
#Data
#Entity
#Table(name = "PRODUCT_EXPORT")
public class ArticleExport {
#Id
private Long Id;
#NotNull
#Column(name = "PRODUCT_ID",nullable = false)
private Long productId;
}
The database is a PostgreSQL located in Google Cloud but also testing in local it doesn't work.
Any suggestion?
Thanks!

unable to get table from postgreSQL despite the spring boot program being connected to database and the database not being empty

I am rather new to Spring boot and I am trying to write a very simple program that can perform post, get and delete on a postgreSQL data base. the database is named "recipes" schema "public" and table "recipe"
The problem that I ran into is that when I make the get request through postman, it simply returns null despite the data base being initialized with data.
like so
I did my best to try and narrow down the problem and the furthest I got is that line from the service layer is returning nothing when evaluated
jdbcTemplate.query(sql, new RecipeRowMapper())
The database is initialized with the following SQL
INSERT INTO recipe(id, name, ingredients, instructions, date_added)
values (1, 'ini test1', '10 cows 20 rabbits', 'cook ingredients with salt', '2004-01-02'),
(2, 'ini test2', '30 apples 20 pears', 'peel then boil', '2004-01-13');
I know the database is not empty because when I run the following SQL
SELECT * from recipe
i get
And the data base is connected as seen below (one thing I do find strange is that the table "recipe" isn't showing up in the DB browser but I don't know what to make of it)
application.yml
app:
datasource:
main:
driver-class-name: org.postgresql.Driver
jdbc-url: jdbc:postgresql://localhost:5432/recipes?currentSchema=public
username: postgres
password: password
server:
error:
include-binding-errors: always
include-message: always
spring.jpa:
database: POSTGRESQL
hibernate.ddl-auto: create
show-sql: true
dialect: org.hibernate.dialect.PostgreSQL9Dialect
format_sql: true
spring.flyway:
baseline-on-migrate: true
this is the service layer
public List<Recipe> getRecipes(){
var sql = """
SELECT id, name, ingredients, instructions, date_added
FROM public.recipe
LIMIT 50
""";
return jdbcTemplate.query(sql, new RecipeRowMapper());
}
and this is the controller
#GetMapping(path = "/test")
public String testRecipe(){
return recipeService.test();
}
and rowmapper
public class RecipeRowMapper implements RowMapper<Recipe> {
#Override
public Recipe mapRow(ResultSet rs, int rowNum) throws SQLException {
return new Recipe(
rs.getLong("id"),
rs.getString("name"),
rs.getString("ingredients"),
rs.getString("instructions"),
LocalDate.parse(rs.getString("date_added"))
);
}
}
finally recipe entity looks like this
#Data
#Entity
#Table
public class Recipe {
#Id
#GeneratedValue(
strategy = GenerationType.IDENTITY
)
#Column(name = "id", updatable = false, nullable = false)
private long id;
#Column(name = "name")
private String name;
#Column(name = "ingredients")
private String ingredients;
#Column(name = "instructions")
private String instructions;
#Column(name = "date_added")
private LocalDate dateAdded;
public Recipe(){};
public Recipe(long id, String name, String ingredients, String instructions, LocalDate date){}
public Recipe(String name,
String ingredients,
String instructions,
LocalDate dateAdded
) {
this.name = name;
this.ingredients = ingredients;
this.instructions = instructions;
this.dateAdded = dateAdded;
}
}
As it turns out the problem is caused by the LocalDate not being converted correctly and was being posted as null. That caused the
LocalDate.parse(rs.getString("date_added"))
to throw a null pointer exception which is what has been causing all the problems...

jsonb type data saving with postgres db and spring boot getting JpaSystemException after java 8 to java 11 migration

I'm trying to save jsonb type data to a Postgres db table using the following library
<dependency>
<groupId>com.vladmihalcea</groupId>
<artifactId>hibernate-types-52</artifactId>
<version>2.11.1</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.2.14</version>
</dependency>
In java 8 it worked without any issue, But due to requirement I had to migrate the service to java 11 but after the migration when I tried to save jsonb to the table I got the following error.
org.springframework.orm.jpa.JpaSystemException: java.lang.IllegalArgumentException: The given byte array cannot be transformed to Json object; nested exception is org.hibernate.HibernateException: java.lang.IllegalArgumentException: The given byte array cannot be transformed to Json object.
NOTE - Hibernate versions are the same in both java 8 and java 11
version: 5.4.20.Final in both
Following is the entity which trying to save
#Builder(toBuilder = true)
#Data
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "test")
#TypeDefs({
#TypeDef(name = "jsonb", typeClass = JsonBinaryType.class),
})
public class Test extends Auditable {
#Id
#Column(name = "id", updatable = false, nullable = false, unique = true)
private UUID id;
#Type(type = "jsonb")
#Column(name = "data", columnDefinition = "jsonb")
private RequestEventDto data;
}
following is the RequestEventDto
import lombok.Builder;
import lombok.Data;
import java.util.List;
import java.util.Map;
#Data
#Builder
public class RequestEventDto {
private String requestId;
#Builder.Default
private String applicationId = "program1";
private String entityType;
private List<Map<String, Object>> listEntities;
}
Can you help with this problem?
Issue fixed with adding the following annotations to the RequestEventDto
#NoArgsConstructor
#AllArgsConstructor
public class RequestEventDto {
It seems it is due to the constructor not there when serialization happening.

Unidirectional Many-To-One:saving parent entity generates unwanted insert into child table

I need to use a #JoinColumn #ManyToOne column as part of a composite primary key (having preferred option for defining composite key #IdClass, not #EmbeddedId).
The enities involved are Parent and Child - relation sort of similar to employee and department, with the only difference that I only need to access child ('department') fields from parent ('employee'), never vice versa. Therefore I use unidirectional #ManyToOne in the Parent entity.
Currently I have an issue with updating the parent table - upon parent entity update, hibernate tries to insert a record into the child table and understandably fails with primary key violation. I need to suppress the inserts to child table. I hoped that 'insertable = false, updatable = false' in the annotation would solve the issue, but it doesn't.
I used these guides to set up a composite primary key: https://www.baeldung.com/jpa-composite-primary-keys, https://dwuysan.wordpress.com/2012/02/22/joincolumn-is-part-of-the-composite-primary-keys/ , https://vladmihalcea.com/the-best-way-to-map-a-composite-primary-key-with-jpa-and-hibernate/
My code comes below.
Parent entity:
#Data
#Builder
#Entity
#Table(name = "Parent", schema = "dbo")
#IdClass(value = ParentPrimaryKey.class)
public class Parent {
#Id
#Column(name = "dateTime", insertable = false, updatable = false)
private Timestamp dateTime;
#Id
#MapsId("child")
#ManyToOne
#JoinColumn(name = "child", insertable = false, updatable = false)
private Child child;
#Column(name = "status")
private Integer status;
}
Parent's primary key entity:
#Data
#NoArgsConstructor
#AllArgsConstructor
public class ParentPrimaryKey implements Serializable {
private static final long serialVersionUID = 1;
private Timestamp dateTime;
private String child;
}
Child entity:
#Data
#Entity(name = "Child")
#NoArgsConstructor
#AllArgsConstructor
#Table(name = "Child", schema = "dbo")
public class Location implements Serializable {
private static final long serialVersionUID = 1;
#Id
#Column(name = "child")
private String child;
#Column(name = "param")
private String param;
// some other child param fields
}
Integration testing on in-memory H2 with some entries to TestTable available:
#Test
public void shouldSaveRecord() {
Parent selectedRecord = parentRepository.findAll().get(0);
selectedRecord.setStatus(9);
parentRepository.save(selectedRecord);
}
Logs generated by running the test:
2019-07-01 09:52:20.547 [main] DEBUG org.hibernate.SQL - select parent0_.dateTime as dateTime1_15_, parent0_.child as child 3_15_, parent0_.status as status4_15_ from dbo.Parent parent0_
2019-07-01 09:52:20.555 [main] DEBUG org.hibernate.SQL - select child0_.child as child1_5_0_, child0_.param as param3_5_0_ from dbo.Child child0_ where child0_.child=?
2019-07-01 09:52:20.602 [main] DEBUG org.hibernate.SQL - select parent0_.dateTime as dateTime1_15_0_, parent0_.child as child3_15_0_, parent0_.status as status4_15_0_ from dbo.Parent parent0_ where parent0_.dateTime=? and parent0_.child=?
2019-07-01 09:52:20.630 [main] DEBUG org.hibernate.SQL - insert into dbo.child (child, param) values (?, ?)
2019-07-01 09:52:20.636 [main] WARN o.h.e.jdbc.spi.SqlExceptionHelper - SQL Error: 23505, SQLState: 23505
2019-07-01 09:52:20.636 [main] ERROR o.h.e.jdbc.spi.SqlExceptionHelper - Unique index or primary key violation: "PRIMARY_KEY_81 ON DBO.CHILD(CHILD) VALUES ('MTS_0102_010R4', 8)"; SQL statement:
insert into dbo.child (child, param) values (?, ?) [23505-197]
org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint ["PRIMARY_KEY_81 ON DBO.CHILD(CHILD) VALUES ('MTS_0102_010R4', 8)"; SQL statement:
insert into dbo.child (child, param) values (?, ?) [23505-197]]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement
I need to suppress unwanted insert into child table.
Any help would be highly appreciated.
Upd 2019-07-02. Current solution: had to switch to #EmbeddedId. Sort of pain, as I had to update all non-native queries. Still wondering whether it could have been managed with #IdClass.

JPA #Index annotation doesn't work

I want to add index to one of the columns in my table, but it always complained that column is not found, I checked the table, the column exists!
If I removed that index annotation from the entity object, it works fine.
If I change columnList to the field name, eg, columnList="reportOwnerId", it fails with same message: 'database column not found'
how can i make it work? thanks
here is my entity class:
import javax.persistence.Id;
import javax.persistence.Index;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
#Entity
#Getter
#NoArgsConstructor
#Table(name = "schedules",
indexes = { #Index(name = "SCHEDULE_OWNER_GUID_INDEX", columnList = "report_owner_guid")}
)
public class ReportSchedule extends AbstractTimestampEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Setter
#Column(name = "report_owner_guid", nullable = false, unique = false)
private String reportOwnerId;
...
and here is the error message:
Caused by: org.hibernate.AnnotationException: Unable to create unique key constraint (report_owner_guid) on table schedules: database column 'report_owner_guid' not found. Make sure that you use the correct column name which depends on the naming strategy in use (it may not be the same as the property name in the entity, especially for relational types)
at org.hibernate.cfg.Configuration.buildUniqueKeyFromColumnNames(Configuration.java:1682)
at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1457)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1844)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:850)
Later I figured out it might caused by the fact that column is not unique, and I updated the index to :
#Table(name = "schedules",
indexes = { #Index(name = "SCHEDULE_OWNER_GUID_INDEX", columnList = "report_owner_guid", unique = false)}
)
it still failed with the same error.