Spring Boot migration script syntax problems due to H2 version update - java

I've updated the version of H2 database (used only for testing) from 1.4.196 to 2.1.210 and the migration scripts now fails due to syntax errors.
Migration scripts are 2 types - pre (pure SQL) and post liquibase (XML based) introduction to the project.
Here is what is defiend in the test yaml config:
spring:
datasource:
url: jdbc:h2:mem:testdb;Mode=Oracle
platform: h2 // changed to sql.init.platform: h2
jpa:
hibernate:
ddl-auto: validate
I've tryed to add spring.datasource.driverClassName=org.h2.Driver and spring.jpa.database-platform=org.hibernate.dialect.H2Dialect proerpties, but that did not do the trick.
I very mych tink it is due to some configuration difference between the two version 1.4 and 2.1
COMMENT REPLY
The liquibase changeset that causes the error:
<changeSet id="20191112130000-1" author="zdravko">
<dropColumn tableName="NEWS_CONTENT" columnName="DAY"/>
</changeSet>
Error itself:
Reason: liquibase.exception.DatabaseException: Syntax error in SQL statement "ALTER TABLE PUBLIC.NEWS_CONTENT DROP COLUMN [*]DAY"; expected "identifier"; SQL statement:
ALTER TABLE PUBLIC.NEWS_CONTENT DROP COLUMN DAY [42001-210] [Failed SQL: (42001) ALTER TABLE PUBLIC.NEWS_CONTENT DROP COLUMN DAY]

DAY is a keyword in H2 and it is also a reserved word in the SQL Standard (even in completely outdated SQL-92 and in all newer versions), it means it cannot be used as unquoted identifier.
Liquibase supports H2 2.x.y and its keywords since the version 4.7.0, if you use some older version, you need to upgrade. If you use this or newer version, something is going wrong.
In that case you can check objectQuotingStrategy and use QUOTE_ALL_OBJECTS:
https://docs.liquibase.com/commands/config-ref/objectquotingstrategy.html
Also you can add ;NON_KEYWORDS=DAY to JDBC URL of H2, but it would be better to avoid it if you can, this setting isn't very reliable and should be used only when you can't do anything else.

Related

Does the combination of Spring Boot and H2 honor JPA-property javax.persistence.schema-generation.create-database-schemas?

I have a Spring Boot app that is configured to use JPA and its default-provider Hibernate. The app, in environment Non-Local, expects, and, through Oracle, is configured to connect to, an existing SQL DB, while the app, in environment Local, must, through H2, create it...embedded, in-memory, and private.
The DB utilizes one schema, as seen in this entity:
#Entity #Table(schema="foo",name="transactions")
Environment Local must, and is configured to, create (after first dropping any existing) tables and their schema. It is the creation, and dropping, of the latter - schema - that exhibits a problem:
Hibernate: drop table if exists foo.foo_transactions CASCADE
org.hibernate.tool.schema.spi.CommandAcceptanceException: Error executing DDL "drop table if exists foo.foo_transactions CASCADE " via JDBC Statement
Caused by: org.h2.jdbc.JdbcSQLSyntaxErrorException: Schema "FOO" not found; SQL statement: drop table if exists foo.foo_transactions CASCADE [90079-200]
...and a similar one (Schema not found) for CREATE.
So as to ensure no overlap, in the classpath, between H2 and Oracle, I, in the POM, separate them by build-profile (local and non-local):
<profile><id>local</id>...<dependencies><dependency>H2</dependency></dependencies>
<profile><id>non-local</id>...<dependences><dependency>Oracle</dependency></dependencies>
My relevant configuration of Spring Boot is:
# file application.properties
spring.datasource.url=jdbc:oracle:thin...
# file application-local.properties
# blankness of following value masks default configuration, in application.properties
spring.datasource.url=
spring.jpa.properties.javax.persistence.schema-generation.create-database-schemas=true
That JPA-property, javax.persistence.schema-generation.create-database-schemas, is supposed to do EXACTLY what I desire: create, in addition to tables, any internal schema. Yet, as shown in the aforementioned errors, it doesn't work!
Does H2, or the combination of H2, JPA-provider Hibernate, and Spring Boot, not honor it??? Or, have I miscoded/misconfigured something?
I don't want to get bogged down in the following (as the preceding question is my main concern), but for full disclosure...
P.S. If I remove the schema and build and run locally, everything works fine. But, the non-local (Production) flavor mandates that schema, so I must comply, and wish to do so also locally.
P.P.S. I am, indeed, aware of H2's directive 'INIT=CREATE SCHEMA IF NOT EXISTS foo' (to be applied to the datasource URL), and it, if used, does alleviate the problem. However, if I do use it (thereby having to explicitly supply a url, thereby conceding Spring Boot's very nice and full auto-configuration [of H2]), it causes another problem, which I need to avoid:
2022-08-29 15:43:27.494 WARN 15288 --- [on(5)-127.0.0.1] o.s.b.f.support.DisposableBeanAdapter: Invocation of destroy method failed on bean with name 'inMemoryDatabaseShutdownExecutor': org.h2.jdbc.JdbcSQLNonTransientConnectionException: Database is already closed (to disable automatic closing at VM shutdown, add ";DB_CLOSE_ON_EXIT=FALSE" to the db URL) [90121-200]
P.P.P.S. Neither that recommendation (...;DB_CLOSE_ON_EXIT=FALSE) nor its sister (...;DB_CLOSE_DELAY=-1) nor their combination alleviate that problem (attempt to close an already-closed DB).
I've now figured it out - my relevant JPA-property, at least as far as Hibernate is concerned, was slightly wrong...
Whereas I used (as it had once worked for me in EclipseLink, lazily leading me to believe that it was generic to all JPA-providers)
javax.persistence.schema-generation.create-database-schemas
I should have used this
javax.persistence.create-database-schemas

Cannot populate database with liquibase

I'm building a small spring-boot application. I'm using spring-data-jpa to create the database schema and liquibase to populate it with test data.
application.properties:
spring.datasource.url=jdbc:postgresql://localhost:5432/book-db
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.username=admin
spring.datasource.password=lTIDDYz3n3jD3BeYaAJz
spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=create
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
According to the documentation no configuration for liquibase is required if I have a gradle dependency and master changeLog under the default path.
db.changelog-master.yaml:
databaseChangeLog:
- changeSet:
id: 1
author: jb
changes:
- sqlFile:
path: db/migration/insert-books.sql
insert-books.sql:
--liquibase formatted sql
--changeset admin:1
delete from book;
insert into book (id, title)
values (nextval('seq'), 'Functional Programming for Mortals');
commit;
I have tried it with and without commit. The tables databasechangelog and databasechangelog are created successfully and contain the migration (insert-books).
The migration goes through, because if I add an invalid insert (to some table that does not exist), I get the exception:
ERROR: relation "xxx" does not exist
How to populate the database with data in insert-books.sql script using liquibase?
Don't use both, liquibase and JPA, to manage the DB structure. If you want to use liquibase, set JPA (Hibernate) to just validate the schema and manage the schema within liquibase.
spring.jpa.hibernate.ddl-auto=validate
The problem with your solution is in the order of operations. When your application starts, it first runs liquibase, which inserts the data, then JPA is started and the schema is created from scratch.
Try dropping the schema before running the app, I bet the migration (liquibase) will fail.
Liquibase has to be in charge of the schema, there is a way to add liquibase to an existing database, but it again makes liquibase the owner of the schema:
Using liquibase on the existing database

Difference between spring.jpa.hibernate.hbm2ddl and spring.jpa.hibernate.ddl

What is the difference between spring.jpa.hibernate.hbm2ddl and spring.jpa.hibernate.ddl?
I have found in this question: What are the possible values of spring.datasource.initialization-mode? that OP is using both in properties, however it seems like the origin of hbm2ddl is hibernate directly not Spring Data Jpa.
Nevertheless, reading the answer from another OP, it looks like pass-through only.
However in our commercial project with mariadb, when we do not close our spring boot application gracefully with spring.jpa.hibernate.hbm2ddl.auto=create, when the application is run again, it deletes old data and creates everything from scratch. On the other hand with spring.jpa.hibernate.ddl.auto=create every second run (after no graceful application shutdown) causes key constraint exceptions (DB is not being dropper before creation)
From this Link
By default, JPA databases are automatically created only if you use an embedded database (H2, HSQL, or Derby).
You can explicitly configure JPA settings by using spring.jpa.* properties. For example, to create and drop tables you can add the following line to your application.properties:
spring.jpa.hibernate.ddl-auto=create-drop
Hibernate’s own internal property name for this (if you happen to remember it better) is hibernate.hbm2ddl.auto.
From this Link
spring.jpa.hibernate.ddl-auto This is actually a shortcut for the "hibernate.hbm2ddl.auto" property.
Defaults to "create-drop" when using an embedded database and no schema manager was detected. Otherwise, defaults to "none".
From this Link
Spring Boot can automatically create the schema (DDL scripts) of your DataSource and initialize it (DML scripts).
It loads SQL from the standard root classpath locations: schema.sql and data.sql, respectively.
In addition, Spring Boot processes the schema-${platform}.sql and data-${platform}.sql files (if present), where platform is the value of spring.datasource.platform.
This allows you to switch to database-specific scripts if necessary. For example, you might choose to set it to the vendor name of the database (hsqldb, h2, oracle, mysql, postgresql, and so on).

MYSQL schema keeps getting dropped

I have a spring-boot application running using docker on a Ubuntu server, with MYSQL DB running on the server itself not in a docker image. The schema I am using for the spring-boot app keeps getting deleted randomly and I always have to restore it using backed up dump. I cannot determine the reason behind this weird issue. I tried googling this issue and I could not find anything about this issue.
What could possibly cause a DB to be dropped?!?
It is random like once a week! may be more than that. I cannot even track that.
My application.yml has this:
spring:
datasource:
url: jdbc:mysql://localhost/schemaname?useSSL=false
username: username
password: password
tomcat:
validation-query: SELECT 1
test-on-borrow: true
jpa:
show-sql: true
hibernate:
ddl-auto: update
properties:
hibernate:
dialect: org.hibernate.dialect.MySQL5Dialect
You shouldn't use ddl-auto for a production app.
Quote from Java Persistence with Hibernate.
WARNING: We've seen Hibernate users trying to use SchemaUpdate to
update the schema of a production database automatically. This can
quickly end in disaster and won't be allowed by your DBA.
This option is intended to be used only for development.
If you don't want to manually run your SQL migrations, you should consider tools like flyway or liquibase.

Not specifying Hibernate dialect

I am new to Hibernate. While reading Hibernate, I came across the Dialect property. Whatever database we will use in our application, we need to set dialect related to that database and Hibernate will generate appropriate query related to that database.
Just want to know if it is the mandatory property to be set? If it is not and not specified in the hibernate.cfg.xml file, then how will Hibernate generate the SQL queries i.e. which database compliant SQL query will be generated?
No it is not mandatory as per documentation
http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/session-configuration.html#configuration-optional-dialects
, I had not try with the same. While your answer of others points I will also like to get answer from some good experienced here. :)
I think it's not mandatory but it's a good practice to set it in your hibernate.cfg.xml as
<property name="dialect">your dialect</property>
posible values:
DB2 org.hibernate.dialect.DB2Dialect
DB2 AS/400 org.hibernate.dialect.DB2400Dialect
DB2 OS390 org.hibernate.dialect.DB2390Dialect
PostgreSQL org.hibernate.dialect.PostgreSQLDialect
MySQL org.hibernate.dialect.MySQLDialect
MySQL with InnoDB org.hibernate.dialect.MySQLInnoDBDialect
MySQL with MyISAM org.hibernate.dialect.MySQLMyISAMDialect
Oracle (any version) org.hibernate.dialect.OracleDialect
Oracle 9i org.hibernate.dialect.Oracle9iDialect
Oracle 10g org.hibernate.dialect.Oracle10gDialect
Sybase org.hibernate.dialect.SybaseDialect
Sybase Anywhere org.hibernate.dialect.SybaseAnywhereDialect
Microsoft SQL Server org.hibernate.dialect.SQLServerDialect
SAP DB org.hibernate.dialect.SAPDBDialect
Informix org.hibernate.dialect.InformixDialect
HypersonicSQL org.hibernate.dialect.HSQLDialect
Ingres org.hibernate.dialect.IngresDialect
Progress org.hibernate.dialect.ProgressDialect
Mckoi SQL org.hibernate.dialect.MckoiDialect
Interbase org.hibernate.dialect.InterbaseDialect
Pointbase org.hibernate.dialect.PointbaseDialect
FrontBase org.hibernate.dialect.FrontbaseDialect
Firebird org.hibernate.dialect.FirebirdDialect
This is why...
You do not need the dialect property if you set up your database connection with hibernate configuration because hibernate does it for you:
hibernate.connection.driver
hibernate.connection.url
user configs and etc...
However, if you set up the connection with regular data source code and use hibernate you need to specify the hibernate.dialect property, because then the connection will not know what hibernate dialect.
dataSource.setdriverClassName
datasource.setUrl...
password configs and etc...

Categories

Resources