Preloader image

This is an example of how to use the Liquibase tool to import a database programmatically.

Import Database By Liquibase

To use Liquibase, at application runtime within the application server, we will use the application server’s DataSource, but for unit testing/integrated testing, we will use a configured DataSource to use an in-memory database.

public class ImportByLiquibase {
	private static final Logger LOGGER = Logger.getInstance(LogCategory.OPENEJB,
			EntityManagerFactoryCallable.class.getName());

	public static final String IMPORT_FILE_PREFIX = "V_";
	public static final String IMPORT_FILE_EXTENSION = ".sql";

	private final DataSource dataSource;
	private final String resource;

	public ImportByLiquibase(final ClassLoader cl, final String resource, final DataSource ds) {
		this.dataSource = ds;
		this.resource = resource;

		if (dataSource == null) {
			throw new NullPointerException("datasource can't be null");
		}

	}

	@SuppressWarnings("deprecation")
	public void doImport() {

		try {

			List<String> sqlFiles = new ArrayList<String>();

			try {

				sqlFiles = listFilteredFiles(resource, Integer.MAX_VALUE);

				if (Objects.nonNull(sqlFiles)) {
					if (sqlFiles.isEmpty()) {
						LOGGER.error("The Resource directory for sql files, can not to be empty.");
						throw new Exception("The Resource directory for sql files, can not to be empty.");
					}
				}

			} catch (final IOException e) {
				throw new OpenEJBRuntimeException("The Resource directory for sql files, can not to be empty.", e);
			}
			ChangeLogHistoryServiceFactory.getInstance().resetAll();
			for (String changelogPath : sqlFiles) {

				try (Liquibase liquibase = getLiquibase(changelogPath)) {
					liquibase.update("test");

				} catch (Exception e) {
					LOGGER.error("Error running Liquibase changelog", e);
					throw new RuntimeException("Error running Liquibase changelog", e);
				}
			}

		} catch (final Exception e) {
			LOGGER.error("Can not create a statement, import scripts will be ignored", e);
			return;
		}

	}

	public List<String> listFilteredFiles(String dir, int depth) throws IOException {
		try (Stream<Path> stream = Files.walk(Paths.get(dir), depth)) {
			return stream.filter(file -> !Files.isDirectory(file))
					.filter(file -> !file.getFileName().toString().startsWith(".")).map(Path::toString)
					.map(s -> s.replaceFirst(resource, "")).collect(Collectors.toList());
		}
	}

	private Liquibase getLiquibase(final String changelogPath) throws DatabaseException, SQLException {
		Liquibase liquibase = null;
		HsqlConnection hsqlConnection = new HsqlConnection(dataSource.getConnection());

		try {
			Path path = Paths.get(this.resource);

			ResourceAccessor resourceAccessor = new SearchPathResourceAccessor(
					new DirectoryResourceAccessor(path.toFile()));

			liquibase = new Liquibase(changelogPath, resourceAccessor, hsqlConnection);

		} catch (FileNotFoundException ex) {
			LOGGER.error("can't create a statement, import scripts will be ignored", ex);

		} catch (LiquibaseException ex) {
			LOGGER.error("can't create a statement, import scripts will be ignored", ex);
		}
		return liquibase;
	}

}

The unit test is tied to a set of SQL statements, defined in the scripts 0010_create_table_test.sql and 0020_insert_table_test.sql:

0010_create_table_test.sql
----
CREATE TABLE public.table_test (ID INT NOT NULL, DESCRIPTION VARCHAR(120) NOT NULL, PRIMARY KEY (ID));
----

0020_insert_table_test.sql

insert into public.table_test (id, description) values(1, 'Test 1');
insert into public.table_test (id, description) values(2, 'Test 2');
insert into public.table_test (id, description) values(3, 'Test 3');

Running the unit test:

Run the unit test for the ImportByLiquibaseTest class. The following steps must be followed:

Run the build of the examples/import-database-liquibase project:

mvn clean install

The following output should be displayed:

_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
 T E S T S
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

Running org.apache.openejb.assembler.classic.migrate.database.ImportByLiquibaseTest
[Server@50b494a6]: [Thread[main,5,main]]: checkRunning(false) entered
[Server@50b494a6]: [Thread[main,5,main]]: checkRunning(false) exited
[Server@50b494a6]: [Thread[main,5,main]]: setDatabaseName(0,testdb)
[Server@50b494a6]: [Thread[main,5,main]]: checkRunning(false) entered
[Server@50b494a6]: [Thread[main,5,main]]: checkRunning(false) exited
[Server@50b494a6]: [Thread[main,5,main]]: setDatabasePath(0,mem:testdb;sql.enforce_strict_size=true;sql.restrict_exec=true)
[Server@50b494a6]: [Thread[main,5,main]]: checkRunning(false) entered
[Server@50b494a6]: [Thread[main,5,main]]: checkRunning(false) exited
[Server@50b494a6]: [Thread[main,5,main]]: setPort(9001)
[Server@50b494a6]: [Thread[main,5,main]]: start() entered
[Server@50b494a6]: [Thread[HSQLDB Server @50b494a6,5,main]]: run() entered
[Server@50b494a6]: Initiating startup sequence...
[Server@50b494a6]: [Thread[HSQLDB Server @50b494a6,5,main]]: server.maxdatabases=10
[Server@50b494a6]: [Thread[HSQLDB Server @50b494a6,5,main]]: server.tls=false
[Server@50b494a6]: [Thread[HSQLDB Server @50b494a6,5,main]]: server.port=9001
[Server@50b494a6]: [Thread[HSQLDB Server @50b494a6,5,main]]: server.trace=false
[Server@50b494a6]: [Thread[HSQLDB Server @50b494a6,5,main]]: server.database.0=mem:testdb;sql.enforce_strict_size=true;sql.restrict_exec=true
[Server@50b494a6]: [Thread[HSQLDB Server @50b494a6,5,main]]: server.restart_on_shutdown=false
[Server@50b494a6]: [Thread[HSQLDB Server @50b494a6,5,main]]: server.no_system_exit=true
[Server@50b494a6]: [Thread[HSQLDB Server @50b494a6,5,main]]: server.silent=true
[Server@50b494a6]: [Thread[HSQLDB Server @50b494a6,5,main]]: server.default_page=index.html
[Server@50b494a6]: [Thread[HSQLDB Server @50b494a6,5,main]]: server.dbname.0=testdb
[Server@50b494a6]: [Thread[HSQLDB Server @50b494a6,5,main]]: server.address=0.0.0.0
[Server@50b494a6]: [Thread[HSQLDB Server @50b494a6,5,main]]: server.root=.
[Server@50b494a6]: [Thread[HSQLDB Server @50b494a6,5,main]]: openServerSocket() entered
[Server@50b494a6]: [Thread[HSQLDB Server @50b494a6,5,main]]: Got server socket: ServerSocket[addr=0.0.0.0/0.0.0.0,localport=9001]
[Server@50b494a6]: Server socket opened successfully in 174 ms.
[Server@50b494a6]: [Thread[HSQLDB Server @50b494a6,5,main]]: openServerSocket() exiting
[Server@50b494a6]: [Thread[HSQLDB Server @50b494a6,5,main]]: openDatabases() entered
[Server@50b494a6]: [Thread[HSQLDB Server @50b494a6,5,main]]: Opening database: [mem:testdb]
[Server@50b494a6]: Database [index=0, id=0, db=mem:testdb, alias=testdb] opened successfully in 613 ms.
[Server@50b494a6]: [Thread[HSQLDB Server @50b494a6,5,main]]: openDatabases() exiting
[Server@50b494a6]: Startup sequence completed in 828 ms.
[Server@50b494a6]: 2024-09-28 00:47:40.682 HSQLDB server 2.7.3 is online on port 9001
[Server@50b494a6]: To close normally, connect and execute SHUTDOWN SQL
[Server@50b494a6]: From command line, use [Ctrl]+[C] to abort abruptly
[Server@50b494a6]: [Thread[main,5,main]]: start() exiting
set. 27, 2024 9:47:40 PM com.zaxxer.hikari.HikariDataSource <init>
INFORMAÇÕES: hikariCP - Starting...
set. 27, 2024 9:47:41 PM com.zaxxer.hikari.pool.PoolBase getAndSetNetworkTimeout
INFORMAÇÕES: hikariCP - Driver does not support get/set network timeout for connections. (feature not supported)
set. 27, 2024 9:47:41 PM com.zaxxer.hikari.pool.HikariPool checkFailFast
INFORMAÇÕES: hikariCP - Added connection org.hsqldb.jdbc.JDBCConnection@4d49af10
set. 27, 2024 9:47:41 PM com.zaxxer.hikari.HikariDataSource <init>
INFORMAÇÕES: hikariCP - Start completed.
set. 27, 2024 9:47:41 PM liquibase.database
INFORMAÇÕES: Set default schema name to PUBLIC
set. 27, 2024 9:47:42 PM liquibase.changelog
INFORMAÇÕES: Creating database history table with name: PUBLIC.DATABASECHANGELOG
set. 27, 2024 9:47:42 PM liquibase.changelog
INFORMAÇÕES: Reading from PUBLIC.DATABASECHANGELOG
set. 27, 2024 9:47:42 PM liquibase.lockservice
INFORMAÇÕES: Successfully acquired change log lock
set. 27, 2024 9:47:42 PM liquibase.command
INFORMAÇÕES: Using deploymentId: 7484462456
set. 27, 2024 9:47:42 PM liquibase.changelog
INFORMAÇÕES: Reading from PUBLIC.DATABASECHANGELOG
Running Changeset: 0010_create_table_test.sql::1::user
set. 27, 2024 9:47:42 PM liquibase.changelog
INFORMAÇÕES: Custom SQL executed
set. 27, 2024 9:47:42 PM liquibase.changelog
INFORMAÇÕES: ChangeSet 0010_create_table_test.sql::1::user ran successfully in 37ms

UPDATE SUMMARY
Run:                          1
Previously run:               0
Filtered out:                 0
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
Total change sets:            1

set. 27, 2024 9:47:42 PM liquibase.util
INFORMAÇÕES: UPDATE SUMMARY
set. 27, 2024 9:47:42 PM liquibase.util
INFORMAÇÕES: Run:                          1
set. 27, 2024 9:47:42 PM liquibase.util
INFORMAÇÕES: Previously run:               0
set. 27, 2024 9:47:42 PM liquibase.util
INFORMAÇÕES: Filtered out:                 0
set. 27, 2024 9:47:42 PM liquibase.util
INFORMAÇÕES: -------------------------------
set. 27, 2024 9:47:42 PM liquibase.util
INFORMAÇÕES: Total change sets:            1
set. 27, 2024 9:47:42 PM liquibase.util
INFORMAÇÕES: Update summary generated
set. 27, 2024 9:47:42 PM liquibase.command
INFORMAÇÕES: Update command completed successfully.
Liquibase: Update has been successful. Rows affected: 1
set. 27, 2024 9:47:42 PM liquibase.lockservice
INFORMAÇÕES: Successfully released change log lock
set. 27, 2024 9:47:42 PM liquibase.command
INFORMAÇÕES: Command execution complete
set. 27, 2024 9:47:42 PM liquibase.database
INFORMAÇÕES: Set default schema name to PUBLIC
set. 27, 2024 9:47:42 PM liquibase.changelog
INFORMAÇÕES: Reading from PUBLIC.DATABASECHANGELOG
set. 27, 2024 9:47:42 PM liquibase.lockservice
INFORMAÇÕES: Successfully acquired change log lock
set. 27, 2024 9:47:42 PM liquibase.command
INFORMAÇÕES: Using deploymentId: 7484462849
set. 27, 2024 9:47:42 PM liquibase.changelog
INFORMAÇÕES: Reading from PUBLIC.DATABASECHANGELOG
Running Changeset: 0020_insert_table_test.sql::1::user
set. 27, 2024 9:47:42 PM liquibase.changelog
INFORMAÇÕES: Custom SQL executed
set. 27, 2024 9:47:42 PM liquibase.changelog
INFORMAÇÕES: ChangeSet 0020_insert_table_test.sql::1::user ran successfully in 7ms
Running Changeset: 0020_insert_table_test.sql::2::user
set. 27, 2024 9:47:42 PM liquibase.changelog
INFORMAÇÕES: Custom SQL executed
set. 27, 2024 9:47:42 PM liquibase.changelog
INFORMAÇÕES: ChangeSet 0020_insert_table_test.sql::2::user ran successfully in 10ms
Running Changeset: 0020_insert_table_test.sql::3::user
set. 27, 2024 9:47:42 PM liquibase.changelog
INFORMAÇÕES: Custom SQL executed
set. 27, 2024 9:47:42 PM liquibase.changelog
INFORMAÇÕES: ChangeSet 0020_insert_table_test.sql::3::user ran successfully in 18ms

UPDATE SUMMARY
Run:                          3
Previously run:               0
Filtered out:                 0
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
Total change sets:            3

set. 27, 2024 9:47:42 PM liquibase.util
INFORMAÇÕES: UPDATE SUMMARY
set. 27, 2024 9:47:42 PM liquibase.util
INFORMAÇÕES: Run:                          3
set. 27, 2024 9:47:42 PM liquibase.util
INFORMAÇÕES: Previously run:               0
set. 27, 2024 9:47:42 PM liquibase.util
INFORMAÇÕES: Filtered out:                 0
set. 27, 2024 9:47:42 PM liquibase.util
INFORMAÇÕES: -------------------------------
set. 27, 2024 9:47:42 PM liquibase.util
INFORMAÇÕES: Total change sets:            3
set. 27, 2024 9:47:42 PM liquibase.util
INFORMAÇÕES: Update summary generated
set. 27, 2024 9:47:42 PM liquibase.command
INFORMAÇÕES: Update command completed successfully.
Liquibase: Update has been successful. Rows affected: 6
set. 27, 2024 9:47:43 PM liquibase.lockservice
INFORMAÇÕES: Successfully released change log lock
set. 27, 2024 9:47:43 PM liquibase.command
INFORMAÇÕES: Command execution complete
INFORMAÇÕES - id:1 description:Test 1
INFORMAÇÕES - id:2 description:Test 2
INFORMAÇÕES - id:3 description:Test 3
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 3.647 sec

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0