How to set collation for table attribute as utf8_bin in either annotation or application.properties file using Spring Boot - spring-boot

How do I set the collation for table attribute as utf8_bin in either annotation or the application.properties file using Spring Boot?
I have tried many ways but they did not work. Can you help?
I have tried the following ways.
First: Using #Column annotation like this:
#Column(name = "user_id",columnDefinition="VARCHAR(255) COLLATE utf8_bin")
Second:
#Column(columnDefinition="VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_bin")
Third: Using application.properties file
spring.jpa.properties.hibernate.connection.characterEncoding=utf-8
spring.jpa.properties.hibernate.connection.CharSet=utf-8
spring.jpa.properties.hibernate.connection.useUnicode=true
spring.jpa.properties.hibernate.connection.collationConnection=utf8_bin
Fourth:
spring.datasource.url =jdbc:mysql://localhost:3306/iot_schema?createDatabaseIfNotExist=true&useUnicode=true&connectionCollation=utf8_bin&characterSetResults=utf8

Here's a solution inspired by answer to similar question: Set Table character-set/collation using Hibernate Dialect?
Extend the preferred MySQL dialect and override its getTableTypeString() method like this:
public class MySQLCustomDialect extends MySQL8Dialect {
#Override
public String getTableTypeString() {
return " ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin";
}
}
Set that class to be used in application.properties:
spring.jpa.properties.hibernate.dialect=my.package.MySQLCustomDialect
Here's the generated SQL query:
create table test_table (
...
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin

Related

insert data from sql file H2 database spring boot

I'm trying to insert data from sql file using h2 database and spring boot .
when I add the sql file into src/main/ressources, I succeeded to create the table and insert data into it.
but when I create a model class named Employee which refer to the table, I can create the tables but there is no rows inserted .
SQL file:
CREATE TABLE employees ( id INT AUTO_INCREMENT PRIMARY KEY,
first_name VARCHAR(250) NOT NULL, last_name VARCHAR(250) NOT NULL,
mail VARCHAR(250) NOT NULL, password VARCHAR(250) NOT NULL );
INSERT INTO employees (first_name, last_name, mail, password) VALUES
('Laurent', 'GINA', 'laurentgina#mail.com', 'laurent');
model:
import javax.persistence.Column; import javax.persistence.Entity;
import javax.persistence.GeneratedValue; import
javax.persistence.GenerationType; import javax.persistence.Id; import
javax.persistence.Table;
import lombok.Data;
#Data #Entity #Table(name = "employees")
public class Employee {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name="first_name")
private String first_name;
#Column(name="last_name")
private String last_name;
private String mail;
private String password;
}
application.properties
#Global configuration
spring.application.name=api
#Tomcat configuration
server.port=9000
#Log level configuration
logging.level.root=ERROR
logging.level.com.openclassrooms=INFO
logging.level.org.springframework.boot.autoconfigure.h2=INFO
logging.level.org.springframework.boot.web.embedded.tomcat=INFO
#H2 Configuration
spring.h2.console.enabled=true
There are many ways to initialize the database with spring boot:
Initialize a Database Using JPA
Initialize a Database Using Hibernate
Initialize a Database using basic SQL scripts
For your information: you should separate the schema from the data,
so you should have 2 basic scripts:
schema.sql : In this file you can put your instruction to create table.
CREATE TABLE employees ( id INT AUTO_INCREMENT PRIMARY KEY,first_name VARCHAR(250) NOT NULL, last_name VARCHAR(250) NOT NULL,mail VARCHAR(250) NOT NULL, password VARCHAR(250) NOT NULL );
data.sql : In this file you can put your instruction to insert data
INSERT INTO employees (first_name, last_name, mail, password) VALUES ('Laurent', 'GINA', 'laurentgina#mail.com', 'laurent');
if you choose to use the schema.sql : you shoud use this property spring.jpa.hibernate.ddl-auto=none.
otherwise (Hibernate) you shoud use this property spring.jpa.hibernate.ddl-auto=create-drop
For your information:
Note: spring boot version <= 2.4.x you can choose Hibernate to create the schema or use schema.sql, but you cannot do both.
Note: spring boot version >= 2.5.x if you want script-based DataSource initialization to be able to build upon the schema creation performed by Hibernate, set spring.jpa.defer-datasource-initialization to true.
Be careful it depends on which version you choose!
There are a few things that could be causing this issue.
First, make sure that your SQL file is located in the src/main/resources directory. If it is not, then the file will not be picked up by Spring Boot.
Next, check the permissions on the file. If the file is not readable by the application, then it will not be able to load the data.
Finally, make sure that the SQL file is formatted correctly. If there are any syntax errors in the file, then Spring Boot will not be able to load the data.

The stored procedure call with cursors throws invalid column name exception

We have a Spring Boot application where we need to connect to Oracle DB and fetch data via stored procedures. Each of our stored procedure has REF_CURSOR as OUT parameters. I am trying the same using #NamedStoredProcedureQuery and #Entity annotations. We are using ojdbc14.jar in pom.xml and Oracle12cDialect in application.properties file. I get the exception Invalid Column Name while executing my piece of code. Also in the entity class I had to introduce a field with annotation #Id, although there is no such field being returned by the REF_CURSOR of my stored procedure. Can this be a problem? Also not defining #Id field is not an option since Hibernate throws an exception then. Any hints would be highly appreciated.
Implementation and Problem is very similar to the question
Invalid column name exception when calling an Oracle stored procedure with ref_cursor through JPA 2.1
But no answer is posted there
The simple example how you can achieve it:
The database schema.
create table MY_PATIENT
(
PAT_RECID number,
PAT_NAME varchar2(100),
constraint PAT_PK primary key(PAT_RECID)
);
create table MY_ORDER
(
ORD_RECID number,
ORD_CODE varchar2(15),
ORD_PATID number,
constraint ORD_PK primary key(ORD_RECID),
constraint ORD_PAT_FK foreign key(ORD_PATID) references MY_PATIENT(PAT_RECID),
constraint ORD_CODE_UNIQUE unique (ORD_CODE)
);
CREATE OR REPLACE PROCEDURE fetch_patient_orders(
patientId IN NUMBER,
patientOrders OUT SYS_REFCURSOR)
AS
BEGIN
OPEN patientOrders FOR
SELECT *
FROM MY_ORDER
WHERE ORD_PATID = patientId;
END;
The entity definition.
#NamedStoredProcedureQueries(
#NamedStoredProcedureQuery(
name = "fetch_patient_orders",
procedureName = "fetch_patient_orders",
resultClasses = Order.class,
parameters = {
#StoredProcedureParameter(
name = "patientId",
type = Long.class,
mode = ParameterMode.IN
),
#StoredProcedureParameter(
name = "patientOrders",
type = Class.class,
mode = ParameterMode.REF_CURSOR
)
}
)
)
#Entity
#Table(name = "MY_ORDER")
public class Order
{
#Id
#Column(name = "ORD_RECID")
private Long id;
#Column(name = "ORD_CODE")
private String code;
#ManyToOne
#JoinColumn(name = "ORD_PATID")
private Patient patient;
}
And usage:
List<Order> orders = session.createNamedStoredProcedureQuery("fetch_patient_orders")
.setParameter("patientId", 2L)
.getResultList();
It was tested with hibernate 5.4.12.Final, ojdbc8.jar, Oracle12cDialect.
See also the hibernate documentation.
The described above approach will work in a pure hibernate application, but not in spring boot app.
According to the spring boot documentation:
Connection to a Production Database
Production database connections can also be auto-configured by using a pooling DataSource. Spring Boot uses the following algorithm for choosing a specific implementation:
We prefer HikariCP for its performance and concurrency. If HikariCP is available, we always choose it.
Otherwise, if the Tomcat pooling DataSource is available, we use it.
If neither HikariCP nor the Tomcat pooling datasource are available and if Commons DBCP2 is available, we use it.
If you use the spring-boot-starter-jdbc or spring-boot-starter-data-jpa “starters”, you automatically get a dependency to HikariCP.
You can bypass that algorithm completely and specify the connection pool to use by setting the spring.datasource.type property.
So, spring boot uses HikariCP JDBC connection pool by default. And it looks like it has a problem with REF_CURSOR parameter registering:
o.h.r.j.i.ResourceRegistryStandardImpl : Registering statement [HikariProxyCallableStatement#770201936 wrapping oracle.jdbc.driver.OracleCallableStatementWrapper#528a6369]
o.h.type.descriptor.sql.BasicBinder : binding parameter [patientId] as [BIGINT] - [2]
o.h.s.i.AbstractServiceRegistryImpl : Initializing service [role=org.hibernate.engine.jdbc.cursor.spi.RefCursorSupport]
o.h.engine.jdbc.spi.SqlExceptionHelper : Error registering REF_CURSOR parameter [patientOrders] [n/a]
When I use the oracle specific data source pool in the application.properties:
# com.zaxxer.hikari.HikariDataSource (default value)
spring.datasource.type=oracle.jdbc.pool.OracleDataSource
all work fine.

How to select InnoDB or XtraDB as storage engine in MariaDB in Spring Boot 2 JPA application

I am developing a new application using Spring Boot 2.0.0.M6 and Spring Data JPA. I am using MariaDB v10.
Below is my dev properties file.
spring.jpa.hibernate.ddl-auto=create-drop
spring.datasource.url=jdbc:mariadb://localhost:3306/testdb
spring.datasource.username=user
spring.datasource.password=
spring.jpa.show-sql=true
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
org.hibernate.dialect.Dialect=MariaDB53Dialect
spring.jooq.sql-dialect=MariaDB53Dialect
I get output:
Hibernate: create table hibernate_sequence (next_val bigint) engine=MyISAM
I am not able to change the storage engine. All the tables are being created using storage engine MyISAM.
I am able to create tables manually using other storage engines. But for some reason Spring or Hibernate falls back to MyISAM engine only.
With pure Hibernate-Java application, Hibernate uses InnoDB as default.
INFO: HHH000412: Hibernate Core {5.2.11.Final}
Hibernate: create table hibernate_sequence (next_val bigint) engine=InnoDB
Is there any way to override Database storage engine from the Spring Boot properties?
As described in Spring Boot's documentation, all properties prefixed with spring.jpa.properties are passed through to the underlying JPA provider (Hibernate in this case) with the prefix removed.
The Hibernate property to configure the dialect is hibernate.dialect and its value should be the fully qualified class name of the dialect that you want to use. In this case that's org.hibernate.dialect.MariaDB53Dialect.
Putting the above together, you could set the following property in your application.properties:
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MariaDB53Dialect
With this in place your Spring Boot-based application uses the MariaDB dialect:
2017-11-09 14:18:17.557 INFO 69955 --- [ost-startStop-1] org.hibernate.dialect.Dialect : HHH000400: Using dialect: org.hibernate.dialect.MariaDB53Dialect
With Hibernate 5.2.12, if I run the MySQLStoredProcedureTest while setting the dialect to MariaDB:
#RequiresDialect(MariaDB53Dialect.class)
public class MySQLStoredProcedureTest
extends BaseEntityManagerFunctionalTestCase {
#Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Person.class,
Phone.class,
};
}
...
}
The Post entity is mapped as follows:
#Entity
public class Person {
#Id
#GeneratedValue
private Long id;
private String name;
private String nickName;
private String address;
#Temporal(TemporalType.TIMESTAMP )
private Date createdOn;
#OneToMany(mappedBy = "person", cascade = CascadeType.ALL)
#OrderColumn(name = "order_id")
private List<Phone> phones = new ArrayList<>();
#Version
private int version;
//Getters and setter omitted for brevity
}
And, when I run the test on MariaDB, Hibernate generates the following schema:
create table Person (
id bigint not null,
address varchar(255),
createdOn datetime(6),
name varchar(255),
nickName varchar(255),
version integer not null,
primary key (id)
) engine=InnoDB
That's because MariaDB53Dialect extends the MariaDBDialect which uses the InnoDBStorageEngine:
public class MariaDBDialect extends MySQL5Dialect {
public MariaDBDialect() {
super();
}
public boolean supportsRowValueConstructorSyntaxInInList() {
return true;
}
#Override
protected MySQLStorageEngine getDefaultMySQLStorageEngine() {
return InnoDBStorageEngine.INSTANCE;
}
}
So, it's impossible to get MyISAM with MariaDB53Dialect when generating the schema with hbm2ddl.
However, you should only be using hbm2ddl to generate the initial script. In a production environment, you should use a tool like FlywayDB.
We actually wrote this in the Hibernate User Guide:
Although the automatic schema generation is very useful for testing
and prototyping purposes, in a production environment, it’s much more
flexible to manage the schema using incremental migration scripts.
We changed this in Hibernate 5.2.8, so I suppose you are using an older version instead, otherwise, there is no explanation why you'd see MyISAM in your hbm2ddl auto-generated schema.
Check the dependencies using:
mvn dependency:tree
and make sure you are really using Hibernate 5.2.12.

Sequence "HIBERNATE_SEQUENCE" not found; SQL statement

In my spring mvc app, i have the following object. I am trying to make a visual of data using devtool in my app.
#Entity
#Data
public class ConsultationRequest {
#Id
#GeneratedValue
private Long id;
private String name;
private String email;
private String purpose;
private String programme;
private int year;
private String language;
private String comments;
#Enumerated(EnumType.STRING)
private ConsultationStatus status;
}
Then i used the jpa to make the entity:
#Repository
public interface ConsultationRequestRepository extends JpaRepository<ConsultationRequest, Long> {
}
The problem is when i load my application, i face with 2 errors:
Unsuccessful: drop sequence hibernate_sequence
[36morg.hibernate.tool.hbm2ddl.SchemaExport Sequence "HIBERNATE_SEQUENCE" not found; SQL statement:
Then when i open the
http://localhost:8080/h2-console/
I cannot see the table.
It seems that the in the boot process, table is not made.
Update your code as below:
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
As you have not specified a sequence table name, hibernate will look for a sequence table named as hibernate_sequence and use it as default.
For Oracle/Postgres, increment fields used are sequence tables.
In MySql, there are increment fields that automatically increment.
If someone is getting this error with Spring Boot testing(with H2) , we have to use following in the application.properties(or whatever profile we are using) :
spring.jpa.hibernate.ddl-auto=create-drop
Setting the following property in the properties file helped me solve the hibernate_sequence problem for hibernate 5.4v
spring:
jpa:
hibernate:
use-new-id-generator-mappings: false
Check persistence.xml
property name="hibernate.hbm2ddl.auto" value="create"
not hdm2ddl
This worked in my case.
SQL operation ERROR when start app spring-boot.
I added the setting in spring properties and solved: in the spring:jpa.hibernate.ddl-auto= create-drop to hibernate be able create or drop table automatically.
If you use a 2nd cache with liquidbase, you have to add the sequence in the changelog like this:
<changeSet author="liquibase-docs"
id="createSequence-example">
<createSequence catalogName="cat" cycle="false"
incrementBy="1" ordered="true" schemaName="public"
sequenceName="hibernate_sequence" startValue="0" />
</changeSet>
For spring-boot 2.7.x and h2 2.x you need to add MODE=LEGACY; in the database connection:
example application.yml:
spring:
datasource:
url: jdbc:h2:mem:test;MODE=LEGACY;
exemple application.properties:
spring.datasource.url=jdbc:h2:mem:test;MODE=LEGACY;
For Mysql:
the auto-increment is not added, modify ur table:
ALTER TABLE table_name MODIFY COLUMN id BIGINT AUTO_INCREMENT=1

H2 JdbcSQLException: "Table not found" with camelcase table & entity name

Using Spring Boot, with Spring Data JPA and H2 in-memory database (in PostgreSQL mode if it makes a difference).
I have a table & entity class named ContentBlock, yet H2 is complaining about missing CONTENT_BLOCK table, when I do a findAll() or findOne():
org.h2.jdbc.JdbcSQLException: Table "CONTENT_BLOCK" not found
I'm not sure if uppercase/camelcase makes a difference, but where does the underscore in CONTENT_BLOCK come from?
In my schema definition:
CREATE TABLE ContentBlock (
id BIGSERIAL PRIMARY KEY,
content TEXT
-- etc
);
And in the entity class:
#Entity
#Table(name = "ContentBlock")
public class ContentBlock {
// ...
}
(Of course I first tried without #Table annotation, as the class name exactly matches the table name.)
With my other tables/entities, with names like Asset, there are no problems, and I don't need to explicitly specify the table name on Java side:
#Entity
public class Asset {
// ...
}
In my setup, the H2 datasource is explicitly defined like this:
#Bean
public DataSource devDataSource() {
return new EmbeddedDatabaseBuilder()
.generateUniqueName(true)
.setType(EmbeddedDatabaseType.H2)
.setScriptEncoding("UTF-8")
.ignoreFailedDrops(true)
.addScripts("database/init.sql", "database/schema.sql", "database/test_data.sql")
.build();
}
(Contents of init.sql is SET MODE PostgreSQL;)
As workaround, I just renamed the ContentBlock table to Block in schema.sql, with #Table(name = "Block") in the Java class which I still call ContentBlock.
But this is weird, surely you can map a table with camelcase name to an entity somehow?
By default Spring Boot uses SpringNamingStrategy. It extends org.hibernate.cfg.ImprovedNamingStrategy from Hibernate 4. ImprovedNamingStrategy generates underscores in table names.
To map a table with camel case name to an entity you can use org.hibernate.cfg.EJB3NamingStrategy or implement your own.
An example of set a name strategy using properties
spring.jpa.hibernate.naming_strategy=org.hibernate.cfg.EJB3NamingStrategy

Resources