This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Exposed is a lightweight ORM framework for Kotlin that provides two APIs:
- DSL API: Type-safe SQL-wrapping Domain Specific Language (in
exposed-core)- Works with both JDBC (
exposed-jdbc) and R2DBC (exposed-r2dbc)
- Works with both JDBC (
- DAO API: Lightweight Data Access Object API (in
exposed-dao)- Only works with JDBC - does not support R2DBC
- exposed-core: Foundation layer with DSL API, database abstractions, column types, and vendor dialects
- exposed-dao: DAO API with entity classes and relationships (JDBC only, does not work with R2DBC)
- exposed-jdbc: JDBC implementation with blocking transactions
- exposed-r2dbc: R2DBC implementation with suspending transactions
- exposed-java-time, exposed-jodatime, exposed-kotlin-datetime: Date/time support
- exposed-json: JSON/JSONB column types
- exposed-crypt: Encrypted column types
- exposed-money: JavaMoney MonetaryAmount support
- exposed-migration-core: Common migration functionality
- exposed-migration-jdbc: JDBC-based schema migrations
- exposed-migration-r2dbc: R2DBC-based schema migrations
- exposed-spring-boot-starter: Spring Boot integration
- spring-transaction: Spring Framework transaction manager
- exposed-tests: Main JDBC-based test suite
- exposed-r2dbc-tests: R2DBC-specific test suite
- exposed-jdbc-r2dbc-tests: Cross-compatibility tests
./gradlew compileKotlin # Compile the projects code
./gradlew detekt # Validate code style
./gradlew apiDump # Update dokka API docs after changing public APITests are organized by database and dialect. Each module has database-specific test tasks:
./gradlew test_h2_v2 # All modules with H2
./gradlew :exposed-tests:test_h2_v2 # JDBC Tests with H2
./gradlew :exposed-r2dbc-tests:test_h2_v2 # R2DBC Tests with H2./gradlew test_postgres # All modules with Postgres
./gradlew :exposed-tests:test_postgres # JDBC Tests with Postgres
./gradlew :exposed-r2dbc-tests:test_postgres # R2DBC Tests with Postgres# Start database containers first
./gradlew mariadbComposeUp # Start MariaDB
./gradlew postgresComposeUp # Start PostgreSQL
./gradlew mysql8ComposeUp # Start MySQL 8
./gradlew oracleComposeUp # Start Oracle
./gradlew sqlserverComposeUp # Start SQL Server
# Run tests
./gradlew :exposed-tests:test_postgres
./gradlew :exposed-tests:test_mysql_v8
./gradlew :exposed-tests:test_mariadb
# Stop containers
./gradlew postgresComposeDownForced./gradlew :exposed-tests:test_h2_v2 --tests "org.jetbrains.exposed.v1.tests.shared.dml.InsertTests"
./gradlew :exposed-tests:test_h2_v2 --tests "*.InsertTests.testBatchInsert"test_h2_v2,test_h2_v2_mysql,test_h2_v2_psql, etc. (H2 with different dialect emulations)test_sqlitetest_mysql_v5,test_mysql_v8test_mariadbtest_postgres,test_postgresngtest_oracletest_sqlserver
Tests inherit from different base classes depending on the driver:
JDBC Tests - inherit from DatabaseTestsBase (in exposed-tests/src/main/kotlin/org/jetbrains/exposed/v1/tests/):
- Tests are parameterized by database dialect using
@ParameterizedClassand@MethodSource("data") - Each test automatically runs against all enabled dialects
- Available dialects are determined by system properties set by Gradle test tasks
R2DBC Tests - inherit from R2dbcDatabaseTestsBase (in exposed-r2dbc-tests/src/main/kotlin/):
- Similar parameterized testing pattern as JDBC
- Uses suspending functions and coroutine context
- Test methods use
= runTest { }for coroutine support, or utils methods likewithDb,withTables,
There are separate TestDB enums for JDBC and R2DBC tests:
JDBC TestDB (exposed-tests/src/main/kotlin/org/jetbrains/exposed/v1/tests/TestDB.kt):
- Connection strings using JDBC URLs (e.g.,
jdbc:h2:mem:...,jdbc:postgresql://...) - JDBC driver class names
- Before/after connection hooks
- Database-specific configuration (e.g., H2 dialect emulation modes)
Available JDBC TestDB values:
H2_V2,H2_V2_MYSQL,H2_V2_PSQL,H2_V2_MARIADB,H2_V2_ORACLE,H2_V2_SQLSERVERSQLITE,MYSQL_V5,MYSQL_V8,MARIADB,POSTGRESQL,POSTGRESQLNG,ORACLE,SQLSERVER
R2DBC TestDB (exposed-r2dbc-tests/src/main/kotlin/org/jetbrains/exposed/v1/r2dbc/tests/TestDB.kt):
- Connection strings using R2DBC URLs (e.g.,
r2dbc:h2:mem:...,r2dbc:postgresql://...) - R2DBC isolation levels
- Suspend-aware before/after connection hooks
Available R2DBC TestDB values:
H2_V2,H2_V2_MYSQL,H2_V2_PSQL,H2_V2_MARIADB,H2_V2_ORACLE,H2_V2_SQLSERVERMYSQL_V5,MYSQL_V8,MARIADB,POSTGRESQL,ORACLE,SQLSERVER- Note: R2DBC does not support
SQLITEorPOSTGRESQLNG
Tests extend DatabaseTestsBase and use these helper functions:
class MyTests : DatabaseTestsBase() {
@Test
fun testSomething() {
withDb { testDb -> // Runs against current dialect
// Create tables
SchemaUtils.create(MyTable)
// Insert/query data
MyTable.insert { it[name] = "test" }
// Clean up
SchemaUtils.drop(MyTable)
}
}
}@Test
fun testWithTables() {
withTables(MyTable, AnotherTable) {
// Tables are created before block and dropped after
MyTable.insert { it[name] = "test" }
}
}@Test
fun testPostgresOnly() {
withDb(TestDB.POSTGRESQL) { // Only runs for PostgreSQL
// Postgres-specific test
}
}@Test
fun testJsonSupport() {
withTables(JsonTable, excludeSettings = listOf(TestDB.SQLITE, TestDB.MYSQL_V5)) {
// Test JSON columns
}
}- JDBC:
transaction { }- blocking transaction execution - JDBC:
suspendTransaction { }- suspending, with actually blocking database connections - R2DBC:
suspendTransaction { }- suspending, uses coroutine context - Never mix JDBC and R2DBC transaction functions
Database-specific behavior is in exposed-core/src/main/kotlin/org/jetbrains/exposed/v1/core/vendors/:
H2.kt,MysqlDialect.kt,PostgreSQL.kt,OracleDialect.kt,SQLServerDialect.kt,SQLiteDialect.kt,MariaDBDialect.kt- Extend
DatabaseDialectand implementVendorDialect - Override
DataTypeProviderandFunctionProviderfor dialect-specific SQL
- Create column type class in
exposed-core(extendsColumnType) - Add factory method to
Tableclass - Add dialect-specific SQL type mapping in
DataTypeProviderimplementations - Add tests in
exposed-testscovering multiple databases - Add tests in
exposed-r2dbc-testscovering multiple databases
- Migration modules use serialization to track schema state
- JDBC migrations:
exposed-migration-jdbcwithMigrationUtils - R2DBC migrations:
exposed-migration-r2dbcwith suspend support - Both share common code from
exposed-migration-core
- Always test features against multiple databases, especially H2, PostgreSQL, and MySQL
- Use dialect checks when implementing database-specific features:
if (currentDialectTest is PostgreSQLDialect) { // PostgreSQL-specific code }
- H2 dialect emulation modes (
H2_V2_MYSQL,H2_V2_PSQL, etc.) help catch compatibility issues early
- Extend
DatabaseTestsBaseorR2dbcDatabaseTestsBasefor parameterized multi-database testing - Use
Assumptions.assumeTrue()orexcludeSettingsargument inwithTablesto skip tests for unsupported databases - Prefer
withTablesover manualSchemaUtils.create/dropfor cleaner tests - Test both JDBC and R2DBC implementations when adding core features
- Use
currentDialectTestto access current dialect in assertions
- Run
./gradlew apiCheckbefore committing public API changes - Binary compatibility is critical - breaking changes require major version bump
- Use
@InternalApiannotation for internal implementation details - Document breaking changes in BREAKING_CHANGES.md under "Breaking changes" section
-
EditorConfig:
.editorconfigdefines code formatting rules- Indent: 4 spaces
- Max line length: 166 characters
- Charset: UTF-8
- End of line: LF
- Kotlin code style: KOTLIN_OFFICIAL
-
Detekt: Static analysis with
detekt/detekt-config.yml- Max issues: 0 (all issues must be fixed)
- Wildcard imports are allowed
- Magic numbers allowed in named arguments and ranges
- Run with:
./gradlew detekt
- Package structure uses
org.jetbrains.exposed.v1.*namespace - Table objects: PascalCase (e.g.,
Users,Cities) - Column names: camelCase in code, snake_case in SQL
- Test classes: Suffix with
TestsorTest - Test methods: Descriptive names starting with
test
Located in exposed-tests/src/main/kotlin/org/jetbrains/exposed/v1/tests/:
TestUtils.kt:currentDialectTest,currentDialectMetadataTest, helper functionsDatabaseTestsBase.kt: Base class for all JDBC testsR2DBCDatabaseTestsBase.kt: Base class for all R2DBC testsTestDB.kt: Database connection configurationsshared/Assert.kt: Custom assertion functionsshared/MiscTable.kt,shared/ForeignKeyTables.kt: Reusable test tables
The samples/ directory contains reference implementations:
- exposed-ktor: Ktor application with JDBC
- exposed-ktor-r2dbc: Ktor application with R2DBC
- exposed-migration: Migration examples
- exposed-spring: Spring Boot integration examples
These demonstrate best practices for using Exposed in real applications.
buildSrc/: Custom Gradle plugins and build configurationbuild.gradle.kts: Root build configuration with testDb DSL usagesettings.gradle.kts: Module definitionsbuildScripts/docker/: Database container configurationsgradle.properties: Version and build settings.editorconfig: Code formatting rulesdetekt/detekt-config.yml: Static analysis configuration