{"id":39906941,"url":"https://github.com/polskianonim/octaviusdatabase","last_synced_at":"2026-03-05T23:18:28.405Z","repository":{"id":333010852,"uuid":"1135940774","full_name":"PolskiAnonim/OctaviusDatabase","owner":"PolskiAnonim","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-01-18T17:48:25.000Z","size":200,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-01-18T21:50:33.764Z","etag":null,"topics":["data-mapping","kotlin","postgresql"],"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/PolskiAnonim.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-01-18T17:48:16.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/PolskiAnonim/OctaviusDatabase","commit_stats":null,"previous_names":["polskianonim/octaviusdatabase"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/PolskiAnonim/OctaviusDatabase","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PolskiAnonim%2FOctaviusDatabase","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PolskiAnonim%2FOctaviusDatabase/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PolskiAnonim%2FOctaviusDatabase/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PolskiAnonim%2FOctaviusDatabase/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/PolskiAnonim","download_url":"https://codeload.github.com/PolskiAnonim/OctaviusDatabase/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PolskiAnonim%2FOctaviusDatabase/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28671195,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-22T20:48:19.482Z","status":"ssl_error","status_checked_at":"2026-01-22T20:48:14.968Z","response_time":144,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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","kotlin","postgresql"],"created_at":"2026-01-18T16:00:24.733Z","updated_at":"2026-02-22T01:40:24.281Z","avatar_url":"https://github.com/PolskiAnonim.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://polskianonim.github.io/OctaviusDatabase/api)\n[![KDoc Core](https://img.shields.io/badge/KDoc-core-7F52FF?logo=kotlin\u0026logoColor=white)](https://polskianonim.github.io/OctaviusDatabase/core)\n\n*It's not an ORM. It's a ROM (Relational-Object Mapper) — an Anti-ORM.*\n\n\u003c/div\u003e\n\n---\n\n## Philosophy\n\n| Principle                   | Description                                                       |\n|-----------------------------|-------------------------------------------------------------------|\n| **Query is King**           | 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 a clean API\n- **Automatic Type Mapping** — PostgreSQL `COMPOSITE`, `ENUM`, `ARRAY` ↔ Kotlin types\n- **Transaction Plans** — Multi-step atomic operations with step dependencies\n- **Dynamic Filters** — Safe, composable `WHERE` clauses with `QueryFragment`\n- **Dynamic Type System** — Polymorphic storage \u0026 ad-hoc object mapping with `dynamic_dto`\n\n## Quick Start\n\n```kotlin\n// Define your data class — it maps directly to query results\ndata class Book(val id: Int, val title: String, val author: String)\n\n// Query with named parameters\nval books = dataAccess.select(\"id\", \"title\", \"author\")\n    .from(\"books\")\n    .where(\"published_year \u003e :year\")\n    .orderBy(\"title\")\n    .toListOf\u003cBook\u003e(\"year\" to 2020)\n```\n\n## Query Builders\n\n```kotlin\n// SELECT with pagination\nval users = dataAccess.select(\"id\", \"name\", \"email\")\n    .from(\"users\")\n    .where(\"active = true\")\n    .orderBy(\"created_at DESC\")\n    .limit(10)\n    .offset(20)\n    .toListOf\u003cUser\u003e()\n\n// INSERT with RETURNING\nval newId = dataAccess.insertInto(\"users\")\n    .value(\"name\")\n    .value(\"email\")\n    .returning(\"id\")\n    .toField\u003cInt\u003e(mapOf(\"name\" to \"John\", \"email\" to \"john@example.com\"))\n\n// UPDATE with expressions\ndataAccess.update(\"products\")\n    .setExpression(\"stock\", \"stock - 1\")\n    .where(\"id = :id\")\n    .execute(\"id\" to productId)\n\n// DELETE\ndataAccess.deleteFrom(\"sessions\")\n    .where(\"expires_at \u003c NOW()\")\n    .execute()\n```\n\n## Safe Dynamic Filters\n\nBuild complex `WHERE` clauses without SQL injection risks:\n\n```kotlin\nfun buildFilters(name: String?, minPrice: Int?, category: Category?): QueryFragment {\n    val fragments = mutableListOf\u003cQueryFragment\u003e()\n\n    name?.let { fragments += QueryFragment(\"name ILIKE :name\", mapOf(\"name\" to \"%$it%\")) }\n    minPrice?.let { fragments += QueryFragment(\"price \u003e= :minPrice\", mapOf(\"minPrice\" to it)) }\n    category?.let { fragments += QueryFragment(\"category = :cat\", mapOf(\"cat\" to it)) }\n\n    return fragments.join(\" AND \")\n}\n\nval filter = buildFilters(name = \"Pro\", minPrice = 100, category = null)\nval products = dataAccess.select(\"*\")\n    .from(\"products\")\n    .where(filter.sql)\n    .toListOf\u003cProduct\u003e(filter.params)\n```\n\n## Transaction Plans\n\nExecute multi-step operations atomically with dependencies between steps:\n\n```kotlin\nval plan = TransactionPlan()\n\n// Step 1: Create order, get handle to future ID\nval orderIdHandle = plan.add(\n    dataAccess.insertInto(\"orders\")\n        .values(listOf(\"user_id\", \"total\"))\n        .returning(\"id\")\n        .asStep()\n        .toField\u003cInt\u003e(mapOf(\"user_id\" to userId, \"total\" to total))\n)\n\n// Step 2: Create order items using the handle\nfor (item in cartItems) {\n    val orderItem: Map\u003cString, Any?\u003e = mapOf(\n        \"order_id\" to orderIdHandle.field(),  // Reference future value\n        \"product_id\" to item.productId,\n        \"quantity\" to item.quantity\n    )\n    \n    plan.add(\n        dataAccess.insertInto(\"order_items\")\n            .values(orderItem)\n            .asStep()\n            .execute(orderItem)\n    )\n}\n\n// Execute all steps in single transaction\ndataAccess.executeTransactionPlan(plan)\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| `date`                    | `LocalDate`     | `kotlinx.datetime` \u003csup\u003e*\u003c/sup\u003e |\n| `time`                    | `LocalTime`     | `kotlinx.datetime`              |\n| `timetz`                  | `OffsetTime`    | `java.time`                     |\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-types) 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 Address(val street: String, val city: String, val zipCode: String)\n\n// PostgreSQL ENUM → Kotlin enum\n@PgEnum\nenum class OrderStatus { Pending, Processing, Shipped, Delivered }\n\n// Works seamlessly in queries\ndata class Order(val id: Int, val status: OrderStatus, val shippingAddress: Address)\n\nval orders = dataAccess.select(\"id\", \"status\", \"shipping_address\")\n    .from(\"orders\")\n    .toListOf\u003cOrder\u003e()  // Types converted automatically\n```\n\n## Dynamic Type System\n\nOctavius uses `dynamic_dto` — a PostgreSQL composite type combining a type discriminator with JSONB payload — to bridge static SQL and Kotlin's type system. This type is automatically initialized in your database on startup.\n\n```sql\n-- Created automatically by Octavius\nCREATE TYPE dynamic_dto AS (\n    type_name    TEXT,\n    data_payload JSONB\n);\n\n-- Helper function for constructing values\nCREATE FUNCTION dynamic_dto(p_type_name TEXT, p_data JSONB)\nRETURNS dynamic_dto AS $$\nBEGIN\n    RETURN ROW(p_type_name, p_data)::dynamic_dto;\nEND;\n$$ LANGUAGE plpgsql;\n```\n\n### 1. Polymorphic Storage\n\nStore different types in a single column or array. The framework deserializes each element to its correct Kotlin class based on `type_name`.\n\n```kotlin\n@DynamicallyMappable(typeName = \"text_note\")\n@Serializable\ndata class TextNote(val content: String)\n\n@DynamicallyMappable(typeName = \"image_note\")\n@Serializable\ndata class ImageNote(val url: String, val caption: String?)\n\n// Database: CREATE TABLE notebooks (id INT, notes dynamic_dto[]);\n\nval notes: List\u003cAny\u003e = listOf(\n    TextNote(\"Hello world\"),\n    ImageNote(\"https://example.com/img.png\", \"A photo\")\n)\n\ndataAccess.insertInto(\"notebooks\")\n    .values(listOf(\"notes\"))\n    .execute(\"notes\" to notes)\n\n// Read back — each element deserialized to its correct type\nval notebook = dataAccess.select(\"notes\")\n    .from(\"notebooks\")\n    .where(\"id = 1\")\n    .toField\u003cList\u003cAny\u003e\u003e()  // Returns [TextNote(...), ImageNote(...)]\n```\n\n### 2. 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 = \"user_profile\")\n@Serializable\ndata class UserProfile(val role: String, val permissions: List\u003cString\u003e)\n\ndata class UserWithProfile(val id: Int, val name: String, val profile: UserProfile)\n\nval users = dataAccess.rawQuery(\"\"\"\n    SELECT\n        u.id,\n        u.name,\n        dynamic_dto(\n            'user_profile',\n            jsonb_build_object(\n                'role', p.role,\n                'permissions', p.permissions\n            )\n        ) AS profile\n    FROM users u\n    JOIN profiles p ON p.user_id = u.id\n\"\"\").toListOf\u003cUserWithProfile\u003e()\n```\n\n\u003e **Why use this?** Usually, to get a user with their profile in one query, you'd fetch flat columns (`user_id`, `user_name`, `profile_role`...) and manually map them, or create a database VIEW. With ad-hoc mapping, you construct the nested structure directly in SQL. The database does the packaging, Octavius does the unpacking — zero boilerplate.\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/mydb\ndb.username=postgres\ndb.password=secret\ndb.schemas=public,myschema\ndb.packagesToScan=com.myapp.domain,com.myapp.dto\n\n# Optional settings\ndb.setSearchPath=true\ndb.dynamicDtoStrategy=AUTOMATIC_WHEN_UNAMBIGUOUS\ndb.disableFlyway=false\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/mydb\",\n        dbUsername = \"user\",\n        dbPassword = \"pass\",\n        dbSchemas = listOf(\"public\"),\n        packagesToScan = listOf(\"com.myapp.domain\")\n    )\n)\n\n// From existing DataSource\nval dataAccess = OctaviusDatabase.fromDataSource(existingDataSource, ...)\n```\n\n## Database Migrations\n\nOctavius Database integrates [Flyway](https://flywaydb.org/) for schema migrations. Migration files are loaded from `src/main/resources/db/migration/` and applied automatically on startup.\n\n- To disable automatic migrations, set `disableFlyway = true` in `DatabaseConfig`.\n- To integrate with an existing database, set `flywayBaselineVersion` to the current version. Flyway will treat the existing schema as the baseline.\n\n## Documentation\n\nFor detailed guides and examples, see the [full documentation](docs/README.md):\n\n- [Configuration](docs/configuration.md) - Initialization, Flyway, core types, DynamicDto strategy\n- [Query Builders](docs/query-builders.md) - SELECT, INSERT, UPDATE, DELETE, CTEs, subqueries, ON CONFLICT\n- [Executing Queries](docs/executing-queries.md) - Terminal methods, DataResult, async, streaming\n- [Data Mapping](docs/data-mapping.md) - toMap(), toDataObject(), @MapKey, nested structures \u0026 strict typing\n- [ORM-Like Patterns](docs/orm-patterns.md) - CRUD patterns, real-world examples\n- [Transactions](docs/transactions.md) - Transaction plans, StepHandle, passing data between steps\n- [Error Handling](docs/error-handling.md) - Exception hierarchy, debugging\n- [Type System](docs/type-system.md) - @PgEnum, @PgComposite, @DynamicallyMappable, helper serializers\n\n## Architecture\n\n| Module | Platform      | Description                                                   |\n|--------|---------------|---------------------------------------------------------------|\n| `api`  | Multiplatform | Public API, interfaces; annotations with no JVM dependencies. |\n| `core` | JVM           | Implementation using Spring JDBC \u0026 HikariCP.                  |\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpolskianonim%2Foctaviusdatabase","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpolskianonim%2Foctaviusdatabase","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpolskianonim%2Foctaviusdatabase/lists"}