H2: Column "SYSDATE" not found after upgrade - spring-boot

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?

Related

Where to specify the driver version db h2?

there are two databases h2 of different versions: 1.4.200 and 2.1.214.
contents of the application.yaml file:
app:
datasource1:
jdbcUrl: jdbc:h2:~/database/test1
driverClassname: org.h2.Driver
h2.version: 1.4.200
username: xxx
password: xxx
datasource2:
jdbcUrl: jdbc:h2:~/database/test2
driverClassname: org.h2.Driver
h2.version: 2.1.214
username: xxx
password: xxx
there are two configuration classes: DataSourcesConfiguration1 и DataSourcesConfiguration2.
#Configuration
#EnableTransactionManagement
public class DataSourcesConfiguration1 {
#Primary
#Bean
#ConfigurationProperties("app.datasource1")
public DataSourceProperties dataSourceProperties1() {
return new DataSourceProperties();
}
#Primary
#Bean("dataSource1")
#ConfigurationProperties(prefix = "app.datasource1")
public HikariDataSource dataSource1() {
return dataSourceProperties1()
.initializeDataSourceBuilder().type(HikariDataSource.class).build();
}
#Primary
#Bean("jdbcTemplate1")
public NamedParameterJdbcTemplate jdbcTemplate1()
{
HikariDataSource ds = dataSource1();
return new NamedParameterJdbcTemplate(ds);
}
#Primary
#Bean
#Qualifier("transactionManager1")
DataSourceTransactionManager transactionManager1() {
HikariDataSource ds = dataSource1();
return new DataSourceTransactionManager(ds);
}
}
the DataSourcesConfiguration2 class looks similar, without the #Primary annotation.
I do not know where to specify the db driver version.
I can only specify the version in the file pom.xml, but it works for two data sources:
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.200</version>
<!-- <version>2.1.214</version> -->
<scope>runtime</scope>
</dependency>
lines
h2.version: 1.4.200 and h2.version: 2.1.214
specified in the file application.yaml, ignored when creating beans.
need to find some property HikariDataSource ds.setDriverVersion();
but I don't know where to specify the driver version db h2 ?
You cannot load two versions of H2 at once, unless they are loaded by different classloaders.
H2 1.4.200 is an old unsupported version, why you need to use it these days? If you want to move your data from old version of H2 to new one you can try to use org.h2.tools.Upgrade class in H2 2.1.214 or you can use a third-party upgrade tool: https://github.com/manticore-projects/H2MigrationTool
But if you really need to use them both, the simplest solution is to include H2 2.1.214 to the classpath of your application and start a separate H2 Server process with H2 1.4.200 (java -jar h2-1.4.200.jar), connection URLs will be jdbc:h2:tcp://localhost/~/database/test1 for 1.4.200 and the same jdbc:h2:~/database/test2 for 2.1.214.
Your application will use a remote database on 1.4.200 server and embedded database opened by 2.1.214.

Spring Boot Data JPA with H2 and data.sql - Table not Found

I have a Spring Boot 2.5.0 project. I'm using spring-data-jap with the h2 in-memory database. I want to populate data on startup with a data.sql file but I'm getting a table not found exception. If I remove the data.sql file, I can see that a table for my entity does get created automatically. But if I include the data.sql file, I get the error saying the table doesn't exist. Maybe it is an error with my sql syntax of I have misconfigured the h2 database?
applicaltion.yml
spring:
datasource:
url: jdbc:h2:mem:test
driverClassName: org.h2.Driver
username: sa
password: sa
jpa:
database-platform: org.hibernate.dialect.H2Dialect
debug: true
data.sql
INSERT INTO BUSINESS_SUMMARY VALUES (1, "ALM470", "B48", 3);
BusinessSummary.java entity
#NoArgsConstructor(access = AccessLevel.PROTECTED)
#Getter
#Entity
public class BusinessSummary {
#Id
private Long id;
private String businessId;
private String businessDomainId;
private Integer cityCode;
}
BusinessSummaryRepository.java
#Repository
public interface BusinessSummaryRepository extends JpaRepository<BusinessSummary, Long> {
}
Exception:
Caused by: org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "BUSINESS_SUMMARY" not found; SQL statement:
INSERT INTO BUSINESS_SUMMARY VALUES(1, "ALM470", "B48", 3) [42102-200]
spring.jpa.defer-datasource-initialization=true
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.
you'll have to convert spring.jpa.defer-datasource-initialization to yml.
If you're using hibernate as a JPA implementation, the best way I think is by using the file import.sql instead of data.sql for Database Initialization.
for more information on database initialization see the official Spring Boot documentation Database Initialization
in addition to defer-datasource-initialization: true, you may also need
spring:
sql:
init:
mode: always
spring.jpa.defer-datasource-initialization = true
spring.sql.init.mode = always
if still doesn`t work try renaming the file from data.sql to import.sql

Liquibase migration files in spring boot app

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

Weblogic jndi NameNotFoundException occur with java config

I been searching again for this issue where I cannot locate the jndi database by using java config. Before this I use xml and its work perfectly but in java config it cause an issue;
Xml code:
<!-- Jndi database connection -->
<jee:jndi-lookup id="dbDataSource" jndi-name="${db.jndi}"
resource-ref="true" />
<beans:bean id="jdbcTemplate"
class="org.springframework.jdbc.core.JdbcTemplate" >
<beans:property name="dataSource" ref="dbDataSource"></beans:property>
</beans:bean>
Java config now:
#Bean(name = "dbDataSource")
public DataSource dataSource(#Value("${db.jndi}") String jndiName)
{
JndiDataSourceLookup lookup = new JndiDataSourceLookup();
return lookup.getDataSource(jndiName);
}
#Bean
public JdbcTemplate jdbcTemplate(DataSource ds) {
return new JdbcTemplate(ds);
}
Properties file:
db.jndi=jndi/myData
JNDI name in weblogic:
jndi/myData
After change to java config, sometimes the system can read the database but rarely occur, until I clean and restart my computer then it can find the database, but usually its always trigger:
javax.naming.NameNotFoundException: Unable to resolve 'jndi.myData'. Resolved 'jndi'; remaining name 'myData'
Why the application cannot find the database correctly?
Thanks!!!
I've had the same issue. If you're using 4.x version of spring that's probably the cause.
You should also check Weblogic's JNDI Tree. If your data source disapears from the tree after rebuilding the project, that's another symptom
If that's the case, what's happening is:
Your Datasource implements Closeable (and therefore AutoCloseable) and the context will always invoke the shutdown method regardless of your Bean definition
as seen here : SPR-12551: Document how to prevent a JNDI DataSource retrieved using JavaConfig to be removed on shutdown of the context
It's been marked as a documentation issue as this is the "expected" behaviour:
This issue was solely about documentation since we decided not to implement anything at the framework level
the solution, is to define the destroy method of the bean as empty, such as:
#Bean(name = "dbDataSource", destroyMethod="")
public DataSource dataSource(#Value("${db.jndi}") String jndiName)
{
JndiDataSourceLookup lookup = new JndiDataSourceLookup();
return lookup.getDataSource(jndiName);
}
#Bean
public JdbcTemplate jdbcTemplate(DataSource ds) {
return new JdbcTemplate(ds);
}
This is described in this issue (SPR-13022:Destroy callback cannot be disabled for AutoCloseable beans) .
PS: By the way, it seems like on early 4.x version of spring you couldn't override this behaviour by assingning destroyMethod. It apears that this bug was fixed on version 4.2 RC1.
I've had the same issue and I solved problem. I used to jndi datasource on weblogic. After I restart application, I notice my jndi datasource remove from Weblogic's JNDI Tree. Xml configuration works successfuly but java configuration don't work.
My old spring version: 4.1.6.RELEASE Upgrade to 4.3.9.RELEASE
Xml configuration like this;
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName">
<value>${db-jndi.name}</value>
</property>
</bean>
Java configuration like this;
#Bean(name = "dataSource")
public DataSource dataSource() throws IllegalArgumentException, NamingException
{
JndiTemplate jndiTemplate = new JndiTemplate();
DataSource dataSource = (DataSource) jndiTemplate.lookup(env.getProperty("db-jndi.name"));
logger.info("DataSource initialized in jndi ");
return dataSource;
}
Then i changed
#Bean(name = "dataSource")
to
#Bean(name = "dataSource", destroyMethod = "")
And it's works successfuly.
It looks like your datasource hasn't been deployed. You should look for JNDI tree for the server you tried to deploy datasource. (https://docs.oracle.com/cd/E12839_01/apirefs.1111/e13952/taskhelp/jndi/ViewObjectsInTheJNDITree.html) If you don't see "jndi.myData" on JNDI tree, you can assume that your datasource haven't been deployed. So you can go to your datasource monitoring tab and test the datasource. (https://docs.oracle.com/cd/E17904_01/apirefs.1111/e13952/taskhelp/jdbc/jdbc_datasources/TestDataSources.html)

View content of embedded H2 database started by Spring

I would like to view in a web browser the content of the H2 database started by Spring thanks to the following configuration:
<jdbc:embedded-database id="dataSource" type="H2" />
<jdbc:initialize-database data-source="dataSource">
<jdbc:script location="classpath:db/populateDB.sql"/>
</jdbc:initialize-database>
I searched for the JDBC URL in the logs:
DEBUG o.s.j.d.SimpleDriverDataSource - Creating new JDBC Driver Connection to [jdbc:h2:mem:dataSource;DB_CLOSE_DELAY=-1]
So that I could fill the connection form as follows:
But unfortunately, the db is still empty, whereas it shouldn't due to the populateDB.sql script.
Any idea?
Thanks!
Pretty much the same question as View content of H2 or HSQLDB in-memory database.
Simply add the following to your configuration.
<bean id="h2Server" class="org.h2.tools.Server" factory-method="createTcpServer" init-method="start" destroy-method="stop" depends-on="h2WebServer">
<constructor-arg value="-tcp,-tcpAllowOthers,-tcpPort,9092"/>
</bean>
<bean id="h2WebServer" class="org.h2.tools.Server" factory-method="createWebServer" init-method="start" destroy-method="stop">
<constructor-arg value="-web,-webAllowOthers,-webPort,8082"/>
</bean>
This will start both H2 web console and TCP server in the same JVM as your embedded database so that you can access port 8082 with your web browser (enter jdbc:h2:mem:dataSource as URL), or access port 9092 with external SQL client such as SQuirreLSQL and view the same data.
With spring boot you can do this with couple of configurations in the application.properties file.
spring.h2.console.enabled=true
spring.h2.console.path=/console/
Then you can access h2 web console in http://localhost:8080/console/. Default login configuration should work unless you change them.
See spring boot documentation.
The database URL jdbc:h2:mem:dataSource means you are using an in-memory database. Now if you start a second Java process and connect to this database, you will end up having two in-memory databases (one for each process).
If you want to connect to the existing database, you have multiple options:
Connect to the database from within the same process. Don't start a second process.
Use a persisted database, with a hardcoded absolute path, for example: `jdbc:h2:/data/db/dataSource'.
More complicated / not recommended: If you start a second process, you could theoretically connect to an in-memory database using the server mode. But that means you need to start the server where you ran the test.
When using Spring Boot you can register the H2 Console Servlet as follows:
#Bean
public ServletRegistrationBean h2servletRegistration() {
ServletRegistrationBean registration = new ServletRegistrationBean(new WebServlet());
registration.addUrlMappings("/console/*");
registration.addInitParameter("webAllowOthers", "true");
return registration;
}
If you want the console to be available remotely, the important line is the addInitParameter to set the "webAllowOthers" to "true".
When you use the an embeddeb with the xml jdbc configuration the default name of the database is 'testdb'
Try to use in your url connection:
jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1
For those wanting a Java Config setup it's fairly easy to do as well initializing the TCP server when implementing ServletContextInitializer and chaining the Console Server...
#Configuration
public class WebConfig implements ServletContextInitializer{
...
#Override
public void onStartup( ServletContext servletContext )
//do stuff onStartUp...
initH2TCPServer( servletContext );
....
#Bean(initMethod="start", destroyMethod="stop")
public Server initH2TCPServer(ServletContext servletContext) {
log.debug( "Initializing H2 TCP Server" );
try {
server = Server.createTcpServer( "-tcp", "-tcpAllowOthers", "-tcpPort", "9092" );
} catch( SQLException e ) {
e.printStackTrace();
} finally {
//Always return the H2Console...
initH2Console( servletContext );
}
return server;
}
public void initH2Console( ServletContext servletContext ) {
log.debug( "Initializing H2 console" );
ServletRegistration.Dynamic h2ConsoleServlet = servletContext.addServlet(
"H2Console", new org.h2.server.web.WebServlet() );
h2ConsoleServlet.addMapping( "/console/*" );
);
}
I was facing similar issue. But the fix was really very small. Please refer page : https://springframework.guru/using-the-h2-database-console-in-spring-boot-with-spring-security/ for more details.
In my case, I have added scope of H2 dependency as "runtime". I removed it and it fixed my issue. Not I am able to see tables in H2-console.
Previous dependency in my pom was :
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
And new dependency which fixed my issue :
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>

Resources