Spring boot auto create table in mysql fail - mysql

I meet an following error, can not figure out. I suppose I can use Spring boot connect to mysql db. And it can create table auto. And this is connect to a docker container, not sure if this matters
spring.datasource.poolName=spring-jpa
spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=UTC&createDatabaseIfNotExist=true
spring.datasource.username=root
spring.datasource.password=1q2w3e4R
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
server.session.timeout=8640000
spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.hibernate.use-new-id-generator-mappings=false
The error is:
org.hibernate.tool.schema.spi.CommandAcceptanceException: Error executing DDL "create table subscription (id bigint not null auto_increment, consent bit, email varchar(255), first_name varchar(255), gender varchar(255), newsletter_id integer, primary key (id)) type=MyISAM" via JDBC Statement
at org.hibernate.tool.schema.internal.exec.GenerationTargetToDatabase.accept(GenerationTargetToDatabase.java:67) ~[hibernate-core-5.4.27.Final.jar:5.4.27.Final]
....
Caused by: java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'type=MyISAM' at line 1
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:120) ~[mysql-connector-java-8.0.22.jar:8.0.22]
....
This is the subscription entity class:
import lombok.Getter;
import lombok.Setter;
import javax.annotation.Generated;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
#Getter
#Setter
#Entity
#Table
public class Subscription {
#Id
#GeneratedValue(strategy= GenerationType.AUTO)
private long id;
#Column
private String email;
#Column
private String firstName;
#Enumerated(EnumType.STRING)
#Column
private Gender gender;
#Column
private boolean consent;
#Column
private int newsletterId;
}
This is the Gender class
public enum Gender {
MALE, FEMALE
}

This is caused because type=MyISAM was deprecated since MySQL 4.x.
The property
spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect
Implies hibernate to use the initial Dialect.
Changing it to
spring.jpa.database-platform=org.hibernate.dialect.MySQL5Dialect
Will generate SQL for MySQL 5.0+, with engine=MyISAM instead of type=MyISAM , similarly based on your MySQL version, change your dialect.
Example if you have MySQL 7 use org.hibernate.dialect.MySQL7Dialect

Related

How to write case sensitive column pojo in spring data JPA

We have a product which runs in mysql, oracle and sqlserver. We are writing one custom module which needs to access a customer table and retrieve data. We are writing it using spring data JPA.
For this we have Customer pojo as below and created a Repository.
Note that the columns are case sensitive. The POJO that works for oracle is as below,
import lombok.*;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Getter
#Setter
#ToString
#Table(name="customer")
public class Customer{
#Id
#Column(name="\"cxCifID\"")
private String cxCifID;
#Column(name="\"hostCustId\"")
private String hostCustId;
#Column(name="\"custMobile1\"")
private String custMobile1;
#Column(name="\"custEmail1\"")
private String custEmail1;
#Column(name="\"custName\"")
private String custName;
#Column(name="\"custMotherTongue\"")
private String custMotherTongue;
}
The same will not work in mysql and will give the error as below,
"Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '"cxCifID" as cxcifid1_0_, customer0_."custEmail1" as custemai2_0_, customer0_."c' at line 1"
If i use the below POJO for mysql it works without error,
import lombok.*;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Getter
#Setter
#ToString
#Table(name="customer")
public class Customer{
#Id
#Column(name="cxCifID")
private String cxCifID;
#Column(name="hostCustId")
private String hostCustId;
#Column(name="custMobile1")
private String custMobile1;
#Column(name="custEmail1")
private String custEmail1;
#Column(name="custName")
private String custName;
#Column(name="custMotherTongue")
private String custMotherTongue;
}
The question is how can I create and use a single POJO which can be used in both the databases or in fact in all the databases? Please help

Getting null at created_at and updated_at when inserting record into MySQL database using Spring Boot JPA Auditing

I have tried to create a REST APIs in Kotlin and Spring Boot and my Country entity has auditing fields created_at and updated_at which indicate the record creation and last modified date and time respectively. The Spring Boot application is simple and I have not added any configuration files. Here is the implementation of the application:
/model/Country.kt
(EDIT: I have also tried to add #EntityListeners(AuditingEntityListener::class) but this also didn't work.)
package com.example.example.model
import jakarta.persistence.Column
import jakarta.persistence.Entity
import jakarta.persistence.EntityListeners
import jakarta.persistence.GeneratedValue
import jakarta.persistence.GenerationType
import jakarta.persistence.Id
import jakarta.persistence.Table
import org.springframework.data.annotation.CreatedDate
import org.springframework.data.annotation.LastModifiedDate
import org.springframework.data.jpa.domain.support.AuditingEntityListener
import java.sql.Timestamp
#Entity
#Table(name = "country")
class Country (
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "country_id")
val id: Long,
val code: String,
val displayName: String,
#CreatedDate
#Column(name = "created_at", nullable = false, updatable = false)
val createdAt: Timestamp,
#LastModifiedDate
#Column(name = "updated_at", nullable = false)
val updatedAt: Timestamp
)
/controller/CountryCountroller.kt
package com.example.example.controller
import com.example.example.model.Country
import com.example.example.repository.CountryRepository
import jakarta.validation.Valid
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RestController
import org.springframework.http.MediaType
import org.springframework.web.bind.annotation.PostMapping
#RestController
class CountryController {
#Autowired
lateinit var countryRepository: CountryRepository
#PostMapping("/countries", consumes = arrayOf(MediaType.APPLICATION_JSON_VALUE))
fun createCountry(#Valid #RequestBody country: Country): Country {
return countryRepository.save(country);
}
}
ExampleApplication.kt
package com.example.example
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.data.jpa.repository.config.EnableJpaAuditing
#SpringBootApplication
#EnableJpaAuditing
class ExampleApplication
fun main(args: Array<String>) {
runApplication<ExampleApplication>(*args)
}
Schema of the Country table:
country_id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
code VARCHAR(3) NOT NULL,
displayName VARCHAR(40) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (country_id)
The request body is a json and the request header has include the correct Content-Type information:
{
"code": "FRA",
"displayName": "France"
}
After calling the API, the following record is inserted into the table:
{
"id": 1,
"code": "FRA",
"displayName": "France",
"createdAt": null,
"updatedAt": null
}
From my understanding, since my auditing does not include auditing the created and update user details, i.e. #CreatedBy and #LastModifiedBy, I do not have to create a new Auditable class and implement functions for the value of CreatedBy and LastModifiedBy fields. Also, I also expect that the annotations #CreatedDate and #LastModifiedDate would help me insert the record creation datetime and last updated datetime right before the record is being inserted into the table. I wonder if I have missed anything in my code which results in having null on both created_at and updated_at fields.
Also worth discussing about, I have tried to use #CreationTimestamp and #UpdateTimestamp annotations from Hibernate to replace #CreatedDate and #LastModifiedDate. This actually works with current datetime being populated in created_at and updated_at fields. However, I would like to use #CreatedDate and #LastModifiedDate instead, as #CreatedBy and #LastModifedBy annotations are also available which I might be using in the future. Even if I decide to go with the Hibernate annotations in the future, I would still like to understand and know why I am not able to use the JPA annotations.
Not sure if this is helpful but I do have
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL8Dialect
in application.properties file.
Have you tried this annotation? #EntityListeners(AuditingEntityListener.class)
#Entity
#Table(name = "country")
#EntityListeners(AuditingEntityListener::class)
class Country (
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "country_id")
val id: Long,
val code: String,
val displayName: String,
#CreatedDate
#Column(name = "created_at", nullable = false, updatable = false)
val createdAt: Timestamp,
#LastModifiedDate
#Column(name = "updated_at", nullable = false)
val updatedAt: Timestamp
)
I have found the answer to my question.
To use #CreatedDate and #LastModifiedDate, I have to use var instead of val to define the variables created_at and updated_at. Since I am new to Kotlin coming from Java, I did not realise the importance of choosing var and val. As stated in Kotlin Docs,
Read-only local variables are defined using the keyword val. They can
be assigned a value only once.
Variables that can be reassigned use the var keyword.
https://kotlinlang.org/docs/basic-syntax.html#variables
Since I have used val to define the two variables, they are both read-only and therefore JPA Auditing is not able to populate the time into the two fields, thus I am getting NULL on the two columns created_at and updated_at in the table.

Liquibase generate migration for geometry type (Spring boot with JPA/Hibernate)

I am writing a RESTfull API with Spring boot using Maven Liquibase to manage migrations alongside MySQL 8 for the database.
I have searched online (1,2, 3) but Liquibase is still generating "TINYBLOB" type in migrations instead of "POINT" or "GEOMETRY". Surprisingly, when I edit the migration file (i.e. changeSet and use "POINT", mvn liquibase:update still creates a TINYBLOB column on the database.
I have a typal JPA entity:
import org.locationtech.jts.geom.Point;
#Entity
class MyModel {
private Point location;
// more fields
I am using Liquibase version 4.3 and Hibernate version 5.4. For hibernate dialect, I am using org.hibernate.spatial.dialect.mysql.MySQL8SpatialDialect.
It appears to me that spatial types are not supposed by Liquibase... but that would be surprising. Any help would be greatly appreciated (all other data types are behaving as expected).
Ran into the same issue some time ago and ended up manually overwriting parts of the auto-generated migration file. Worked fine for MySQL 8.
<!-- Define the type-->
<property name="pointType" value="geometry" dbms="h2"/> <!-- Only relevant for in-memory integration tests-->
<property name="pointType" value="POINT" dbms="mysql, oracle, mssql, mariadb, postgresql"/>
<!-- Use the type on the column-->
<column name="location" type="${pointType}">
<constraints nullable="true" />
</column>
A simplified version of my Hibernate model.
package com.stackoverflow.sample.domain;
import com.vividsolutions.jts.geom.Point;
import org.hibernate.annotations.Type;
import org.springframework.data.elasticsearch.annotations.FieldType;
import javax.persistence.*;
import java.io.Serializable;
#Entity
#Table(name = "some_entity")
public class SomeEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#org.springframework.data.elasticsearch.annotations.Field(type = FieldType.Keyword)
private Long id;
#Type(type = "com.vividsolutions.jts.geom.Point")
#Column(name = "location", nullable = false, columnDefinition = "geometry")
private Point location;
}

SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet

Error: could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet
I am working on CRUD using JPA in Spring Boot; and trying to insert data in MySQL table using JSP pages.BUT after submitting the form, Table automatically deletes from MySQL Database....
I have gone through all tutorial by searching the Error. I am following this example
student.java
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.GenerationType;
#Entity
#Table(name="student")
public class Student
{
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="sname")
private String sname;
#Column(name="email")
private String email;
#Column(name="key1")
private String key1;
}
I expect that data should be enter into the mentioned table.
Whenever you face such type of error, while running Spring Boot + JPA CRUD Example, Just check once that you have defined ID field using AUTO_INCREMENT option or not.
I was having such type of Error because I did not require ID field. BUT Spring Boot Hibernate checks for Unique A_I ID field.
If you don't require ID field, then use and Your problem will be solved.

Spring boot/Hibernate fails creating table

I am getting this warn when I try to start my spring boot application:
2018-09-30 07:34:23.097 INFO 59360 --- [ main] org.hibernate.dialect.Dialect : HHH000400: Using dialect: org.hibernate.dialect.MySQL5Dialect
Hibernate: create table social_link (author_username varchar(255) not null, type varchar(255) not null, value varchar(255), primary key (author_username, type)) engine=MyISAM
2018-09-30 07:34:24.067 WARN 59360 --- [ main] o.h.t.s.i.ExceptionHandlerLoggedImpl : GenerationTarget encountered exception accepting command : Error executing DDL via JDBC Statement
Strange thing is that I can execute SQL statement via the SQL workbench without a problem and everything works fine after that.
Here is the entity that is responsible for that table:
#Entity
public class SocialLink {
#EmbeddedId
private SocialLinkKeyEmbeddable id = new SocialLinkKeyEmbeddable();
private String value;
#ManyToOne
#MapsId("author_username")
#JoinColumns({
#JoinColumn(name="author_username", referencedColumnName="author_username")
})
private Author author;
//Getters and setters
}
#Embeddable
public class SocialLinkKeyEmbeddable implements Serializable {
#Column(name = "author_username")
private String author_username;
private String type;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getAuthor_username() {
return author_username;
}
public void setAuthor_username(String author_username) {
this.author_username = author_username;
}
#Override
public int hashCode() {
return super.hashCode();
}
#Override
public boolean equals(Object obj) {
return super.equals(obj);
}
}
public interface SocialLinkRepository extends CrudRepository<SocialLink, SocialLinkKeyEmbeddable> {
}
The problem was with the length of the key. MySQL was pretending it is larger than 1000 bytes. It seems its a common problem with MyISAM storage engine.
In order to fix it I added:
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL55Dialect
to my applicaiton.properties and now the problem is fixed.
Was using this post as reference
#1071 - Specified key was too long; max key length is 1000 bytes
I had a similar problem caused by spring.jpa.database-platform property set to MySQL5Dialect.
Setting the property to MySQL8Dialect or removing it altogether (which enables auto-configuration) solved the problem.
I added a column length to my ID field to prevent the varchar(255) default.
#Entity
public class MyEntity {
#Id
#Column(length = 64)
private String id;
...
}
For those who are only using Hibernate (not Spring), edit the SQL dialect property from hibernate.cfg.xml file, located in src/main/resources folder.
<property name="dialect">org.hibernate.dialect.MySQL55Dialect</property>
Or remove the dialect property altogether.
Error-Caused by: java.sql.SQLSyntaxErrorException: Specified key was too long; max key length is 1000 bytes
operation perform- #Id private String userName;
Solution
step1-Go to mysql database--> step2-Go to schema setting -->step3-change Default charset to latin1--> step 4-Run on server your spring mvc program--> step 5- Finally Your table is created with #Id on String
I had the same issue. I resolved it by assigning char set to my database on creation:
CREATE DATABASE mydb DEFAULT CHARACTER SET utf8mb3;