Exposed 1.0.0-rc-3 Help

Working with Databases

In Exposed, the Database and R2dbcDatabase classes represent a database instance, and encapsulates the necessary connection details and configuration required to interact with a specific database.

Choosing between JDBC and R2DBC

Exposed supports both JDBC and R2DBC as transport layers for database connectivity. Understanding their differences will help you choose the right approach for your application:

JDBC

JDBC (Java Database Connectivity) is the traditional, synchronous and blocking API used for interacting with relational databases. Exposed's JDBC integration is well-established with broad database support and extensive tooling. It’s ideal for:

  • Traditional applications where simplicity is preferred over scalability.

  • Projects using connection pools and transaction management in a synchronous context.

JDBC is well-supported and works seamlessly with most existing database drivers and tools.

R2DBC

R2DBC (Reactive Relational Database Connectivity) is a non-blocking, asynchronous alternative to JDBC. Exposed's R2DBC support enables integration with reactive frameworks and Kotlin coroutines. Use R2DBC if:

  • You're building a high-concurrency, I/O-bound application.

  • You want to avoid thread-blocking and leverage Kotlin coroutines end-to-end.

R2DBC is still evolving, and not all databases or features are supported equally compared to JDBC.

Choose JDBC when you need simplicity, broad database compatibility, or are building a traditional application with moderate concurrency requirements. Choose R2DBC when building reactive applications, working with Kotlin coroutines, or needing to handle many concurrent connections efficiently with limited resources.

Connecting to a Database

Every database access in Exposed begins by establishing a connection and creating a transaction.

To connect to a database, you first need to tell Exposed of the connection details. You have two options:

These functions do not immediately establish a connection. Instead, they provide a descriptor for future usage. An actual connection is only established when a transaction is initiated.

To get a database instance using simple connection parameters, use the following approach:

val h2db = Database.connect("jdbc:h2:mem:test", driver = "org.h2.Driver")
val h2db = R2dbcDatabase.connect("r2dbc:h2:mem:///test")

H2

In order to use H2, you need to add the H2 driver dependency:

implementation("com.h2database:h2:2.2.224")
implementation("io.r2dbc:r2dbc-h2:1.0.0.RELEASE")

Then connect to a database:

val h2dbFromFile = Database.connect("jdbc:h2:./myh2file", driver = "org.h2.Driver")
val h2dbFromFile = R2dbcDatabase.connect("r2dbc:h2:file///./myh2file")

Or in-memory database:

val h2db = Database.connect("jdbc:h2:mem:test", driver = "org.h2.Driver")
val h2db = R2dbcDatabase.connect("r2dbc:h2:mem:///test")

By default, H2 closes the database when the last connection is closed. If you want to keep the database open, you can use the DB_CLOSE_DELAY=-1 option:

Database.connect("jdbc:h2:mem:regular;DB_CLOSE_DELAY=-1;", "org.h2.Driver")

MariaDB

Add the required dependency:

implementation("org.mariadb.jdbc:mariadb-java-client:3.3.1")
implementation("org.mariadb:r2dbc-mariadb:1.3.0")

Connect to a database:

val mariadb = Database.connect( "jdbc:mariadb://localhost:3306/test", driver = "org.mariadb.jdbc.Driver", user = "root", password = "your_pwd" )
val mariadb = R2dbcDatabase.connect( "r2dbc:mariadb://localhost:3306/test", driver = "mariadb", user = "root", password = "your_pwd" )

MySQL

Add the required dependency:

implementation("mysql:mysql-connector-java:8.0.33")
implementation("io.asyncer:r2dbc-mysql:1.3.2")

Connect to a database:

val mysqldb = Database.connect( "jdbc:mysql://localhost:3306/test", driver = "com.mysql.cj.jdbc.Driver", user = "user", password = "password" )
val mysqldb = R2dbcDatabase.connect( "r2dbc:mysql://localhost:3306/test", driver = "mysql", user = "user", password = "password" )

Oracle

Add the required dependency:

implementation("com.oracle.database.jdbc:ojdbc8:19.24.0.0")
implementation("com.oracle.database.r2dbc:oracle-r2dbc:1.3.0")

Connect to a database:

val oracledb = Database.connect( "jdbc:oracle:thin:@//localhost:1521/test", driver = "oracle.jdbc.OracleDriver", user = "user", password = "password" )
val oracledb = R2dbcDatabase.connect( "r2dbc:oracle://localhost:3306/test", driver = "oracle", user = "user", password = "password" )

PostgreSQL

Add the required dependency:

implementation("org.postgresql:postgresql:42.7.3")
implementation("org.postgresql:r2dbc-postgresql:1.0.7.RELEASE")

Connect to a database:

val postgresqldb = Database.connect( "jdbc:postgresql://localhost:12346/test", driver = "org.postgresql.Driver", user = "user", password = "password" )
val postgresqldb = R2dbcDatabase.connect( url = "r2dbc:postgresql://db:5432/test", driver = "postgresql", user = "user", password = "password" )

PostgreSQL using the pgjdbc-ng JDBC driver

Add the required dependency:

implementation("com.impossibl.pgjdbc-ng:pgjdbc-ng:0.8.9")

Connect to a database:

val postgresqldbNG = Database.connect( "jdbc:pgsql://localhost:12346/test", driver = "com.impossibl.postgres.jdbc.PGDriver", user = "user", password = "password" )

SQL Server

Add the required dependency:

implementation("com.microsoft.sqlserver:mssql-jdbc:9.4.1.jre8")
implementation("io.r2dbc:r2dbc-mssql:1.0.2.RELEASE")

Connect to a database:

val sqlserverdb = Database.connect( "jdbc:sqlserver://localhost:32768;databaseName=test", "com.microsoft.sqlserver.jdbc.SQLServerDriver", user = "user", password = "password" )
val sqlserverdb = R2dbcDatabase.connect( "r2dbc:mssql://localhost:32768;databaseName=test", driver = "sqlserver", user = "user", password = "password" )

SQLite

Add the required dependency:

implementation("org.xerial:sqlite-jdbc:3.49.1.0")

Connect to a database:

Database.connect("jdbc:sqlite:/data/data.db", "org.sqlite.JDBC")

Or an in-memory database:

Database.connect("jdbc:sqlite:file:test?mode=memory&cache=shared", "org.sqlite.JDBC")

Set SQLite compatible isolation level:

TransactionManager.manager.defaultIsolationLevel = Connection.TRANSACTION_SERIALIZABLE // or Connection.TRANSACTION_READ_UNCOMMITTED
Last modified: 27 November 2025