Liquibase migration files in spring boot app - spring-boot

I have a spring boot application that uses liquibase for database/data migration, the current state is, there is one migration file which is db.changelog-master.xml, that contains all the changesets. Now I want to divide this file into multiple files, or at least, the created new scripts will be created in new files. So I tried to add db.changelog-master.yaml to include the main file and any additional files. But the thing is, it generates a new checksum value so the scripts that already on the old file execute again. Is there any way to separate the old big file or even include it in the db.changelog-master.yaml without running the old scripts?

Two possible options can be :
1.Change the logicalFilePath:
Liquibase allows you to define so called logical file path of your changelog. This allows you to fake Liquibase that the changesets actually come from the same file. Liquibase will treat the changeset as if it was previously defined in changelog.xml.
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog-3.5.xsd"
logicalFilePath="classpath:changelog.xml">
<changeSet author="me" id="changeset2">
<createTable tableName="TABLE2">
<column name="COLUMN1" type="VARCHAR2(10)"/>
</createTable>
</changeSet>
</databaseChangeLog>
This approach is not the best if your intent to split the changelog is to move the part of it to another package, module, and so on.
2.Use intermediate changelog:
The first step is to move all the relevant changesets to another file elsewhere. We need to fake Liquibase that those changesets didn't change. We do it by modifying the FILENAME value in the database as part of the Liquibase changelog itself .
Create one more (intermediate/temporary) changelog (let's call it tmp-migration.xml) with just this one changeset:
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog-3.5.xsd">
<changeSet id="moving changesets from changelog to changelog2" author="Maros Kovacme">
<sql>
UPDATE DATABASECHANGELOG
SET
FILENAME = REPLACE(FILENAME, 'changelog.xml', 'changelog2.xml'))
WHERE
ID IN (
'changeset2'
);
</sql>
</changeSet>
</databaseChangeLog>
This changeset will replace the FILENAME column value in the DB from classpath:changelog.xml to classpath:changelog2.xml. When we then run Liquibase with the changelog2.xml, it will think that all changesets are already applied.
The last step we have to apply is to define the corresponding beans in our context in the right order:
#Configuration
public class MultipleLiquiaseConfiguration {
#Bean
public SpringLiquibase liquibaseChangelog(DataSource dataSource) {
SpringLiquibase liquibase = new SpringLiquibase();
liquibase.setDataSource(dataSource);
liquibase.setChangeLog("classpath:changelog.xml");
return liquibase;
}
#Bean
#DependsOn("liquibaseChangelog")
public SpringLiquibase liquibaseMigration(DataSource dataSource) {
SpringLiquibase liquibase = new SpringLiquibase();
liquibase.setDataSource(dataSource);
liquibase.setChangeLog("classpath:tmp-migration.xml");
return liquibase;
}
#Bean("liquibase")
#DependsOn("liquibaseMigration")
public SpringLiquibase liquibaseChangelog2(DataSource dataSource) {
SpringLiquibase liquibase = new SpringLiquibase();
liquibase.setDataSource(dataSource);
liquibase.setChangeLog("classpath:changelog2.xml");
return liquibase;
}
}
Details here

Related

H2: Column "SYSDATE" not found after upgrade

I upgraded the version of h2 from 1.4.200 to 2.1.214 and tests fails now because of the use of "SYSDATE" in a liquibase file (we use an Oracle database when the app is deployed).
Content of liquibase file:
<createTable tableName="myTable">
<column defaultValueComputed="SYSDATE" name="CREATION_DATE" type="TIMESTAMP(6)">
<constraints nullable="true" />
</column>
</createTable>
When the liquibase file is loaded to initialize the database for tests, then it fails with this error:
Column "SYSDATE" not found
The executed query is like this:
CREATE TABLE PUBLIC.my_table (..., CREATION_DATE TIMESTAMP(6) DEFAULT SYSDATE,...)
I tried to force the Oracle compatibility mode of H2 like this:
spring:
datasource:
url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;MODE=Oracle
I saw that h2, in Oracle compatibility mode, should accept "SYSDATE" keyword but I still have the error.
Do you know what I have to do to solve the issue please?
I found a solution for this error.
For local tests, a configuration of the DataSource was done like this:
#Bean
#Profile(SpringProfiles.H2)
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()//
.setType(EmbeddedDatabaseType.H2) //
.setName("local") //
.build();
}
I had to change it by adding the mode in the name:
#Bean
#Profile(SpringProfiles.H2)
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()//
.setType(EmbeddedDatabaseType.H2) //
.setName("local;MODE=Oracle") //
.build();
}
I found the solution in this link: Does Spring embedded database support different SQL dialects?

Not Found in ExternalContext as a Resource Error (Amazon Elastic Beanstalk / Spring Boot / JSF + Primefaces)

I created an app using Spring Boot, JSF + Primefaces. I deployed it to Amazon Elastic Beanstalk, and set the environment variables that project needed to use.
When I tried to access my website, I got this error message:
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Thu Mar 04 07:21:33 UTC 2021
There was an unexpected error (type=Not Found, status=404).
/login.xhtml Not Found in ExternalContext as a Resource
Here is my Faces Config (as java class):
package com.jsf;
#SpringBootApplication
#Configuration
#ComponentScan("com.jsf")
public class HelloJsfApplication {
public static void main(String[] args) {
SpringApplication.run(HelloJsfApplication.class, args);
}
// JSF Configration Başlangıc
#Bean
public ServletRegistrationBean<FacesServlet> facesServletRegistraiton() {
ServletRegistrationBean<FacesServlet> registration = new ServletRegistrationBean<FacesServlet>(
new FacesServlet(), new String[] { "*.xhtml" });
registration.setName("Faces Servlet");
registration.setLoadOnStartup(1);
registration.addUrlMappings("*.xhtml");
return registration;
}
#Bean
public ServletContextInitializer servletContextInitializer() {
return servletContext -> {
servletContext.setInitParameter("com.sun.faces.forceLoadConfiguration", Boolean.TRUE.toString());
servletContext.setInitParameter("primefaces.THEME", "bootstrap");
// Primefaces client browser tarafında kontrol edilebilme örneğin textbox 10
// karakter olmalı vs..
servletContext.setInitParameter("primefaces.CLIENT_SIDE_VALIDATION", Boolean.TRUE.toString());
// Xhtml sayfalarında commentlerin parse edilmemesi.
servletContext.setInitParameter("javax.faces.FACELETS_SKIP_COMMENTS", Boolean.TRUE.toString());
// primefaces icon set için
servletContext.setInitParameter("primefaces.FONT_AWESOME", Boolean.TRUE.toString());
};
}
#Bean
public ServletListenerRegistrationBean<ConfigureListener> jsfConfigureListener() {
return new ServletListenerRegistrationBean<ConfigureListener>(new ConfigureListener());
}
// JSF Configration Sonu
}
my faces config file under webapp/WEB-INF folder:
<?xml version="1.0" encoding="UTF-8"?>
<faces-config xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd"
version="2.2">
<application>
<el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>
</application>
</faces-config>
and as you see, all my xhtml files are under webapp folder:
my application.properties file (using java params):
server.servlet.context-path = ${CONTEXT_PATH}
server.port = ${PORT}
spring.datasource.url = ${SPRING_DATASOURCE_URL}
spring.datasource.username = ${SPRING_DATASOURCE_USERNAME}
spring.datasource.password = ${SPRING_DATASOURCE_PASSWORD}
spring.jpa.properties.hibernate.dialect = ${SPRING_JPA_DATABASE_PLATFORM}
# Hibernate ddl auto (create, create-drop, validate, update)
spring.jpa.hibernate.ddl-auto = ${SPRING_JPA_HIBERNATE_DDL_AUTO}
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=${SPRING_JPA_HIBERNATE_LOB_CREATION}
spring.datasource.driver-class-name=${SPRING_DATASOURCE_DRIVERCLASSNAME}
and here are java parameters that I defined on Amazon (I will not post db params for security reasons):
PORT=5000
CONTEXT_PATH=/
SPRING_DATASOURCE_DRIVERCLASSNAME=org.postgresql.Driver
...
and so on.
Really I run out of solutions. With same packaged jar, I deployed to Heroku with same environment parameters, and it works flawlessly.
Where I am doing wrong with Amazon Elastic Beanstalk?
Kind regards.
I found the problem.
Maven, does not add *.xhtml files inside "jar" package.
Here is the ss of decompiled version of jar packaging (with jd-gui)
..and here is the ss of war packaging:
so if you are think that you did everything correct during development and managing after, check the packaging type.

How to declare a element for complex data type defined in a xsd file located in jar file

I have a below xsd file in a jar dependency file in a Spring boot project. The xsd does not declare the element declaration, which is required by spring boot validation. So I am trying to add another xsd in my project resource folder to declare element type.
XSD in Jar file: /wsdl/xsd/UserService-v1-0.xsd
<?xml version="1.0" encoding="utf-8"?>
<xsd:schema targetNamespace="http://usermanagement.com/userservice/xsd/2014/07"
xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:userservice="http://usermanagement.com/userservice/xsd/2014/07"
elementFormDefault="qualified">
<xsd:import namespace="http://usermanagement.com/userservice/common/xsd/2014/09" schemaLocation="userserviceCommon-v1-0.xsd"/>
<xsd:import namespace="http://usermanagement.com/userserviceabsolute/common/xsd/2014/09" schemaLocation="userserviceAbsoluteCommon-v1-0.xsd"/>
<xsd:complexType name="GetUsersRequest">
<xsd:attribute name="language" type="xsd:language" use="optional" default="en" />
</xsd:complexType>
</xsd:schema>
Please note that I tried to keep the complex type simple by removing some of the elements for this post. The location of file is /wsdl/xsd.
XSD to declare the element in the main Project resource: /wsdl/xsd/UserService-type-v1-0.xsd
<?xml version="1.0" encoding="utf-8"?>
<xsd:schema targetNamespace="http://usermanagement.com/userservice/wsdl/userserviceService-v1-0"
xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:userservice="http://usermanagement.com/userservice/xsd/2014/07"
elementFormDefault="qualified">
<import namespace="http://usermanagement.com/userservice/xsd/2014/07" schemaLocation="jar:file://{path to the jar}/!/wsdl/xsd/userserviceService-v1-0.xsd"/>
<element name="GetUsersRequest" type="userservice:GetUsersRequest"/>
</xsd:schema>
As per my understanding we need to refer to the schemaLocation of xsd file in jar using notation "jar:file://{path to the jar}/!/wsdl/xsd/userserviceService-v1-0.xsd".
However I am not sure how to declare the path to the jar. Also not sure if this is the only way to refer to the definitions in the xsd in the jar files. In the above approach when ever a new version of jar is released we need to update the xsd file.
Also including the Spring WS schema validation config:
#Configuration
public class MyWsValidatorConfig extends WsConfigurerAdapter {
#Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
PayloadValidatingInterceptor validatingInterceptor = new PayloadValidatingInterceptor();
validatingInterceptor.setValidateRequest(true);
validatingInterceptor.setValidateResponse(true);
validatingInterceptor.setXsdSchemaCollection(new XsdSchemaCollection() {
#Override
public XsdSchema[] getXsdSchemas() {
return null;
}
#Override
public XmlValidator createValidator() {
try {
return XmlValidatorFactory.createValidator(getSchemas(), "http://www.w3.org/2001/XMLSchema");
} catch (Exception e) {
log.error("Failed to create validator e={}", e);
}
return null;
}
public Resource[] getSchemas() {
List<Resource> schemaResources = new ArrayList<>();
schemaResources.add(new ClassPathResource("/wsdl/xsd/UserService-v1-0.xsd"));
schemaResources.add(new ClassPathResource("/wsdl/xsd/UserService-type-v1-0.xsd"));
return schemaResources.toArray(new Resource[schemaResources.size()]);
}
});
interceptors.add(validatingInterceptor);
}
}
This is the first time I am working with XSD and SOAP web services. Able to cross other hurdles but got stuck with this issue.
Please help.

Spring / HibernateJpaVendorAdapter under JPA 2.1 is not executing schema generation of Entity model / Metadata info

Spring / HibernateJpaVendorAdapter under JPA 2.1 is not the executing schema generation of Entity model / Metadata info.
I have added two code snippets and the persistence.xml below.
(Checkout my JPA 2.1 properties about schema generation... they seem to be okay. (?))
When I re-run my webapplication (restart of Tomcat) the scripts files are not created on my filesystem. (I even did a complete file search on my filesystem whether they were saved on a different place than expected. But no sign of these files on my filesystem).
What is wrong with the code / persistence.xml below? I checked with the javadocs of Spring's HibernateJpaVendor class but I didn't see anything which could point me to the cause.
Question: What's wrong here... I have been breaking my head on this one?
Remark 1: However, when I move my JPA 2.1 schema generation properties from HibernateJpaVendor to persistence.xml the files are generated successfully. (These properties have been put in comments in my persistence.xml to reflect the problem I am describing above.)
Remark 2: I am using Hibernate 5.1.0.Final and Spring framework 4.2.5.RELEASE
Versions in POM:
<!-- hibernate - validator JSR303-->
<hibernate-validator.version>5.1.3.Final</hibernate-validator.version>
<!-- hibernate -->
<hibernate-jpa-2.1-api.version>1.0.0.Final</hibernate-jpa-2.1-api.version>
<hibernate.version>5.1.0.Final</hibernate.version>
<!-- spring framework -->
<spring.framework.version>4.2.5.RELEASE</spring.framework.version>
Properties:
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost/ob?autoReconnect=true
jdbc.username=<user>
jdbc.password=<password>
jdbc.db=obi
jpa.database=MYSQL
hbm2ddl=auto
jpa.showSql=true
jpa.generateDdl=false
jpa.persistenceUnit=obPU
hibernate.hbm2ddl.auto=create-update
hibernate.archive.autodetection=class
c3p0.acquire_increment=5
c3p0.idle_test_period=100
c3p0.max_size=100
c3p0.max_statements=0
c3p0.min_size=10
Persistence.xml:
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_2_1.xsd"
version="2.1">
<persistence-unit name="obPU" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<class>com.company.ob.domain.model.Obi</class>
<class>com.company.ob.domain.model.Con</class>
<class>com.company.ob.domain.model.ImageMeta</class>
<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
<!--<properties>-->
<!--<property name="javax.persistence.schema-generation.database.action" value="drop-and-create"/>-->
<!--<property name="javax.persistence.schema-generation.scripts.action" value="drop-and-create"/>-->
<!--<property name="javax.persistence.schema-generation.scripts.create-target" value="obituaries-create.sql"/>-->
<!--<property name="javax.persistence.schema-generation.scripts.drop-target" value="obituaries-drop.sql"/>-->
<!--<property name="javax.persistence.sql-load-script-source" value="obituaries-insert.sql"/>-->
<!--</properties>-->
</persistence-unit>
Code snippets from my Spring Java #Configuration file. My cofiguration is something like this example but not identical (to give an idea/picture):
#Bean(name = "entityManagerFactory")
public EntityManagerFactory entityManagerFactory(DataSource dataSource, JpaVendorAdapter jpaAdapter) {
LocalContainerEntityManagerFactoryBean fb = new LocalContainerEntityManagerFactoryBean();
fb.setDataSource(dataSource);
fb.setJpaVendorAdapter(jpaAdapter);
fb.setPersistenceUnitName(persUnit);
fb.setPackagesToScan("com.company.ob.domain.model"); //if set info persistence is not needed. Scan based on Spring and not on JPA
fb.afterPropertiesSet();
return fb.getObject();
}
#Bean
public JpaVendorAdapter jpaAdapter() {
HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
adapter.setDatabase(Database.valueOf(database));
adapter.setShowSql(Boolean.valueOf(showSql));
// not setting to true because not possible as of Hibernate v5 if using JPA 2.1 schema generation (see javadocs Hibernate)
//adapter.setGenerateDdl(Boolean.valueOf(genDdl));
//adapter.getJpaPropertyMap().put("hibernate.hbm2ddl.auto", hbm2ddl);
adapter.getJpaPropertyMap().put("javax.persistence.schema-generation.database.action", "drop-and-create");
adapter.getJpaPropertyMap().put("javax.persistence.schema-generation.scripts.action", "drop-and-create");
adapter.getJpaPropertyMap().put("javax.persistence.schema-generation.scripts.create-target", "ob-create.sql");
adapter.getJpaPropertyMap().put("javax.persistence.schema-generation.scripts.drop-target", "ob-drop.sql");
adapter.getJpaPropertyMap().put("javax.persistence.database-product-name", "MySQL");
// adapter.getJpaPropertyMap().put("javax.persistence.sql-load-script-source", "ob-insert.sql");
return adapter;
}

Does Spring Data JPA automatically create cache for query method?

I created query method in Spring Data JPA repository. I used method naming conventions.
I'm not sure but it looks like Spring Data made query cache for it. I have those entity:
public class Feature {
private String desc;
private FeatureFor featureFo;
}
public enum FeatureFor {
ABC, DEF, XYZ;
}
In repository I created method with name:
public List<Feature> findByFeatureForIn(List<FeatureFor> featureFors);
This method is invoked twice from different servises.
First time when it's invoked I can see generated query in console which is something like this:
SELECT ...... WHERE ((t0.featureFor = ? OR t0.featureFor = ?) AND t0.featureFor IS NOT NULL) [params=(String) ABC, (String) DEF]
This is ok. But When method is invoked via second service I can see same select generated with the same parameters. I'm sure that in featureFors parameter there were ABC and XYZ. But it's completely ignored. I also tried to use featureFors with one ABC value contained in (in first invoked service). In this case query generated was something like this:
SELECT ...... WHERE (t0.featureFor = ? AND t0.featureFor IS NOT NULL) [params=(String) ABC, (String) DEF]
Second service passed same params (ABC and XYZ) but the generated query and params did not change (it's same which generated in the first service). Does anybody knows if Spring Data creates cache for queries? I didn't realize I made some mistake. Maybe I did. I'm using OpenJPA. Thank you for your help.
The persistence configuration looks like this:
<?xml version="1.0"?>
<persistence version="2.0"
xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="openjpa">
<provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
<class>...difened entities here (including Feature)...</class>
<properties>
<property name="openjpa.Log" value="DefaultLevel=INFO, Runtime=INFO, Tool=INFO, SQL=TRACE"/>
<property name="openjpa.ConnectionFactoryProperties" value="PrintParameters=true" />
<property name="openjpa.jdbc.MappingDefaults" value="ForeignKeyDeleteAction=restrict,JoinForeignKeyDeleteAction=restrict"/>
</properties>
</persistence-unit>
</persistence>

Resources