Liquibase Java, changesets don't apply - java

I'm currently building a project for my internship, and I'm stuck since two days trying to use Liquibase in java. Everything seems right : the changelog file is found, correct URI, username and password; but when I run it, my changesets are not processed.
I use this class to manage liquibase actions from my program, such as rollback, update, updateSQL and futureRollbackSQL, given a changelog and eventually a destination file. If the source or destination is from a remote server, I use some SSH interactions (scp to, scp from) with JSch and temporary files (but it's not the topic).
This is the java code I have for now, given db, user, passwd, realAction are set previously, changelogpath and dest are some data storage class.
Connection c = null;
Database database = null;
PrintWriter pw = null;
File file = null;
liquibase.Liquibase liquibase = null;
contexts = db+"."+user;
try {
pw = new PrintWriter(new FileWriter(file));
// Get connection
c = SQLManager.getInstance().getConnection(db, user, passwd);
// Get liquibase connection
database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(c));
liquibase = new liquibase.Liquibase(new DatabaseChangeLog(fsource), new FileSystemResourceAccessor(),
database);
// Run liquibase action
switch (realAction) {
case Constants.LIQUIBASE_ACTION_FUTUREROLLBACKSQL:
liquibase.futureRollbackSQL(pw);
break;
case Constants.LIQUIBASE_ACTION_UPDATESQL:
liquibase.update(contexts, pw);
break;
case Constants.LIQUIBASE_ACTION_UPDATE:
liquibase.update(contexts);
if (!c.getAutoCommit())
c.commit();
break;
default:
throw new OdewipElementRuntimeException(this, "Action not implemented");
}
pw.close();
database.close();
c.close();
} catch (IOException | SQLException | LiquibaseException e) {
throw new Exception(e.getMessage());
} finally {
if (c != null) {
try {
c.close();
} catch (SQLException e) {
// nothing to do
throw new RuntimeException(e.getClass() + ": " + e.getMessage());
}
}
}
And here is my changelog:
<?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"
xmlns:ora="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-2.0.xsd http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd">
<changeSet id="mychangeset" author="testy">
<preConditions onSqlOutput="TEST" onFail="MARK_RAN">
<not>
<tableExists tableName="abcd"/>
</not>
</preConditions>
<createTable tableName="abcd">
<column name="id" type="number">
<constraints primaryKey="true"/>
</column>
</createTable>
</changeSet>
</databaseChangeLog>
Liquibase seems to do something, except parsing my changeset. When I launch my actions, sql generated files only contains the creation of the two tables of liquibase (databasechangelog and databasechangeloglock) and that's all. The update action won't modify anything at all (not even creating the two previously mentioned tables). I'm (100%) sure that the table abcd does not exist in the database before execution.
So I think I need some help at this point, to figure out what isn't working. I tried to look at some examples from the liquibase forum, but nothing helped.
I'm currently using Maven's liquibase 3.4.0:
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
<version>3.4.0</version>
</dependency>
Is there another dependency to include I missed?
One small other question is how do I include a special oracle database driver(ojdbc6.jar)?
Thank you for your answers.
EDIT 06/08/2015:
I got the logs in debug mode (deliberately changed the schema name):
DEBUG 06/08/15 09:28: liquibase: Executing QUERY database command: select count(*) from MYSCHEMA.DATABASECHANGELOGLOCK
DEBUG 06/08/15 09:28: liquibase: Create Database Lock Table
DEBUG 06/08/15 09:28: liquibase: Executing EXECUTE database command: CREATE TABLE MYSCHEMA.DATABASECHANGELOGLOCK (ID NUMBER(10) NOT NULL, LOCKED NUMBER(1) NOT NULL, LOCKGRANTED TIMESTAMP, LOCKEDBY VARCHAR2(255), CONSTRAINT PK_DATABASECHANGELOGLOCK PRIMARY KEY (ID))
DEBUG 06/08/15 09:28: liquibase: Created database lock table with name: MYSCHEMA.DATABASECHANGELOGLOCK
DEBUG 06/08/15 09:28: liquibase: Executing QUERY database command: select count(*) from MYSCHEMA.DATABASECHANGELOGLOCK
DEBUG 06/08/15 09:28: liquibase: Initialize Database Lock Table
DEBUG 06/08/15 09:28: liquibase: Executing EXECUTE database command: DELETE FROM MYSCHEMA.DATABASECHANGELOGLOCK
DEBUG 06/08/15 09:28: liquibase: Executing EXECUTE database command: INSERT INTO MYSCHEMA.DATABASECHANGELOGLOCK (ID, LOCKED) VALUES (1, 0)
DEBUG 06/08/15 09:28: liquibase: Executing QUERY database command: SELECT LOCKED FROM MYSCHEMA.DATABASECHANGELOGLOCK WHERE ID=1 FOR UPDATE
DEBUG 06/08/15 09:28: liquibase: Lock Database
DEBUG 06/08/15 09:28: liquibase: Executing UPDATE database command: UPDATE MYSCHEMA.DATABASECHANGELOGLOCK SET LOCKED = 1, LOCKEDBY = 'CRO09177 (xx.xx.xx.xxx)', LOCKGRANTED = to_date('2015-08-06 09:28:28', 'YYYY-MM-DD HH24:MI:SS') WHERE ID = 1 AND LOCKED = 0
INFO 06/08/15 09:28: liquibase: Successfully acquired change log lock
DEBUG 06/08/15 09:28: liquibase: Create Database Change Log Table
INFO 06/08/15 09:28: liquibase: Creating database history table with name: MYSCHEMA.DATABASECHANGELOG
DEBUG 06/08/15 09:28: liquibase: Executing EXECUTE database command: CREATE TABLE MYSCHEMA.DATABASECHANGELOG (ID VARCHAR2(255) NOT NULL, AUTHOR VARCHAR2(255) NOT NULL, FILENAME VARCHAR2(255) NOT NULL, DATEEXECUTED TIMESTAMP NOT NULL, ORDEREXECUTED NUMBER(10) NOT NULL, EXECTYPE VARCHAR2(10) NOT NULL, MD5SUM VARCHAR2(35), DESCRIPTION VARCHAR2(255), COMMENTS VARCHAR2(255), TAG VARCHAR2(255), LIQUIBASE VARCHAR2(20), CONTEXTS VARCHAR2(255), LABELS VARCHAR2(255))
DEBUG 06/08/15 09:28: liquibase: Executing QUERY database command: select count(*) from MYSCHEMA.DATABASECHANGELOG
INFO 06/08/15 09:28: liquibase: Reading from MYSCHEMA.DATABASECHANGELOG
DEBUG 06/08/15 09:28: liquibase: Executing QUERY database command: SELECT FILENAME,AUTHOR,ID,MD5SUM,DATEEXECUTED,ORDEREXECUTED,EXECTYPE,DESCRIPTION,COMMENTS,TAG,LIQUIBASE,LABELS,CONTEXTS FROM MYSCHEMA.DATABASECHANGELOG ORDER BY DATEEXECUTED ASC, ORDEREXECUTED ASC
DEBUG 06/08/15 09:28: liquibase: Executing QUERY database command: select count(*) from MYSCHEMA.DATABASECHANGELOGLOCK
DEBUG 06/08/15 09:28: liquibase: Release Database Lock
DEBUG 06/08/15 09:28: liquibase: Executing UPDATE database command: UPDATE MYSCHEMA.DATABASECHANGELOGLOCK SET LOCKED = 0, LOCKEDBY = NULL, LOCKGRANTED = NULL WHERE ID = 1
INFO 06/08/15 09:28: liquibase: Successfully released change log lock

Here at Datical we have noticed that there are some issues with Liquibase and Java 1.8. You mentioned on your post to the Liquibase user forums that you were using Java 1.8, so it is possible that Java 1.8 may be the issue. Can you try using Java 1.7 and see if you get different results? If not, you could try increasing the logging level - add a line like this after you have created your Liquibase object:
LogFactory.getInstance().getLog().setLogLevel(logLevel);
Where logLevel is the string "debug"

Most likely it is issue with XML versions.
Replace in XML header
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-2.0.xsd
to
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.4.xsd

I encounter the same issue even if the question is really old, I'll try to give an answer. The code is wrong in two points:
changeset path
PrintWriter
Liquibase API doesn't give good feedback if the path is unreadable, but usually, you will see logging locks without any applied query.
Furthermore, in your code, the execution is printed in the Writer instead of executing queries.
here you can find a working example:
Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(ds.getConnection()));
Liquibase liquibase = new Liquibase("./relativepathToChangeset", new FileSystemResourceAccessor(new File("basePathWhereChangeSetIsPresent")), database);
liquibase.update("");

I'm sharing the solution I found to fix changes not being applied when running liquibase update. Please note, I am not using Java, I managing my changelog as a .sql file. I am using the Snowflake JDBC driver. The thing that fixed it for me was explicitly defining the defaultCatalogName and defaultSchemaName properties in the liquibase.properties file. Simply providing these parameters in the JDBC url was insufficient. My liquibase.properties file looks like so:
changeLogFile: sql/changelog.sql
url: jdbc:snowflake://<url>?db=<database name>&schema=PUBLIC&role=SYSADMIN&warehouse=COMPUTE_WH
driver: net.snowflake.client.jdbc.SnowflakeDriver
username: <username>
password: <password>
classpath: snowflake-jdbc-3.13.14.jar
defaultCatalogName: <database name>
defaultSchemaName: PUBLIC
liquibase.hub.mode=off
I hope this helps someone who comes across this StackOverflow post like I did.

I has similar issue where the changeset wasn't being executed.. I changed the xsd version from 3.1 to 3.4 and it worked.

Related

In Spring application prevent H2 from transforming identifiers case to upper

I'm using Spring 2.6.2 and have tried both:
testImplementation("com.h2database:h2:2.1.214")
and
testImplementation("com.h2database:h2:1.4.200")
I create database schema's using raw SQL in JDBC connections to db during app startup e.g.
CREATE SCHEMA IF NOT EXISTS "001"
CREATE SCHEMA IF NOT EXISTS "002schema"
CREATE SCHEMA IF NOT EXISTS "3_schema"
Then Liquibase performs database migration.
When run against PostgreSQL, this works fine and schemas are created using the literal String provided. Tables inside those schemas are all lowercase too, as specified in the Liquibase changelogs.
When run against H2 in unit tests, an exception is thrown when performing database migration for 002schema. It looks like Liquibase then expects all schemas to have been upper-cased causing failure.
Exception during application start-up:
Initializing Liquibase for schema 001
Creating database history table with name: "001".DATABASECHANGELOG
Liquibase ran for schema 001
..
Initializing Liquibase for schema 002schema
Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'liquibaseMultiTenant' defined in class path resource [com/company/app/data/liquibase/LiquibaseConfiguration.class]: Invocation of init method failed; nested exception is liquibase.exception.LockException: liquibase.exception.DatabaseException: Schema "002SCHEMA" not found; SQL statement:
CREATE TABLE "002SCHEMA".DATABASECHANGELOGLOCK (ID INT NOT NULL, LOCKED BOOLEAN NOT NULL, LOCKGRANTED TIMESTAMP, LOCKEDBY VARCHAR(255), CONSTRAINT PK_DATABASECHANGELOGLOCK PRIMARY KEY (ID)) [90079-214] [Failed SQL: (90079) CREATE TABLE "002SCHEMA".DATABASECHANGELOGLOCK (ID INT NOT NULL, LOCKED BOOLEAN NOT NULL, LOCKGRANTED TIMESTAMP, LOCKEDBY VARCHAR(255), CONSTRAINT PK_DATABASECHANGELOGLOCK PRIMARY KEY (ID))]
I've tried the following options in H2 URL to prevent everything being made uppercase but none are working.
Any ideas?
spring:
datasource:
platform: h2
url: jdbc:h2:mem:test;MODE=PostgreSQL;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false;DATABASE_TO_UPPER=false;
driver-class-name: org.h2.Driver
username: sa
password:
Also tried:
spring:
datasource:
platform: h2
url: jdbc:h2:mem:test;MODE=PostgreSQL;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false;DATABASE_TO_LOWER=true;CASE_INSENSITIVE_IDENTIFIERS=true;
driver-class-name: org.h2.Driver
username: sa
password:
If the name of the schema is double quoted like "002SCHEMA" or "002schema" then it is case sensitive, it is not sensitive otherwise without the quotes:
Are PostgreSQL column names case-sensitive?
maybe you can solve the problem by changing this:
CREATE SCHEMA IF NOT EXISTS "002schema"
to this:
CREATE SCHEMA IF NOT EXISTS "002SCHEMA"
or somehow make liquibase not use quotes on schema name

Delete table in database using liquibase does not work

I am developing a spring boot app with hibernate an liquibase on a postsgre db. When i start the spring boot app, the changelog is updated in the db and the log says, that the script run successfully, but the table is still in the schema.
This is my db.changelog.xml:
<changeSet author="stimpson" id="deletetablehans">
<sqlFile dbms="postgre" encoding="UTF-8" endDelimiter=";"
path="sql/00003_deleteTableHans.sql" relativeToChangelogFile="true"
splitStatements="true" stripComments="true" />
</changeSet>
This is my script (deletetablehans.sql in the sql folder):
--liquibase formatted sql
--changeset stimpson:deleteTableHans
DROP TABLE HANS;
commit;
;
This is part of my logfile:
2021-01-25 15:43:50,438 INFO liquibase.lockservice : Changelog-Protokoll erfolgreich gesperrt.
2021-01-25 15:43:50,686 INFO liquibase.changelog : Reading from public.databasechangelog
2021-01-25 15:43:50,704 INFO liquibase.changelog : ChangeSet db/dbchangelog.xml::deletetablehans::stimpson ran successfully in 0ms
2021-01-25 15:43:50,709 INFO liquibase.lockservice : Successfully released change log lock
I do not care why the language changes, but when i look at the database, I see that the table named Hans is still there. But why? I did try with an explicit commit and the delimiter in its own line, but i do not understand the outcome?
This is my liquibase.properties file:
changeLogFile=src/main/resources/db/dbchangelog.xml
url=jdbc:postgresql://localhost:5432/padsyhw3
username=dbuser
password=dbpass
driver=org.postgresql.Driver
This is the only data row in the databasechangelog table in DB:
deletetablehans stimpson db/dbchangelog.xml 2021-01-25 15:49:05 1 EXECUTED 8:90e2fa99c6beeace580e429bd2bf9ae3 sqlFile 4.2.2
I have a working solution to your problem
I wonder if the case difference between deletetablehans and deleteTableHans could have an impact on the execution of the changeset
According to the liquibase supported databases, when using dbms for postgre you should use the value postgresql while you used postgre
Do you have any specific reason to use a changeset inside of an SQL file to do the drop action ? I can see at least two alternatives if no one can bring you a valid solution
Alternative 1: Use sql instead of sqlFile
<changeSet author="stimpson" id="deletetablehans">
<sql dbms="postgresql">
DROP TABLE HANS;
</sql>
</changeSet>
Alternative 2: Use liquibase dropTable
<changeSet author="stimpson" id="deletetablehans">
<dropTable tableName="HANS" />
</changeSet>

H2: Restore a (in-memory) database from a backup file

H2 provides a BACKUP command that can be used from a SQL statetement and creates a backup file:
String url = "jdbc:h2:nioMemFS:atestdb";
try (Connection con = DriverManager.getConnection(url);
Statement s = con.createStatement()) {
s.execute("CREATE TABLE test_table ( test_values VARCHAR(255) )");
s.execute("INSERT INTO test_table (test_values) VALUES ('abc'), "
+ "('def'), ('hji')");
s.execute("BACKUP TO 'backup.zip'"); // writes to backup.zip
}
This also works for in-memory databases (edit: it works with the nioMemFS file system but not with plain in-memory databases; see Oleg`s answer below). Is there also a command for restoring such a database file?
Thanks!
The BACKUP command shouldn't work for in-memory database, when I try I get DATABASE_IS_NOT_PERSISTENT error, if the backup command works for you, you're probably not using a persistent database.
You can use the SCRIPT and RUNSCRIPT to backup and restore respectively via creating and running an sql script from the database.
SCRIPT TO 'backup.sql';
RUNSCRIPT FROM 'backup.sql';

H2 and liquibase syntax error

I am usig liquibase for building database. And on startup of war the liquibase initializes Oracle db from application and makes it successfully. But in unit tests H2 db is used and here is a problem. Can't find smth similar in google. this is my changeset:
<changeSet author="Andriyanchik" id="EBS-639">
<sql endDelimiter="/">
CREATE OR REPLACE TRIGGER TR_SET_TYPE_NUMBER
BEFORE INSERT
ON PROVIDER
FOR EACH ROW
DECLARE
v_category_inc NUMBER;
BEGIN
SELECT MAX(TYPENUMBER) INTO v_category_inc FROM PROVIDER WHERE PRODUCTNAME = :NEW.PRODUCTNAME;
IF v_category_inc is null THEN
:NEW.TYPENUMBER := 0;
ELSE
:NEW.TYPENUMBER := v_category_inc + 1;
END IF;
END;
/
</sql>
</changeSet>
In H2 I am getting error:
Syntax error in SQL statement "CREATE OR REPLACE TRIGGER TR_SET_TYPE_NUMBER..."
expected "QUEUE, NOWAIT, AS, CALL"; SQL statement:

H2 in-memory database. Table not found

I've got a H2 database with URL "jdbc:h2:test". I create a table using CREATE TABLE PERSON (ID INT PRIMARY KEY, FIRSTNAME VARCHAR(64), LASTNAME VARCHAR(64));. I then select everything from this (empty) table using SELECT * FROM PERSON. So far, so good.
However, if I change the URL to "jdbc:h2:mem:test", the only difference being the database is now in memory only, this gives me an org.h2.jdbc.JdbcSQLException: Table "PERSON" not found; SQL statement: SELECT * FROM PERSON [42102-154]. I'm probably missing something simple here, but any help would be appreciated.
DB_CLOSE_DELAY=-1
hbm2ddl closes the connection after creating the table, so h2 discards it.
If you have your connection-url configured like this
jdbc:h2:mem:test
the content of the database is lost at the moment the last connection is closed.
If you want to keep your content you have to configure the url like this
jdbc:h2:mem:test;DB_CLOSE_DELAY=-1
If doing so, h2 will keep its content as long as the vm lives.
Notice the semicolon (;) rather than colon (:).
See the In-Memory Databases section of the Features page. To quote:
By default, closing the last connection to a database closes the database. For an in-memory database, this means the content is lost. To keep the database open, add ;DB_CLOSE_DELAY=-1 to the database URL. To keep the content of an in-memory database as long as the virtual machine is alive, use jdbc:h2:mem:test;DB_CLOSE_DELAY=-1.
I know this was not your case but I had the same problem because H2 was creating the tables with UPPERCASE names then behaving case-sensitive, even though in all scripts (including in the creation ones) i used lowercase.
Solved by adding ;DATABASE_TO_UPPER=false to the connection URL.
For Spring Boot 2.4+
use
spring.jpa.defer-datasource-initialization=true
in application.properties
Hard to tell. I created a program to test this:
package com.gigaspaces.compass;
import org.testng.annotations.Test;
import java.sql.*;
public class H2Test {
#Test
public void testDatabaseNoMem() throws SQLException {
testDatabase("jdbc:h2:test");
}
#Test
public void testDatabaseMem() throws SQLException {
testDatabase("jdbc:h2:mem:test");
}
private void testDatabase(String url) throws SQLException {
Connection connection= DriverManager.getConnection(url);
Statement s=connection.createStatement();
try {
s.execute("DROP TABLE PERSON");
} catch(SQLException sqle) {
System.out.println("Table not found, not dropping");
}
s.execute("CREATE TABLE PERSON (ID INT PRIMARY KEY, FIRSTNAME VARCHAR(64), LASTNAME VARCHAR(64))");
PreparedStatement ps=connection.prepareStatement("select * from PERSON");
ResultSet r=ps.executeQuery();
if(r.next()) {
System.out.println("data?");
}
r.close();
ps.close();
s.close();
connection.close();
}
}
The test ran to completion, with no failures and no unexpected output. Which version of h2 are you running?
When opening the h2-console, the JDBC URL must match the one specified in the properties:
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:testdb
spring.jpa.hibernate.ddl-auto=create
spring.jpa.show-sql=true
spring.h2.console.enabled=true
Which seems obvious, but I spent hours figuring this out..
The H2 in-memory database stores data in memory inside the JVM. When the JVM exits, this data is lost.
I suspect that what you are doing is similar to the two Java classes below. One of these classes creates a table and the other tries to insert into it:
import java.sql.*;
public class CreateTable {
public static void main(String[] args) throws Exception {
DriverManager.registerDriver(new org.h2.Driver());
Connection c = DriverManager.getConnection("jdbc:h2:mem:test");
PreparedStatement stmt = c.prepareStatement("CREATE TABLE PERSON (ID INT PRIMARY KEY, FIRSTNAME VARCHAR(64), LASTNAME VARCHAR(64))");
stmt.execute();
stmt.close();
c.close();
}
}
and
import java.sql.*;
public class InsertIntoTable {
public static void main(String[] args) throws Exception {
DriverManager.registerDriver(new org.h2.Driver());
Connection c = DriverManager.getConnection("jdbc:h2:mem:test");
PreparedStatement stmt = c.prepareStatement("INSERT INTO PERSON (ID, FIRSTNAME, LASTNAME) VALUES (1, 'John', 'Doe')");
stmt.execute();
stmt.close();
c.close();
}
}
When I ran these classes one after the other, I got the following output:
C:\Users\Luke\stuff>java CreateTable
C:\Users\Luke\stuff>java InsertIntoTable
Exception in thread "main" org.h2.jdbc.JdbcSQLException: Table "PERSON" not found; SQL statement:
INSERT INTO PERSON (ID, FIRSTNAME, LASTNAME) VALUES (1, 'John', 'Doe') [42102-154]
at org.h2.message.DbException.getJdbcSQLException(DbException.java:327)
at org.h2.message.DbException.get(DbException.java:167)
at org.h2.message.DbException.get(DbException.java:144)
...
As soon as the first java process exits, the table created by CreateTable no longer exists. So, when the InsertIntoTable class comes along, there's no table for it to insert into.
When I changed the connection strings to jdbc:h2:test, I found that there was no such error. I also found that a file test.h2.db had appeared. This was where H2 had put the table, and since it had been stored on disk, the table was still there for the InsertIntoTable class to find.
One reason can be that jpa tries to insert data before creating table structure, in order to solve this problem , insert this line in application.properties :
spring.jpa.defer-datasource-initialization=true
I have tried to add
jdbc:h2:mem:test;DB_CLOSE_DELAY=-1
However, that didn't helped. On the H2 site, I have found following, which indeed could help in some cases.
By default, closing the last connection to a database closes the database. For an in-memory database, this means the content is lost. To keep the database open, add ;DB_CLOSE_DELAY=-1 to the database URL. To keep the content of an in-memory database as long as the virtual machine is alive, use jdbc:h2:mem:test;DB_CLOSE_DELAY=-1.
However, my issue was that just the schema supposed to be different than default one. So insted of using
JDBC URL: jdbc:h2:mem:test
I had to use:
JDBC URL: jdbc:h2:mem:testdb
Then the tables were visible
Solved by creating a new src/test/resources folder + insert application.properties file, explicitly specifying to create a test dbase :
spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=create
I had the same problem and changed my configuration in application-test.properties to this:
#Test Properties
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.hibernate.ddl-auto=create-drop
And my dependencies:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/com.h2database/h2 -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.198</version>
<scope>test</scope>
</dependency>
And the annotations used on test class:
#RunWith(SpringRunner.class)
#DataJpaTest
#ActiveProfiles("test")
public class CommentServicesIntegrationTests {
...
}
I was trying to fetch table meta data, but had the following error:
Using:
String JDBC_URL = "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1";
DatabaseMetaData metaData = connection.getMetaData();
...
metaData.getColumns(...);
returned an empty ResultSet.
But using the following URL instead it worked properly:
String JDBC_URL = "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DATABASE_TO_UPPER=false";
There was a need to specify: DATABASE_TO_UPPER=false
I have tried adding ;DATABASE_TO_UPPER=false parameter, which it did work in a single test, but what did the trick for me was ;CASE_INSENSITIVE_IDENTIFIERS=TRUE.
At the end I had: jdbc:h2:mem:testdb;CASE_INSENSITIVE_IDENTIFIERS=TRUE
Moreover, the problem for me was when I upgraded to Spring Boot 2.4.1.
I came to this post because I had the same error.
In my case the database evolutions weren't been executed, so the table wasn't there at all.
My problem was that the folder structure for the evolution scripts was wrong.
from: https://www.playframework.com/documentation/2.0/Evolutions
Play tracks your database evolutions using several evolutions script. These scripts are written in plain old SQL and should be located in the conf/evolutions/{database name} directory of your application. If the evolutions apply to your default database, this path is conf/evolutions/default.
I had a folder called conf/evolutions.default created by eclipse. The issue disappeared after I corrected the folder structure to conf/evolutions/default
Had the exact same issue, tried all the above, but without success.
The rather funny cause of the error was that the JVM started too fast, before the DB table was created (using a data.sql file in src.main.resources). So I've put a Thread.sleep(1000) timer to wait for just a second before calling "select * from person".
Working flawlessly now.
application.properties:
spring.h2.console.enabled=true
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
data.sql:
create table person
(
id integer not null,
name varchar(255) not null,
location varchar(255),
birth_date timestamp,
primary key(id)
);
insert into person values (
10001, 'Tofu', 'home', sysdate()
);
PersonJdbcDAO.java:
public List<Person> findAllPersons(){
return jdbcTemplate.query("select * from person",
new BeanPropertyRowMapper<Person>(Person.class));
}
main class:
Thread.sleep(1000);
logger.info("All users -> {}", dao.findAllPersons());
I have tried the above solution,but in my case as suggested in the console added the property DB_CLOSE_ON_EXIT=FALSE, it fixed the issue.
spring.datasource.url=jdbc:h2:mem:testdb;DATABASE_TO_UPPER=false;DB_CLOSE_ON_EXIT=FALSE
I might be a little late to the party, but I faced exactly the same error and I tried pretty much every solution mentioned here and on other websites such as DATABASE_TO_UPPER=false;DB_CLOSE_DELAY=-1; DB_CLOSE_ON_EXIT=FALSE; IGNORECASE=TRUE
But nothing worked for me.
What worked for me was renaming data.sql to import.sql
I found it here - https://stackoverflow.com/a/53179547/8219358
Or
For Spring Boot 2.4+ use spring.jpa.defer-datasource-initialization=true in application.properties (mentioned here - https://stackoverflow.com/a/68086707/8219358)
I realize other solutions are more logical but none of them worked for me and this did.
Had similar problem
Solution was to add the following to application.properties
spring.jpa.defer-datasource-initialization=true
<bean id="benchmarkDataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.h2.Driver" />
<property name="url" value="jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1" />
<property name="username" value="sa" />
<property name="password" value="" />
</bean>
I found it working after adding the dependency of Spring Data JPA -
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
Add H2 DB configuration in application.yml -
spring:
datasource:
driverClassName: org.h2.Driver
initialization-mode: always
username: sa
password: ''
url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
h2:
console:
enabled: true
path: /h2
jpa:
database-platform: org.hibernate.dialect.H2Dialect
hibernate:
ddl-auto: none
The issue can also happen if there was error while generating the table.
If the entities use any features which are not supported by H2 (for example, specifying a non-standard columnDefinition), the schema generation will fail and test will continue without the database generated.
In this case, somewhere in the logs you will find this:
WARN ExceptionHandlerLoggedImpl: GenerationTarget encountered exception accepting command :
Error executing DDL "create table ..." via JDBC Statement
In my case missing table error was happening during jpa test, table was created by schem.sql file, problem was fixed after puting #org.springframework.transaction.annotation.Transactional on test
In my case, I had used the special keywords for my column-names in the table, H2 Database. If you're using different databases avoid those special keywords across different databases. Spring & Hibernate isn't smart enough to tell you exactly which column names are prohibited or where the exact error is in the table-creation.
Keywords such as;
desc, interval, metric
To resolve the issues I was experiencing, I renamed those fields to:
descr, time_interval, time_metric
http://www.h2database.com/html/advanced.html
Use the same in applications.properties file
spring.jpa.show-sql=true
spring.datasource.url=jdbc:h2:mem:testdb;DATABASE_TO_UPPER=false
DB_CLOSE_ON_EXIT=FALSE
spring.data.jpa.repositories.bootstrap-mode=default
spring.h2.console.enabled=true spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=create
spring.datasource.driverClassName=org.h2.Driver
spring.jpa.defer-datasource-initialization=true
Caused by: org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "user" not found; SQL statement:
in my case, my table name was user but from H2 2.1.212 user is reserved so couldn't make the table
changed table name users by #Table(name="users") and
datasource:
url: jdbc:h2:mem:testdb;DATABASE_TO_UPPER=false;DB_CLOSE_DELAY=-1;
and it works now
I suspect the database you opened is a brand new db, not your application db. This is because:
H2 in-memory database by default is private to the JVM and the classloader. It cannot be connected from another process/with TCP from same machine/from another machine; that's why all the guides suggest using H2 console, because that's within the same JVM of your application so it can access the database(however, my H2 console shipped with Spring 2.6 is not working, I need to find another way)
H2 database can be launched in server mode, but ";AUTO_SERVER=true" does not work with in-memory db; it only can be added to the URL when you use a file based db; and to visit it you need to use absolute path to the db file, which is not portable and is ugly; additionally, auto-generation of tables are not done when you use a file so you need to create an init.sql to create tables. AND, when you connect H2 still tells you that you need server mode, because there is already one connection to the db(your app) and to allow 1+ connection, you need server mode
So in both cases you need server mode. How?
In Spring you need to create the DB as a bean(thanks to How to enable H2 Database Server Mode in Spring Boot); put this into a #Configuration and you are done:
#Bean(initMethod = "start", destroyMethod = "stop")
public Server h2Server() throws SQLException {
return Server.createTcpServer("-tcp", "-tcpAllowOthers", "-tcpPort", "1234"); // or any other port
}
Your db url:
spring:
datasource:
url: jdbc:h2:mem:test
driver-class-name: org.h2.Driver
port: 1234
username: sa
password: sa
That's all. You can connect with H2 console, or DB Navigator, or other tools along with your app right now. The connection string is:
jdbc:h2:tcp://localhost:1234/mem:test

Categories

Resources