Spring Boot migration to 2.5/2.6 from 2.1 - Issue with SQL Script DataSource Initialization order - spring-boot

I'm migrating my existing Spring Boot 2.1.x application to 2.6.x. I want the following behavior (for integration tests which uses embedded H2 database), which is working fine in 2.1.x (obviously) but I'm struggling to achieve the same behavior in 2.6.x.
Create DDL automatically (without any custom SQL script)
Execute custom DML (data.sql)
Initialize beans which implements InitializingBean and use data populated in step #2. Eg. Cache database table records for application to use later
Problem #1 (Solved)
2.6.x was no longer created DDL automatically by default. So adding below properties explicit seemed to be the solution.
spring.jpa.generate-ddl = true
spring.jpa.hibernate.ddl-auto = create-drop
This solves Step #1 with 2.6.x.
Problem #2 (Solved)
Starting from 2.5, Boot was running scripts in data.sql before Hibernate is initialized. Hence, data.sql was failing. To make Boot wait for DDL creation to complete, adding below property seemed to be the solution.
spring.jpa.defer-datasource-initialization = true
This solves *Step #2 with 2.6.x.
Problem #3 (Need solution)
Seemingly because of setting spring.jpa.defer-datasource-initialization to true, Step #3 is happening before Step #2 i.e. in 2.6.x. This cause beans to initialize with empty database tables e.g. Cache is populated with no entries. These are causing my tests to fail.
I played with properties like spring.jpa.defer-datasource-initialization, spring.data.jpa.repositories.bootstrap-mode and spring.sql.init.mode to workaround this without any luck.

Related

Spring Boot h2 initial data not creating table first

I am trying to create a small Spring Boot setup with a h2 database.
Now I have a weird problem, which I can't solve.
If I don't create an data.sql for initial data, the app starts fine and creates my entity tables.
If I create an data.sql for initial data and keep the existing table from previous step, everything works fine.
If I create an data.sql for initial data and remove my existing h2 file, I get the error that it can't import the data, because the table is missing.
How do I tell Spring to create my tables before importing the initial data?
This is covered in the release notes for Spring Boot 2.5:
By default, data.sql scripts are now run before Hibernate is initialized. This aligns the behavior of basic script-based initialization with that of Flyway and Liquibase. If you want to use data.sql to populate a schema created by Hibernate, set spring.jpa.defer-datasource-initialization to true. While mixing database initialization technologies is not recommended, this will also allow you to use a schema.sql script to build upon a Hibernate-created schema before it’s populated via data.sql.

Does Spring boot Support Fast fail on Liquibase

From Spring DB Initialization docs, Spring boot DB initialization using Spring JDBC supports
feature Fast fail - that means if there are any issues in DB init script or migration scripts then Spring boot Context initialization failed. as result Spring boot server won't start.
now can we have this functionality when we use Advanced DB Migration tool like liquibase?
Spring Docs Doesn't say anything about this in liquibase section. Does this feature only works in Spring JDBC initialization?
whenever there is an issue in your liquibase script, application won't start because datasource is not properly initialised.
You can specify the desired behaviour using 'Preconditions'.
On default, it actually will fail fast, but you can overwrite it (example using SQL syntax):
-- preconditions onFail:WARN onError:WARN
Available parameters:
HALT: Immediately halt the execution of the entire changelog. [DEFAULT]
CONTINUE: Skip over the changeset. Execution of the change set will be
attempted again on the next update. Continue with the changelog.
MARK_RAN: Skip over the changeset, but mark it as executed. Continue
with the changelog. WARN Output a warning and continue executing the
changeset / changelog as normal.
see https://docs.liquibase.com/concepts/advanced/preconditions.html

Automatically baselining a database with Flyway in a Spring Boot project

I'm trying to add Flyway to a Spring Boot project. In accordance with the instructions, I've created my initial DDL script and committed it to src/main/resources/db/migration/V1__base_version.sql.
If I run the baseline command, this will create the flyway_schema_history table and set the baseline version therein to 1.
While this works fine for my local database, I would like this to happen automatically on the other developers' local environments, UAT environment, etc.
I tried adding the following property to the Spring Boot config
spring:
flyway:
baseline-on-migrate: true
I expected this to do the same thing as the baseline command when the Spring Boot app starts up if the flyway_schema_history table does not exist, i.e. create this table and insert a row specifying the current schema version, but it didn't.
Is there a way to automatically baseline the database when the app starts up?

Disable hibernate hbm2ddl

I have a spring boot project that uses flyway to insert the database structure and it works perfectly except when testing. When I run my tests I can see that the flyway sql files are executed so that works however for some reason hibernate drops and creates the database structure. This only happens when in testing so it seams as if Spring is telling Hibernate to drop-create the schema when testing.
How can I completly disable the hbm2dll?
I can understand that this is the default behaviour however it this the right thing to do? Spring should never assume that someone wants hbm2dll eneabled when testing, that should be indicated by the user not spring.

How does spring.jpa.hibernate.ddl-auto property exactly work in Spring?

I was working on my Spring boot app project and noticed that, sometimes there is a connection time out error to my Database on another server(SQL Server).
This happens specially when I try to do some script migration with FlyWay but it works after several tries.
Then I noticed that I didn't specify spring.jpa.hibernate.ddl-auto in my properties file. I did some research and found that it is recommended to add
spring.jpa.hibernate.ddl-auto= create-drop in development.
And change it to: spring.jpa.hibernate.ddl-auto= none in production.
But I didn't actually understand how does it really work and how does hibernate generate database schema using create-drop or none value. Can you please explain technically how does it really work, and what are recommendations for using this property in development and on a production server.
Thank you
For the record, the spring.jpa.hibernate.ddl-auto property is Spring Data JPA specific and is their way to specify a value that will eventually be passed to Hibernate under the property it knows, hibernate.hbm2ddl.auto.
The values create, create-drop, validate, and update basically influence how the schema tool management will manipulate the database schema at startup.
For example, the update operation will query the JDBC driver's API to get the database metadata and then Hibernate compares the object model it creates based on reading your annotated classes or HBM XML mappings and will attempt to adjust the schema on-the-fly.
The update operation for example will attempt to add new columns, constraints, etc but will never remove a column or constraint that may have existed previously but no longer does as part of the object model from a prior run.
Typically in test case scenarios, you'll likely use create-drop so that you create your schema, your test case adds some mock data, you run your tests, and then during the test case cleanup, the schema objects are dropped, leaving an empty database.
In development, it's often common to see developers use update to automatically modify the schema to add new additions upon restart. But again understand, this does not remove a column or constraint that may exist from previous executions that is no longer necessary.
In production, it's often highly recommended you use none or simply don't specify this property. That is because it's common practice for DBAs to review migration scripts for database changes, particularly if your database is shared across multiple services and applications.
In Spring/Spring-Boot, SQL database can be initialized in different ways depending on what your stack is.
JPA has features for DDL generation, and these can be set up to run on startup against the database. This is controlled through two external properties:
spring.jpa.generate-ddl (boolean) switches the feature on and off and is vendor independent.
spring.jpa.hibernate.ddl-auto (enum) is a Hibernate feature that controls the behavior in a more fine-grained way. See below for more detail.
Hibernate property values are: create, update, create-drop, validate and none:
create – Hibernate first drops existing tables, then creates new tables
update – the object model created based on the mappings (annotations or XML) is compared with the existing schema, and then Hibernate updates the schema according to the diff. It never deletes the existing tables or columns even if they are no more required by the application
create-drop – similar to create, with the addition that Hibernate will drop the database after all operations are completed. Typically used for unit testing
validate – Hibernate only validates whether the tables and columns exist, otherwise it throws an exception
none – this value effectively turns off the DDL generation
Spring Boot internally defaults this parameter value to create-drop if no schema manager has been detected, otherwise none for all other cases.
"spring.jpa.hibernate.ddl-auto= create-drop" means that when the server is run, the database(table) instance is created. And whenever the server stops, the database table instance is droped.
Also depending on spring.jpa.hibernate.ddl-auto the DML files feature is enabled
DDL and DML
It is worth to understand the difference between them.
Data Definition Language(DDL) - related to database schema creating
Data Manipulation Language(DML) - related to importing data
Basically there are 3 types of database schema creating(DDL) and importing data(DML):
Using Hibernate
Using Spring JDBC SQL scripts
Using high level tools like Flyway/Liquibase
This topic covers Hibernate and it's DDL (first option), but it is worth to mention Hibernate DML files feature that enabled if spring.jpa.hibernate.ddl-auto is create or create-drop
That means import.sql in the root of the classpath will be executed on startup by Hibernate. This can be useful for demos and for testing if you are careful, but probably not something you want to be on the classpath in production. It is a Hibernate feature (nothing to do with Spring).
Also here is a table that explains spring.jpa.hibernate.ddl-auto and whether the import.sql can be used depending on spring.jpa.hibernate.ddl-auto value specified:
spring.jpa.hibernate.ddl-auto
Create schema from entities
import.sql
create
true
true
update
update schema from entities
false
create-drop
true
true
validate
false
false
none
false
false
Also some extra information about different types of DDL amd DML can be found in Spring docs
For the Propertie of JPA/Hibernate
spring.jpa.hibernate.ddl-auto value should be create, update, create-drop not other then it will give an exception, where the correct meaning for these value -
Create : when the server will start all entity will be newly created
Update : when the server will start container will find which entities are update and which all are newly created the same thing will happen inside database as well old table will update as per the entity and newly table will created
Create-drop: when the server will start then auto all entity will crete and when the server will stop all the entities will auto remove from database
none : it means database ddl will not impact from back-end application Note: Production environment always set with none value

Resources