Liquibase system properties in changeSet with Spring Boot - spring

I have a Spring Boot application which uses Liquibse to manage the database schema changes.
I have the following change set:
<changeSet id="add-customer-entry" context="dev">
<insert tableName="customer">
<column name="email" value="${email}"/>
</insert>
</changeSet>
I'm trying to pass the values to the email property with no success. The following works;
<property name="email" value="some-email"/>
But I need to set the value either as a property in the application-local.properties file or as a JVM system property.
I defined the property in the application-local.properties file like this:
spring.liquibase.parameters.email=some-email
And in IntelliJ IDE in the program arguments like this: --Dliquibase.email=some-email
No success, the value in the email column is stored as ${email}
I'm using Spring Boot version 2.2.8.RELEASE and the liquibase version is 3.8.9
I followed the following post;
how can I access system properties using spring boot and Liquibase changeset yaml file

Related

Spring: exclude some properties files from context

suppose we have some jars with properties files with the same key/values.
configA.jar:
log4j.A.properties
configB.jar:
log4j.B.properties
The problem: Spring mixes values from the both properties files. So, how to exclude log4j.A.properties from the context and process only log4j.B.properties?
UPDATE (added some stuff): there is a maven build which produces two jars mentioned above. Here in webapp (applicationContext.xml) following setup:
<util:properties id="propertyConfigurer" location="classpath:common.properties,classpath*:edrive.properties,classpath*:job.properties,classpath*:log4j.B.properties"/>
After the startup Spring mixes both jars and takes random (or the last one) jar and it's log4j.properties. But we need only the log4j.B.properties. How to do that?
try adding the config file to be used in your properties file
logging.config=log4j.B.properties
I resolved the issue by myself. I've upgraded logging facility to Log4j2 with following configuration:
log4j2.component.properties in classpath:
log4j.configurationFile=classpath:log4j2.web.xml
That's it.

Unable to execute data.sql and schema.sql at Spring Boot Startup

I'm trying to execute some DB initialization for a Spring Boot application against a MySQL Database that is running in a container.
During the authentication process, I receive an error "Table not found". I've checked the DB and no tables have been created indeed.
Is there something missing in DB properties?
spring.datasource.url = jdbc:mysql://172.17.0.2:3306/schema
spring.datasource.username = user
spring.datasource.password = password
spring.datasource.testWhileIdle = true
spring.datasource.validationQuery = SELECT 1
spring.jpa.show-sql = true
spring.jpa.hibernate.ddl-auto = create-drop
spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.ImprovedNamingStrategy
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect
spring.data.rest.basePath=/
spring.datasource.data = classpath:/data.sql
spring.datasource.schema = classpath:/schema.sql
All in all, the JDBC Settings work fine provided that I create the DDL from the mysql command line. So it's just not executing the data.sql and schema.sql at startup.
Do I need some extra properties for mysql ?
You can update the application.properties with following properties.
application.properties:
spring.jpa.hibernate.ddl-auto=none
spring.jpa.generate-ddl=false
spring.datasource.initialization-mode=always
By default value of spring.jpa.generate-ddl is false. If you set spring.jpa.generate-ddl=true or spring.jpa.hibernate.ddl-auto to any of value validate, update, create, create-drop. Spring boot generates schema scripts and creates the tables based on the entities available in your application.
Now you should create the schema.sql and data.sql files under resources folder and set the property spring.datasource.initialization-mode=always in application.properties.
You can also run the scripts based on the platform. Let's suppose you want to run the scripts for hsqldb database, then set spring.datasource.platform=hsqldb in application.properties file and created scripts file schema-hsqldb.sql and data-hsqldb.sql under resources folder.
You can find more details about how to load schema.sql and data.sql on startup with spring boot.
Everything works as expected until a spring-boot update the pom file from
<version>1.5.8.RELEASE</version>
to
<version>2.3.3.RELEASE</version>
A switch back and update all test are passed without any exceptions. In Version 2.3.3 a test fault caused by missing data in database.
I've spend hours to solve the ddl initialization for Spring Version 2.3.3.RELEASE without find regular solution.
As you have
spring.jpa.hibernate.ddl-auto = create-drop
Spring doesn't run the schema.sql and data.sql.
Try it with
spring.jpa.hibernate.ddl-auto = none
Check the docs
In a JPA-based app, you can choose to let Hibernate create the schema or use schema.sql, but you cannot do both. Make sure to disable spring.jpa.hibernate.ddl-auto if you use schema.sql.
For spring boot version 2.7, ensure that you have configured spring.sql.init.mode property in the application.properties file
Example:
spring.sql.init.mode=always
spring.jpa.hibernate.ddl-auto=update
spring.jpa.generate-ddl=true

Spring Boot Reading Properties Files Based on classpath arg

I have created a standalone boot.jar that I need to start integrating into our higher environments.
Each environment has a property file that contains database specific connection information. Since this does not live in my boot jar, I would like to somehow add the path to this database.properties file and then read the entries based on key. Used to create a bean like so:
<bean id="propertyLoader" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<value>classpath:application.properties</value>
</property>
but with boot, I am not sure how to do this: But I want to point to this below property example and pull out my values and somehow populate the application.properties values that I hardcoded in dev.
server:/config/database.properties
jdbc.username=TEST
jdbc.password=CHANGEME
Updating my application.properites:
spring.datasource.username='jdbc.username'
spring.datasource.password='jdbc.password'
Something like that do I can parameterize my application.properties file.
SpringBoot offers profiles, which basically allows you to have separate application.properties file for each environment.
You can have something like this:
public interface DataSourceConfig {}
#Component
#Profile("dev")
public DevDataSourceConfig implements DataSourceConfig{}
#Component
#Profile("prod")
public ProdDataSourceConfig implements DataSourceConfig{}
If you have the spring profile "dev" set as active, only the DevDataSourceConfig bean will be instantiated and in Spring Environment the properties that will be injected, will be read from the application-dev.properties file.
Similarly when you have the "prod" profile activated, only the ProdDataSourceConfig will be instantiated and the properties will be loaded from application-prod.properties file.
This allows you to have:
---
application-dev.properties
spring.datasource.username='jdbc.username'
spring.datasource.password='jdbc.password'
---
application-prod.properties
spring.datasource.username='PROD_jdbc.username'
spring.datasource.password='PROD_jdbc.password'
If you want to load the configuration from a custom location on the file system - you can check how to pass the location with command line arguments (docs)
Example:
java -jar boot.jar --spring.config.location=classpath:/database.properties
you already told you can not have property files inside your jar, still there are multiple options.
1> passing a property file for respective env.
java -jar myproject.jar --spring.config.location=classpath:/database.properties
2> pass properties while calling the jar
java -jar app.jar --spring.datasource.username="jdbc.username" --spring.datasource.password="jdbc.password"
Read a lot of other options here `
I would go with option 1, because passing credentials is never advisable in arguements.

where spring defines variable value, which is referred by some xml file

I am trying to understand from where does the value of a variable comes from, when it is referred in Spring xml file.
For example:
<context:property-placeholder location="classpath:/${com.example.deploy.environment}/com.example.config/mysql.properties" ignore-resource-not-found="false" />
Where is the value of com.example.deploy.environment defined? In my project I searched all over, however i couldn't find anywhere where this values is defined.
Any information in understanding this would be of great help.
This value can come from a variety of source:
application.properties file which you can define in PropertyPlaceholderConfigurer bean.
<bean id="mailProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:application.properties" />
</bean>
...
//Inside application.properties
com.example.deploy.environment=prod
You can provide via command-line:
With Maven vm arguments for JVM System property:
mvn package -Dcom.example.deploy.environment=prod
Running Spring Boot Application:
java -jar app.jar --com.example.deploy.environment="prod"
From System Environment variable of the Operating System. You might have to restart after setting environment variable. See below for windows:
Refer this doc and this article for more info.

After using gradle-flyway plugin to create test database, how to reference db created in c3p0 config?

I was trying to accomplish the following
1) Create h2 database using gradle-flyway plugin before tests run. I was able to create it and place it under ${buildDir}
flyway {
url = "jdbc:h2:file:${buildDir}/db/test/xxxdb"
user = 'root'
locations = [
'classpath:sql'
]
}
2) My goal was to write integration tests against this db created in step 1. I am using c3p0 for pooling and here is my configuration.
<?xml version="1.0"?>
<c3p0-config>
<default-config>
<property name="driverClass">org.h2.Driver</property>
**<property name="jdbcUrl">jdbc:h2:file:#buildDir#/db/test/xxxdb</property>**
<property name="user">xxx</property>
<property name="password">xxx</property>
<property name="minPoolSize">1</property>
<property name="maxPoolSize">50</property>
<property name="acquireIncrement">1</property>
</default-config>
</c3p0-config>
Questions:
a) How do I configure c3p0 to reference the h2 database present in the build directory created in step 1. c3p0 wants absolute path in jdbcUrl property.
b) I configured gradle to replace #buildDir# with appropriate value using this below gradle task. So the database jdbcUrl is configured correctly for c3p0 when I do "gradle build". But when I run the tests though intelliJ, it still reads the jdbcUrl with value containing "#buildDir#" as intellij does not run processTestResources task before running the test.
processTestResources {
filter(ReplaceTokens, tokens: [buildDir: buildDir.getAbsolutePath()])
}
Any ideas on how to solve this issue?
Thanks Mark for your comments. For relative paths, this is the syntax I had used and now it works with both gradle and intellij.
jdbc:h2:file:./db/test/xxxdb

Resources