ATS Global

Configuring an External MariaDB Database in Liferay DXP

Learn how to connect Liferay DXP to an external MariaDB database for custom application data. This guide explains the database configuration, datasource setup, and an Insurance Self-Service Portal example for managing policy and claim information.

LIFERAY DXPPublished: OCT 29, 20268 min readBy Jyothi N Share
Configuring an External MariaDB Database in Liferay DXP
Introduction

Liferay DXP uses a relational database to store the structured information required by the portal. In some projects, however, custom application entities may need to use a separate database rather than the primary Liferay database.

This article explains how to configure an external MariaDB database for custom Service Builder entities in Liferay DXP.

The implementation uses JDBC configuration, a custom DataSourceProvider, and Java Service Provider Interface (SPI) registration.

To demonstrate the approach, we use an Insurance Self-Service Portal with two custom entities: Policy and Claim. The primary Liferay database remains separate from the database used by these custom entities.

The guide covers the architecture, configuration, implementation, deployment, and verification of the database integration.

Why Use Multiple Databases with Liferay DXP?

Liferay DXP applications are not always limited to a single database connection. Depending on the application architecture and supported integration approach, additional datasources can be configured for custom modules and application-specific data.

For example, a Liferay implementation can use:

  • Primary database (lportal): Stores Liferay's core portal data.
  • External database (insurance_db): Stores the custom Policy and Claim entity records in our example.

This approach helps separate custom application data from the primary portal database and gives developers greater flexibility when designing data access for their applications.

How Does It Work?

The primary datasource is configured using jdbc.default.* properties, while our example defines an additional datasource using jdbc.ext.*.

A custom DataSourceProvider implementation reads the external datasource configuration. Java SPI registration makes the provider discoverable, and the relevant Service Builder integration uses the provider to obtain the datasource.

Key takeaway: Multiple database connections can be used in a Liferay solution, but adding datasource properties alone is not sufficient. Each additional datasource must be configured and integrated with the relevant application module correctly.

1. Understanding the Architecture

The example uses two logical databases:

  • lportal — the primary database configured for Liferay DXP.
  • insurance_db — the separate database intended for the custom Policy and Claim entities.

Both databases can run in the same MariaDB container, but they remain separate databases.

Architecture Flow

┌───────────────────────────────┐│        User / API Client      │└───────────────┬───────────────┘                │ HTTP request                ▼┌───────────────────────────────┐│         Liferay DXP           ││        Headless API           │└───────────────┬───────────────┘                ▼┌───────────────────────────────┐│      Service Builder          ││     Policy / Claim Service    │└───────────────┬───────────────┘                ▼┌───────────────────────────────┐│       DataSourceProvider      ││   Custom datasource setup     │└───────────────┬───────────────┘                ▼┌───────────────────────────────┐│        JDBC Datasource        ││       jdbc.ext.* settings     │└───────────────┬───────────────┘                ▼┌───────────────────────────────┐│       MariaDB Database        ││         insurance_db          ││       policies | claims       │└───────────────────────────────┘

The external database contains two separate tables:

  • policies — stores insurance policy information.
  • claims — stores claim submissions and their status.

The primary Liferay database continues to serve the portal's normal database requirements. The external datasource is configured for the custom application entities.

This separation allows the custom application's database connection to be configured independently of Liferay's default datasource.

2. Environment and Prerequisites

The original local datasource implementation was tested using the following environment. The insurance scenario below adapts that approach; its entities and API operations require their own end-to-end verification.

ComponentConfiguration
Liferay DXP2026.q2.9
Liferay containerliferay-test
MariaDB containerliferay-mariadb-test
MariaDB version11.8
Primary databaselportal
External databaseinsurance_db
Custom entitiesPolicy and Claim
API testingLiferay Swagger interface

The primary and external database configurations use the MariaDB JDBC driver. Verify database-version compatibility against the official compatibility matrix for the exact Liferay DXP release before using this configuration in another environment.

3. Configure the External Datasource

Liferay's database connection properties are configured in portal-ext.properties.

The primary datasource uses the jdbc.default.* prefix, while this example uses jdbc.ext.* for the external datasource.

portal-ext.properties

# Primary Liferay datasourcejdbc.default.driverClassName=org.mariadb.jdbc.Driverjdbc.default.url=jdbc:mariadb://liferay-mariadb-test:3306/lportaljdbc.default.username=liferayjdbc.default.password=<primary-database-password>
# External application datasourcejdbc.ext.driverClassName=org.mariadb.jdbc.Driverjdbc.ext.url=jdbc:mariadb://liferay-mariadb-test:3306/insurance_dbjdbc.ext.username=liferayjdbc.ext.password=<external-database-password>

Replace the placeholders with the credentials configured for your environment. Do not publish real database passwords in source code, blog posts, or screenshots.

Because Liferay and MariaDB run in the same Docker network, the MariaDB container name can be used as the database hostname.

Important: Defining jdbc.ext.* properties does not automatically make every Liferay service use the external database. The custom Service Builder datasource provider must be implemented and integrated correctly.

4. What Is a DataSourceProvider?

A datasource represents a way for an application to obtain database connections.

In this implementation approach, the custom class DataSourceProviderImpl implements Liferay's DataSourceProvider interface:

com.liferay.portal.kernel.dao.jdbc.DataSourceProvider

The interface defines the contract for supplying a datasource. Our implementation reads the external datasource properties and initializes the JDBC datasource.

The central implementation is:

return DataSourceFactoryUtil.initDataSource(    PropsUtil.getProperties("jdbc.ext.", true));

Here is what happens:

  • PropsUtil.getProperties("jdbc.ext.", true) reads the configured properties with the jdbc.ext. prefix.
  • DataSourceFactoryUtil.initDataSource(...) initializes a datasource using those properties.
  • The resulting datasource provides the database connection capability required by the relevant custom service integration.

This allows the external datasource configuration to be maintained separately from the default Liferay datasource.

5. How Java SPI Discovers the Provider

Java SPI stands for Service Provider Interface. It is a mechanism for discovering implementations of an interface without requiring the consuming code to instantiate a particular implementation class directly.

In this project structure, SPI registration connects the DataSourceProvider interface to the custom implementation.

The implementation class is:

public class DataSourceProviderImpl    implements DataSourceProvider {    // Datasource initialization implementation}

The SPI registration file is located at:

META-INF/services/com.liferay.portal.kernel.dao.jdbc.DataSourceProvider

The file contains the fully qualified name of the implementation class, for example:

com.example.insurance.internal.dao.jdbc.DataSourceProviderImpl

The registration file tells Java's service-loading mechanism which implementation provides the interface.

The roles are different:

  • DataSourceProvider: Defines the provider contract.
  • DataSourceProviderImpl: Implements the datasource initialization logic.
  • Java SPI registration: Makes the implementation discoverable.
  • JDBC datasource: Provides the connection capability used to access MariaDB.

SPI discovery alone does not establish a database connection. The provider must also be invoked through the appropriate Liferay integration, and the datasource configuration and JDBC driver must be valid.

Java SPI and DataSourceProvider Flow

┌──────────────────────────────────────┐│       DataSourceProvider Interface   ││       Defines provider contract      │└───────────────────┬──────────────────┘                    │ implemented by                    ▼┌──────────────────────────────────────┐│        DataSourceProviderImpl        ││  Reads jdbc.ext.* properties         ││  Initializes JDBC datasource         │└───────────────────┬──────────────────┘                    ▼┌──────────────────────────────────────┐│        Java SPI Registration         ││ META-INF/services/                   ││ com.liferay.portal.kernel.dao.jdbc.  ││ DataSourceProvider                   │└───────────────────┬──────────────────┘                    │ discovered by                    ▼┌──────────────────────────────────────┐│    Relevant Liferay Integration      ││ Invokes provider and uses datasource │└───────────────────┬──────────────────┘                    ▼┌──────────────────────────────────────┐│        MariaDB: insurance_db         │└──────────────────────────────────────┘

6. Build the Service Builder and Headless API Modules

The Insurance Self-Service Portal example uses Service Builder for its custom Policy and Claim entities and REST Builder for the Headless API.

The build process generates the service code and builds the modules required by the application.

From the Liferay workspace root, run the relevant Gradle tasks for the corresponding module names:

gradlew.bat :modules:insurance-service:insurance-service-service:buildServicegradlew.bat :modules:insurance-service:insurance-service-service:buildgradlew.bat :modules:insurance-service:insurance-service-api:buildgradlew.bat :modules:insurance-headlessAPI:insurance-headlessAPI-impl:buildRESTgradlew.bat :modules:insurance-headlessAPI:insurance-headlessAPI-impl:build

These paths are illustrative. Adjust them to match the actual module names and directory structure in your workspace.

Confirm that the generated JAR files are available in the respective modules' build\libs directories.

The exact generated filenames may vary depending on the project configuration.

7. Deploy the Modules to Liferay

Deploy the required JARs to the Liferay container. The example uses these illustrative modules:

  • insurance.service.api-1.0.0.jar
  • insurance.service.service-1.0.0.jar
  • insurance.headlessAPI.impl-1.0.0.jar

The JARs can be copied into the container and moved to Liferay's deployment directory. Ensure that the files have the appropriate ownership and permissions for the container's Liferay process.

After deployment, verify the Liferay logs and confirm that the relevant OSGi bundles are active.

8. Prepare the External Database Tables

For this example, the insurance_db database contains the custom tables for policy and claim records.

The illustrative tables are:

  • policies
  • claims

The policies table stores insurance policy information, such as a policy number, policyholder reference, policy type, coverage amount, and policy status.

The claims table stores claim information, such as a claim reference, associated policy identifier, claim date, claim amount, and claim status.

The exact columns and data types must match the Service Builder entity definitions and the database schema used by the application.

The tables can be verified from the MariaDB client:

SHOW TABLES;

9. Test the Integration Through the Headless API

After deploying the modules and preparing the tables, test the Insurance Self-Service Portal functionality through the Liferay Swagger interface.

The following are proposed test scenarios for the insurance example, not results from an implemented insurance application.

Test 1: Create an Insurance Policy

Submit a request to create a policy with example values such as:

  • Policy number: POL-1001
  • Policy type: Health Insurance
  • Coverage amount: 500000
  • Policy status: Active

Verify that the API response contains the created policy information and that the corresponding record exists in the policies table.

Test 2: Submit a Claim Linked to a Policy

Submit a claim associated with the previously created policy.

For example:

  • Policy identifier: the ID of POL-1001
  • Claim amount: 25000
  • Claim status: Submitted

The service should validate that the referenced policy exists, save the claim, and store the associated policy identifier.

Test 3: Verify the Records in MariaDB

Query the external database:

SELECT * FROM policies;
SELECT * FROM claims;

Verify that the policy and claim records are present in insurance_db.

The GET endpoints should also return the expected policy and claim information.

These tests can demonstrate that the Policy and Claim operations use the external database once the insurance modules, datasource integration, schema, and API endpoints have been implemented and verified.

Conclusion

Using an external MariaDB database for custom Liferay Service Builder entities involves more than adding another JDBC URL. The datasource properties, provider implementation, Java SPI registration, module deployment, and database schema must work together.

The Insurance Self-Service Portal is an illustrative adaptation of that approach, with Policy and Claim entities intended to use a shared external database separate from Liferay's primary database.

Before using the approach in production, validate the supported database version, secure credentials, configure backups and monitoring, and test schema management and recovery procedures.

Working on something similar? See how ATS Global approaches Liferay DXP development, or browse our case studies.

Found this useful? Share it with your network.

Let’s work together

Ready to build something great?

Tell us about your project. We respond within one business day and can provide a customised quote for your requirements.