documentation-website/Writerside/topics/Working-with-Database.md
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.
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 (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:
JDBC is well-supported and works seamlessly with most existing database drivers and tools.
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:
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.
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:
Database.connect() for traditional JDBC-based access.R2dbcDatabase.connect() for reactive, non-blocking access with R2DBC.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:
<tabs group="connectivity"> <tab id="jdbc-connect" title="JDBC" group-key="jdbc"> <code-block lang="kotlin" src="exposed-databases-jdbc/src/main/kotlin/org/example/Databases.kt" include-symbol="h2db" /> </tab> <tab id="r2dbc-connect" title="R2DBC" group-key="r2dbc"> <code-block lang="kotlin" src="exposed-databases-r2dbc/src/main/kotlin/org/example/R2DBCDatabases.kt" include-symbol="h2db" /> </tab> </tabs><note>Executing this code more than once per database will create leaks in your application, hence it is recommended to store it for later use: <code-block lang="kotlin" src="exposed-databases-jdbc/src/main/kotlin/org/example/Databases.kt" include-symbol="DbSettings"/> </note>
<note> By default, Exposed uses a <code>ServiceLoader</code> to get an implementation of the <a href="https://jetbrains.github.io/Exposed/api/exposed-core/org.jetbrains.exposed.v1.core/-database-connection-auto-registration/index.html"> <code>DatabaseConnectionAutoRegistration</code> </a> interface that represents a connection accessed by the <code>Database</code> instance. This can be modified when calling the <code>Database.connect()</code> method by providing an argument to <code>connectionAutoRegistration</code> in the parameter list. </note>In order to use H2, you need to add the H2 driver dependency:
<tabs group="connectivity"> <tab id="jdbc-h2-db" title="JDBC" group-key="jdbc"> <code-block lang="kotlin"> implementation("com.h2database:h2:%h2_db_version%") </code-block> </tab> <tab id="r2dbc-h2-db" title="R2DBC" group-key="r2dbc"> <code-block lang="kotlin"> implementation("io.r2dbc:r2dbc-h2:%h2_r2dbc_version%") </code-block> </tab> </tabs>Then connect to a database:
<tabs group="connectivity"> <tab id="jdbc-h2-driver-connect" title="JDBC" group-key="jdbc"> <code-block lang="kotlin" src="exposed-databases-jdbc/src/main/kotlin/org/example/Databases.kt" include-symbol="h2dbFromFile" /> </tab> <tab id="r2dbc-h2-driver-connect" title="R2DBC" group-key="r2dbc"> <code-block lang="kotlin" src="exposed-databases-r2dbc/src/main/kotlin/org/example/R2DBCDatabases.kt" include-symbol="h2dbFromFile" /> </tab> </tabs>Or in-memory database:
<tabs group="connectivity"> <tab id="jdbc-h2-db-in-memory" title="JDBC" group-key="jdbc"> <code-block lang="kotlin" src="exposed-databases-jdbc/src/main/kotlin/org/example/Databases.kt" include-symbol="h2db" /> </tab> <tab id="r2dbc-h2-db-in-memory" title="R2DBC" group-key="r2dbc"> <code-block lang="kotlin" src="exposed-databases-r2dbc/src/main/kotlin/org/example/R2DBCDatabases.kt" include-symbol="h2db" /> </tab> </tabs>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")
Add the required dependency:
<tabs group="connectivity"> <tab id="jdbc-maria-db" title="JDBC" group-key="jdbc"> <code-block lang="kotlin"> implementation("org.mariadb.jdbc:mariadb-java-client:%mariadb%") </code-block> </tab> <tab id="r2dbc-maria-db" title="R2DBC" group-key="r2dbc"> <code-block lang="kotlin"> implementation("org.mariadb:r2dbc-mariadb:%mariadb_r2dbc%") </code-block> </tab> </tabs>Connect to a database:
<tabs group="connectivity"> <tab id="jdbc-maria-db-connect" title="JDBC" group-key="jdbc"> <code-block lang="kotlin" src="exposed-databases-jdbc/src/main/kotlin/org/example/Databases.kt" include-symbol="mariadb" /> </tab> <tab id="r2dbc-maria-db-connect" title="R2DBC" group-key="r2dbc"> <code-block lang="kotlin" src="exposed-databases-r2dbc/src/main/kotlin/org/example/R2DBCDatabases.kt" include-symbol="mariadb" /> </tab> </tabs>Add the required dependency:
<tabs group="connectivity"> <tab id="jdbc-mysql" title="JDBC" group-key="jdbc"> <code-block lang="kotlin"> implementation("mysql:mysql-connector-java:%mysql%") </code-block> </tab> <tab id="r2dbc-mysql" title="R2DBC" group-key="r2dbc"> <code-block lang="kotlin"> implementation("io.asyncer:r2dbc-mysql:%mysql_r2dbc%") </code-block> </tab> </tabs>Connect to a database:
<tabs group="connectivity"> <tab id="jdbc-mysql-connect" title="JDBC" group-key="jdbc"> <code-block lang="kotlin" src="exposed-databases-jdbc/src/main/kotlin/org/example/Databases.kt" include-symbol="mysqldb" /> </tab> <tab id="r2dbc-mysql-connect" title="R2DBC" group-key="r2dbc"> <code-block lang="kotlin" src="exposed-databases-r2dbc/src/main/kotlin/org/example/R2DBCDatabases.kt" include-symbol="mysqldb" /> </tab> </tabs>Add the required dependency:
<tabs group="connectivity"> <tab id="jdbc-oracle" title="JDBC" group-key="jdbc"> <code-block lang="kotlin"> implementation("com.oracle.database.jdbc:ojdbc8:%oracle%") </code-block> </tab> <tab id="r2dbc-oracle" title="R2DBC" group-key="r2dbc"> <code-block lang="kotlin"> implementation("com.oracle.database.r2dbc:oracle-r2dbc:%oracle_r2dbc%") </code-block> </tab> </tabs>Connect to a database:
<tabs group="connectivity"> <tab id="jdbc-oracle-connect" title="JDBC" group-key="jdbc"> <code-block lang="kotlin" src="exposed-databases-jdbc/src/main/kotlin/org/example/Databases.kt" include-symbol="oracledb" /> </tab> <tab id="r2dbc-oracle-connect" title="R2DBC" group-key="r2dbc"> <code-block lang="kotlin" src="exposed-databases-r2dbc/src/main/kotlin/org/example/R2DBCDatabases.kt" include-symbol="oracledb" /> </tab> </tabs>Add the required dependency:
<tabs group="connectivity"> <tab id="jdbc-postgresql" title="JDBC" group-key="jdbc"> <code-block lang="kotlin"> implementation("org.postgresql:postgresql:%postgresql%") </code-block> </tab> <tab id="r2dbc-postgresql" title="R2DBC" group-key="r2dbc"> <code-block lang="kotlin"> implementation("org.postgresql:r2dbc-postgresql:%postgresql_r2dbc%") </code-block> </tab> </tabs>Connect to a database:
<tabs group="connectivity"> <tab id="jdbc-postgresql-connect" title="JDBC" group-key="jdbc"> <code-block lang="kotlin" src="exposed-databases-jdbc/src/main/kotlin/org/example/Databases.kt" include-symbol="postgresqldb" /> </tab> <tab id="r2dbc-postgresql-connect" title="R2DBC" group-key="r2dbc"> <code-block lang="kotlin" src="exposed-databases-r2dbc/src/main/kotlin/org/example/R2DBCDatabases.kt" include-symbol="postgresqldb" /> </tab> </tabs>Add the required dependency:
implementation("com.impossibl.pgjdbc-ng:pgjdbc-ng:%postgreNG%")
Connect to a database:
{src="exposed-databases-jdbc/src/main/kotlin/org/example/Databases.kt" include-symbol="postgresqldbNG"}
Add the required dependency:
<tabs group="connectivity"> <tab id="jdbc-sqlserver" title="JDBC" group-key="jdbc"> <code-block lang="kotlin"> implementation("com.microsoft.sqlserver:mssql-jdbc:%sqlserver%") </code-block> </tab> <tab id="r2dbc-sqlserver" title="R2DBC" group-key="r2dbc"> <code-block lang="kotlin"> implementation("io.r2dbc:r2dbc-mssql:%sqlserver_r2dbc%") </code-block> </tab> </tabs>Connect to a database:
<tabs group="connectivity"> <tab id="jdbc-sqlserver-connect" title="JDBC" group-key="jdbc"> <code-block lang="kotlin" src="exposed-databases-jdbc/src/main/kotlin/org/example/Databases.kt" include-symbol="sqlserverdb" /> </tab> <tab id="r2dbc-sqlserver-connect" title="R2DBC" group-key="r2dbc"> <code-block lang="kotlin" src="exposed-databases-r2dbc/src/main/kotlin/org/example/R2DBCDatabases.kt" include-symbol="sqlserverdb" /> </tab> </tabs>Add the required dependency:
implementation("org.xerial:sqlite-jdbc:%sqlite%")
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