{"id":47239761,"url":"https://github.com/octavius-framework/octavius-database","last_synced_at":"2026-05-30T19:00:51.386Z","repository":{"id":333010852,"uuid":"1135940774","full_name":"Octavius-Framework/octavius-database","owner":"Octavius-Framework","description":"SQL-first data access layer for Kotlin \u0026 PostgreSQL. An Anti-ORM with fluent query builders, automatic type mapping (ENUM, COMPOSITE, ARRAY), transaction plans with step dependencies, and polymorphic  storage","archived":false,"fork":false,"pushed_at":"2026-05-30T17:43:49.000Z","size":1230,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-30T18:20:08.659Z","etag":null,"topics":["data-mapping","jdbc","kotlin","postgresql","sql"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Octavius-Framework.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-01-16T20:12:11.000Z","updated_at":"2026-05-30T17:43:23.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/Octavius-Framework/octavius-database","commit_stats":null,"previous_names":["polskianonim/octaviusdatabase","octavius-framework/octavius-database"],"tags_count":88,"template":false,"template_full_name":null,"purl":"pkg:github/Octavius-Framework/octavius-database","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Octavius-Framework%2Foctavius-database","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Octavius-Framework%2Foctavius-database/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Octavius-Framework%2Foctavius-database/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Octavius-Framework%2Foctavius-database/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Octavius-Framework","download_url":"https://codeload.github.com/Octavius-Framework/octavius-database/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Octavius-Framework%2Foctavius-database/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33705207,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-30T02:00:06.278Z","response_time":92,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["data-mapping","jdbc","kotlin","postgresql","sql"],"created_at":"2026-03-14T01:23:00.043Z","updated_at":"2026-05-30T19:00:51.380Z","avatar_url":"https://github.com/Octavius-Framework.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Octavius Database\n\n\u003cdiv align=\"center\"\u003e\n\n**An explicit, SQL-first data access layer for Kotlin \u0026 PostgreSQL**\n\n[![KDoc API](https://img.shields.io/badge/KDoc-api-7F52FF?logo=kotlin\u0026logoColor=white)](https://octavius-framework.github.io/octavius-database/api)\n[![KDoc Core](https://img.shields.io/badge/KDoc-core-7F52FF?logo=kotlin\u0026logoColor=white)](https://octavius-framework.github.io/octavius-database/core)\n[![Maven Central](https://img.shields.io/maven-central/v/io.github.octavius-framework/database-core.svg?label=Maven%20Central)](https://central.sonatype.com/search?namespace=io.github.octavius-framework)\n\n*It's not an ORM. It's a **ROME** (Relational-Object Mapping Engine). Because all queries lead to ROME.*\n\n\u003c/div\u003e\n\n---\n\n## Philosophy (The Pax Romana of Data)\n\n*Just as Augustus brought order to a republic torn apart by the chaos of unchecked power, Octavius brings order to the chaotic republic of database interactions. The Senate of abstraction is dissolved. SQL rules supreme.*\n\nOctavius was built to bring order to the chaotic republic of database interactions. It rejects the unpredictable \"magic\" of traditional ORMs and returns the power to the rightful ruler: **SQL**.\n\n| Principle                   | Description                                                       |\n|-----------------------------|-------------------------------------------------------------------|\n| **Query is Imperator**      | Your SQL query dictates the shape of data — not the framework.    |\n| **Object is a Vessel**      | A `data class` is simply a type-safe container for query results. |\n| **Explicitness over Magic** | No lazy-loading, no session management, no dirty checking.        |\n\n## Features\n\n- **Fluent Query Builders** — SELECT, INSERT, UPDATE, DELETE with named parameters, subqueries, and CTE support\n- **Automatic Type Mapping** — PostgreSQL `COMPOSITE`, `ENUM`, `ARRAY` and **Custom Type Handlers** (Global \u0026 Per-Query) ↔ Kotlin types\n- **Dynamic Type System** — Polymorphic storage \u0026 ad-hoc object mapping with `dynamic_dto` and `dynamic_map`\n- **Transaction Plans** — Multi-step atomic operations with step dependencies\n- **Dynamic Filters** — Safe, composable `WHERE` clauses with `QueryFragment`\n- **Stored Procedures** — CALL with automatic IN/OUT/INOUT handling, composite \u0026 array expansion\n- **LISTEN / NOTIFY** — Flow-based async notifications on a dedicated connection outside the pool\n\nEvery design choice in Octavius is intentional. The reasoning behind them is laid out in the [Design Philosophy](DESIGN_PHILOSOPHY.md).\n\n## Quick Start\n\n```kotlin\n// Define your data class — it maps directly to query results\ndata class Legionnaire(val id: Int, val name: String, val rank: String)\n\n// Query with named parameters\nval legionnaires = dataAccess.select(\"id\", \"name\", \"rank\")\n    .from(\"legions\")\n    .where(\"enlisted_year \u003e @year\")\n    .orderBy(\"name\")\n    .toListOf\u003cLegionnaire\u003e(\"year\" to 24)\n```\n\n## Query Builders\n\n```kotlin\n// SELECT with pagination\nval senators = dataAccess.select(\"id\", \"name\", \"province\")\n    .from(\"senate\")\n    .where(\"active = true\")\n    .orderBy(\"appointed_at DESC\")\n    .limit(10)\n    .offset(20)\n    .toListOf\u003cSenator\u003e()\n\n// INSERT with RETURNING\nval newId = dataAccess.insertInto(\"citizens\")\n    .value(\"name\")\n    .value(\"tribe\")\n    .returning(\"id\")\n    .toField\u003cInt\u003e(mapOf(\"name\" to \"Marcus Aurelius\", \"tribe\" to \"Cornelia\"))\n\n// UPDATE with expressions\ndataAccess.update(\"legion_supplies\")\n    .setExpression(\"quantity\", \"quantity - 1\")\n    .where(\"id = @id\")\n    .execute(\"id\" to supplyId)\n\n// DELETE\ndataAccess.deleteFrom(\"expired_mandates\")\n    .where(\"expires_at \u003c NOW()\")\n    .execute()\n```\n\n\n## Type Mapping\n\nAutomatic conversion between PostgreSQL and Kotlin types.\n\n### Standard Types\n\n| PostgreSQL                | Kotlin          | Notes                                            |\n|---------------------------|-----------------|--------------------------------------------------|\n| `int2`, `smallserial`     | `Short`         |                                                  |\n| `int4`, `serial`          | `Int`           |                                                  |\n| `int8`, `bigserial`       | `Long`          |                                                  |\n| `float4`                  | `Float`         |                                                  |\n| `float8`                  | `Double`        |                                                  |\n| `numeric`                 | `BigDecimal`    |                                                  |\n| `text`, `varchar`, `char` | `String`        |                                                  |\n| `bool`                    | `Boolean`       |                                                  |\n| `uuid`                    | `UUID`          | `java.util.UUID`                                 |\n| `bytea`                   | `ByteArray`     |                                                  |\n| `json`, `jsonb`           | `JsonElement`   | `kotlinx.serialization.json`                     |\n| `void`                    | `Unit`          | Return type of void functions (e.g. `pg_notify`) |\n| `date`                    | `LocalDate`     | `kotlinx.datetime` \u003csup\u003e*\u003c/sup\u003e                  |\n| `time`                    | `LocalTime`     | `kotlinx.datetime`                               |\n| `timestamp`               | `LocalDateTime` | `kotlinx.datetime` \u003csup\u003e*\u003c/sup\u003e                  |\n| `timestamptz`             | `Instant`       | `kotlin.time` \u003csup\u003e*\u003c/sup\u003e                       |\n| `interval`                | `Duration`      | `kotlin.time` \u003csup\u003e*\u003c/sup\u003e                       |\n\n\u003csup\u003e*\u003c/sup\u003e Supports PostgreSQL infinity values (`infinity`, `-infinity`). See [Type System](docs/type-system.md#infinity-values-for-datetime) for details.\n\nArrays of all standard types are supported and map to `List\u003cT\u003e`.\n\n### Custom Types\n\n```kotlin\n// PostgreSQL COMPOSITE TYPE → Kotlin data class\n@PgComposite\ndata class Province(val name: String, val capital: String, val governor: String)\n\n// PostgreSQL ENUM → Kotlin enum\n@PgEnum(schema = \"cursus_honorum\")\nenum class Magistrature { Quaestor, Aedile, Praetor, Consul, Censor }\n\n// Works seamlessly in queries\ndata class Senator(val id: Int, val rank: Magistrature, val homeProvince: Province)\n\nval senators = dataAccess.select(\"id\", \"rank\", \"home_province\")\n    .from(\"senate\")\n    .toListOf\u003cSenator\u003e()  // Types converted automatically\n```\n\n### Custom Type Handlers\n\nExtend the type system for any PostgreSQL type (e.g., `circle`, `ltree`) by implementing `GlobalTypeHandler\u003cT\u003e`. Handlers are automatically discovered via classpath scanning.\n\n```kotlin\nobject PgCircleHandler : GlobalTypeHandler\u003cPgCircle\u003e {\n    override val pgTypeName = \"circle\"\n    override val kotlinClass = PgCircle::class\n    override val fromPgString = { s: String -\u003e /* parse \u003c(x,y),r\u003e */ }\n    override val toPgString = { c: PgCircle -\u003e \"\u003c(${c.x},${c.y}),${c.radius}\u003e\" }\n}\n```\n\n### Per-Query Overrides\nNeed to change a mapping, bypass reflection, or return a composite as a `Map` for just one specific query? Use the `.options()` block without affecting global state:\n\n```kotlin\nval results = dataAccess.select(\"*\").from(\"classified_reports\")\n    .options { \n        registerTypeHandler(LegacyDateHandler) \n        returnCompositeAsMap(\"metadata\")\n    }\n    .toListOf\u003cReport\u003e()\n```\n*See [Type System: Per-Query Configuration](docs/type-system.md#per-query-configuration-via-options-) for full details.*\n\n## Dynamic Type System (`dynamic_dto` \u0026 `dynamic_map`)\n\nOctavius provides a powerful bridge between PostgreSQL and Kotlin's type system using the `dynamic_dto` (JSONB-based storage) and `dynamic_map` (ad-hoc projections) composite types.\nThey allow you to map complex, nested, or polymorphic data on the fly without creating strict database schema types for every nested object.\nThese types are automatically initialized in the **`public`** schema on startup.\n\n\n### 1. Ad-hoc Object Mapping\n\nConstruct Kotlin objects directly in SQL using `jsonb_build_object` — no need to define PostgreSQL COMPOSITE types. Perfect for JOINs and projections where you want nested results without schema changes.\n\n```kotlin\n@DynamicallyMappable(typeName = \"citizen_profile\")\n@Serializable\ndata class CitizenProfile(val tribe: String, val rights: List\u003cString\u003e)\n\ndata class CitizenWithProfile(val id: Int, val name: String, val profile: CitizenProfile)\n\n// The database packages the nested object, Octavius unpacks it. Zero boilerplate.\nval citizens = dataAccess.rawQuery(\"\"\"\n    SELECT\n        c.id,\n        c.name,\n        dynamic_dto(\n            'citizen_profile',\n            jsonb_build_object('tribe', p.tribe, 'rights', p.rights)\n        ) AS profile\n    FROM citizens c\n    JOIN citizen_profiles p ON p.citizen_id = c.id\n\"\"\").toListOf\u003cCitizenWithProfile\u003e()\n```\n\n\u003e **Why use this?** Usually, to get a citizen with their profile in one query, you'd fetch flat columns (`citizen_id`, `citizen_name`, `profile_tribe`...) \n\u003e and manually map them, create a database VIEW or COMPOSITE. With ad-hoc mapping, you construct the nested structure directly in SQL. \n\u003e The database does the packaging, Octavius does the unpacking — zero boilerplate.\n\n### 2. Polymorphic Queries (Row-level)\nStore different entity types in a single table and query them safely as a list of Kotlin interfaces.\n\n```kotlin\n// 1. Define a sealed interface\nsealed interface MonumentRecord\n\n@DynamicallyMappable(typeName = \"inscription\")\n@Serializable\ndata class Inscription(val text: String, val lang: String) : MonumentRecord\n\n@DynamicallyMappable(typeName = \"relief\")\n@Serializable\ndata class Relief(val subject: String) : MonumentRecord\n\n// Database: CREATE TABLE monument_records (id INT, record dynamic_dto);\n\n// 2. Fetch directly to a list of your interface\nval records = dataAccess.select(\"record\")\n    .from(\"monument_records\")\n    .toColumn\u003cMonumentRecord\u003e()\n\n// Returns: [Inscription(...), Relief(...), Inscription(...)]\n```\n\n## Functions and Procedures\n\nOctavius stays true to its SQL-first philosophy. Invoke functions and procedures directly using native PostgreSQL syntax:\n\n```kotlin\n// Functions (SELECT * FROM func)\nval result = dataAccess.select(\"*\").from(\"calculate_tribute(@province, @year)\")\n    .toField\u003cInt\u003e(\"province\" to \"Britannia\", \"year\" to 43)\n\n// Procedures (CALL proc)\nval result = dataAccess.rawQuery(\"CALL register_conscript(@legion_id, @new_rank)\")\n    .toSingleStrict(\n        \"legion_id\" to 7,\n        \"new_rank\" to null.withPgType(\"text\")\n    )\n```\n\n## Safe Dynamic Filters\n\nBuild complex `WHERE` clauses without SQL injection risks:\n\n```kotlin\nfun buildFilters(name: String?, minRank: Int?, province: Province?) = listOfNotNull(\n    name?.let { \"name ILIKE @name\" withParam (\"name\" to \"%$it%\") },\n    minRank?.let { \"rank_order \u003e= @minRank\" withParam (\"minRank\" to it) },\n    province?.let { \"home_province = @province\" withParam (\"province\" to it) }\n).join(\" AND \")\n\nval filter = buildFilters(name = \"Julius\", minRank = 3, province = null)\nval senators = dataAccess.select(\"*\")\n    .from(\"senate\")\n    .where(filter.sql)\n    .toListOf\u003cSenator\u003e(filter.params)\n```\n\n## Transactions\n\nOctavius supports two powerful interaction patterns for atomic operations.\n\n### 1. Transaction Blocks (Imperative)\nThe simplest way to execute multiple operations. Transactions follow a fail-fast policy: they are automatically rolled back if the block returns `DataResult.Failure` or throws an exception.\n\n```kotlin\nval result = dataAccess.transaction {\n    val citizenId = insertInto(\"citizens\")\n        .value(\"name\")\n        .returning(\"id\")\n        .toField\u003cInt\u003e(\"name\" to \"Marcus Aurelius\")\n        .getOrElse { return@transaction it }\n\n    insertInto(\"citizen_profiles\")\n        .values(listOf(\"citizen_id\", \"bio\"))\n        .execute(\"citizen_id\" to citizenId, \"bio\" to \"Stoic philosopher\")\n        .getOrElse { return@transaction it }\n\n    DataResult.Success(citizenId)\n}\n```\n\n### 2. Transaction Plans (Declarative)\nExecute multi-step operations with complex dependencies between steps. Results from previous steps can be referenced in subsequent steps without nested callbacks or manual state management.\n\n```kotlin\nval plan = TransactionPlan()\n\n// Step 1: Record the edict, get handle to future ID\nval edictIdHandle = plan.add(\n    dataAccess.insertInto(\"edicts\")\n        .values(listOf(\"issuer_id\", \"total_tribute\"))\n        .returning(\"id\")\n        .asStep()\n        .toField\u003cInt\u003e(mapOf(\"issuer_id\" to consulId, \"total_tribute\" to tribute))\n)\n\n// Step 2: Assign levy items using the handle\nfor (item in levyItems) {\n    val levyItem: Map\u003cString, Any?\u003e = mapOf(\n        \"edict_id\" to edictIdHandle.field(),  // Reference future value\n        \"province_id\" to item.provinceId,\n        \"amount\" to item.amount\n    )\n\n    plan.add(\n        dataAccess.insertInto(\"edict_items\")\n            .values(levyItem)\n            .asStep()\n            .execute(levyItem)\n    )\n}\n\n// Execute all steps in single transaction\ndataAccess.executeTransactionPlan(plan)\n```\n\n## LISTEN / NOTIFY\n\nSubscribe to PostgreSQL channels and receive real-time notifications as a Kotlin `Flow`:\n\n```kotlin\n// Send a notification\ndataAccess.notify(\"legion_dispatch\", \"legion_id:VII\")\n\n// Listen on a dedicated connection (outside the HikariCP pool)\ndataAccess.createChannelListener().use { listener -\u003e\n    listener.listen(\"legion_dispatch\", \"senate_decrees\")\n\n    listener.notifications()\n        .collect { notification -\u003e\n            when (notification.channel) {\n                \"legion_dispatch\" -\u003e handleDispatch(notification.payload)\n                \"senate_decrees\"  -\u003e handleDecree(notification.payload)\n            }\n        }\n}\n```\n\nEach `PgChannelListener` holds its own dedicated JDBC connection, separate from the query pool. Notifications sent inside a transaction are only delivered after commit.\n\n## Error Handling\n\nOctavius distinguishes between **Database Execution Errors** (returned safely) and **Fatal Developer Errors** (thrown).\n\n- **Queries Never Throw (On Runtime Errors):** If a query is syntactically correct and reaches the database, it returns a `DataResult.Failure(error)` instead of throwing. This forces explicit handling of expected database errors like constraint violations, lock timeouts, or missing records.\n- **Fail-Fast for Bugs:** If an obvious developer mistake is made (e.g., invalid SQL syntax, a missing `WHERE` clause in a `DELETE`, or a Kotlin type mapping mismatch), Octavius **throws a standard exception** (`FatalDatabaseException`). It fails fast because these errors represent broken code that should be caught and fixed during development.\n- **Rich Context:** Every exception includes a `QueryContext` that provides a clean visualization of the high-level SQL, the low-level JDBC query, and the exact parameters involved (great for logging!).\n\n```kotlin\nval result = dataAccess.insertInto(\"citizens\")\n    .value(\"name\")\n    .returning(\"id\")\n    .toField\u003cInt\u003e(\"name\" to \"Marcus Aurelius\")\n\nresult\n    .onSuccess { id -\u003e println(\"New citizen ID: $id\") }\n    .onFailure { error -\u003e\n        when (error) {\n            is ConstraintViolationException -\u003e println(\"Conflict in: ${error.constraintName}\")\n            is DataOperationException -\u003e println(\"Operation failed: ${error.messageEnum}\")\n            is TransactionException -\u003e println(\"Transient error: ${error.errorType}\")\n            else -\u003e println(\"Database error: $error\")\n        }\n    }\n```\n\nSee [Error Handling](docs/error-handling.md) for the full exception hierarchy and debugging tips.\n\n## Configuration\n\n### Using Properties File\n\nCreate a `database.properties` file in `src/main/resources`:\n\n```properties\ndb.url=jdbc:postgresql://localhost:5432/roma\ndb.username=augustus\ndb.password=spqr\ndb.schemas=public,cursus_honorum\ndb.packagesToScan=com.roma.domain,com.roma.dto\n\n# Custom HikariCP settings\ndb.hikari.maximumPoolSize=20\ndb.hikari.minimumIdle=5\n\n# Optional settings\ndb.setSearchPath=true\ndb.dynamicDtoStrategy=AUTOMATIC_WHEN_UNAMBIGUOUS\ndb.disableCoreTypeInitialization=false\n```\n\nLoad it in your application:\n\n```kotlin\n// From properties file\nval dataAccess = OctaviusDatabase.fromConfig(\n    DatabaseConfig.loadFromFile(\"database.properties\")\n)\n```\n\n### Direct Configuration\n\n```kotlin\nval dataAccess = OctaviusDatabase.fromConfig(\n    DatabaseConfig(\n        dbUrl = \"jdbc:postgresql://localhost:5432/roma\",\n        dbUsername = \"augustus\",\n        dbPassword = \"spqr\",\n        dbSchemas = listOf(\"public\"),\n        packagesToScan = listOf(\"com.roma.domain\"),\n        hikariProperties = mapOf(\"maximumPoolSize\" to \"20\")\n    )\n)\n\n// From existing DataSource\nval dataAccess = OctaviusDatabase.fromDataSource(existingDataSource, ...)\n```\n\n## Database Migrations\n\nOctavius provides an optional integration with [Flyway](https://flywaydb.org/) for schema migrations via the `:flyway-integration` module.\n\n```kotlin\nval dataAccess = OctaviusDatabase.fromConfig(\n    config = config,\n    migrationRunner = FlywayMigrationRunner.create(\n        schemas = config.dbSchemas,\n        baselineVersion = \"1\"\n    )\n)\n```\n\nSee [Flyway Migrations](docs/configuration.md#flyway-migrations) in the configuration guide for details.\n\n## Documentation\n\nFor detailed guides and examples, see the [full documentation](docs/README.md):\n\n- [Configuration](docs/configuration.md) - Initialization, DatabaseConfig, Flyway (optional), core types, Type Registry scanning, JSON Configuration, DynamicDto strategy\n- [Multiplatform Support](docs/multiplatform.md) - Shared DTOs, Multiplatform BigDecimal, and JS serializers\n- [Lifecycle \u0026 Shutdown](docs/lifecycle-and-shutdown.md) - Proper cleanup, .use {} block, common integration patterns\n- [Query Builders](docs/query-builders.md) - SELECT (FOR UPDATE), INSERT (ON CONFLICT), UPDATE, DELETE, fragments, `.options()` and builder modes\n- [Functions \u0026 Procedures](docs/functions-and-procedures.md) - CALL, SELECT, IN/OUT, PgTyped resolution\n- [Executing Queries](docs/executing-queries.md) - Terminal methods, DataResult matrix, iterative execution\n- [Parameter Handling](docs/parameter-handling.md) - Named parameters (@), JSONB operator escaping (?), collections \u0026 flattening, unnest and bulk operations, identifiers escaping\n- [Data Mapping](docs/data-mapping.md) - toDataMap(), toDataObject(), @MapKey, nested structures\n- [ORM-Like Patterns](docs/orm-patterns.md) - CRUD patterns, real-world examples\n- [Transactions](docs/transactions.md) - Transaction blocks, TransactionPlan, StepHandle, passing data between steps , propagation, isolation, read-only, timeouts, errors and Concurrency \u0026 Thread Safety\n- [Notifications](docs/notifications.md) - LISTEN/NOTIFY, PgChannelListener, Flow-based receiving\n- [Error Handling](docs/error-handling.md) - Exception hierarchy, debugging\n- [Type System](docs/type-system.md) - @PgEnum, @PgComposite, @DynamicallyMappable, dynamic_map projections, dynamic data insertion, Custom Type Handlers, GlobalTypeHandler, .options() per-query configs, standard type mappings\n\n## Architecture\n\n| Module               | Platform      | Description                                                                            |\n|----------------------|---------------|----------------------------------------------------------------------------------------|\n| `api`                | Multiplatform | **Common:** Annotations \u0026 DTOs (JVM/JS). **JVM-only:** Query \u0026 Transaction interfaces. |\n| `core`               | JVM           | **Zero-dependency** core engine. Pure JDBC \u0026 HikariCP.                                 |\n| `spring-integration` | JVM           | Optional integration for Spring Boot (`@Transactional` support).                       |\n| `flyway-integration` | JVM           | Optional migration runner integration.                                                 |\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foctavius-framework%2Foctavius-database","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Foctavius-framework%2Foctavius-database","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foctavius-framework%2Foctavius-database/lists"}