{"id":20135789,"url":"https://github.com/replikativ/konserve-jdbc","last_synced_at":"2026-01-18T11:53:23.200Z","repository":{"id":45557700,"uuid":"413722741","full_name":"replikativ/konserve-jdbc","owner":"replikativ","description":"A JDBC backend for konserve.","archived":false,"fork":false,"pushed_at":"2025-06-19T16:55:19.000Z","size":115,"stargazers_count":2,"open_issues_count":3,"forks_count":5,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-07-30T01:55:47.915Z","etag":null,"topics":["clojure","jdbc","konserve"],"latest_commit_sha":null,"homepage":"","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"epl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/replikativ.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yaml","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},"funding":{"github":"replikativ"}},"created_at":"2021-10-05T07:50:05.000Z","updated_at":"2025-06-19T16:49:50.000Z","dependencies_parsed_at":"2023-09-22T07:08:26.127Z","dependency_job_id":"4fb1ae3d-9357-4813-93e5-e2486ed7e018","html_url":"https://github.com/replikativ/konserve-jdbc","commit_stats":{"total_commits":71,"total_committers":8,"mean_commits":8.875,"dds":0.3098591549295775,"last_synced_commit":"faaf2002b71dac475159c94ce64c9e0960c7b73c"},"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/replikativ/konserve-jdbc","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/replikativ%2Fkonserve-jdbc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/replikativ%2Fkonserve-jdbc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/replikativ%2Fkonserve-jdbc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/replikativ%2Fkonserve-jdbc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/replikativ","download_url":"https://codeload.github.com/replikativ/konserve-jdbc/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/replikativ%2Fkonserve-jdbc/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":269740800,"owners_count":24467847,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-10T02:00:08.965Z","response_time":71,"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":["clojure","jdbc","konserve"],"created_at":"2024-11-13T21:16:32.823Z","updated_at":"2026-01-18T11:53:23.185Z","avatar_url":"https://github.com/replikativ.png","language":"Clojure","funding_links":["https://github.com/sponsors/replikativ"],"categories":[],"sub_categories":[],"readme":"# konserve-jdbc\n\nA [JDBC](https://github.com/clojure/java.jdbc) backend for [konserve](https://github.com/replikativ/konserve). \n\n## Usage\n\nAdd to your dependencies:\n\n[![Clojars Project](http://clojars.org/org.replikativ/konserve-jdbc/latest-version.svg)](http://clojars.org/org.replikativ/konserve-jdbc)\n\n### Configuration\n\nSupports all [JDBC-compatible databases](https://clojure.org/guides/deps_and_cli).\n\n``` clojure\n(require '[konserve-jdbc.core]  ;; Registers the :jdbc backend\n         '[konserve.core :as k])\n\n;; SQLite\n(def sqlite-config\n  {:backend :jdbc\n   :dbtype \"sqlite\"\n   :dbname \"./tmp/konserve.db\"\n   :table \"store\"\n   :id #uuid \"550e8400-e29b-41d4-a716-446655440000\"})\n\n;; PostgreSQL\n(def postgres-config\n  {:backend :jdbc\n   :dbtype \"postgresql\"\n   :dbname \"mydb\"\n   :host \"localhost\"\n   :user \"postgres\"\n   :password \"password\"\n   :table \"konserve\"\n   :id #uuid \"550e8400-e29b-41d4-a716-446655440001\"})\n\n;; MySQL\n(def mysql-config\n  {:backend :jdbc\n   :dbtype \"mysql\"\n   :dbname \"mydb\"\n   :host \"localhost\"\n   :user \"root\"\n   :password \"password\"\n   :table \"konserve\"\n   :id #uuid \"550e8400-e29b-41d4-a716-446655440002\"})\n\n(def store (k/create-store sqlite-config {:sync? true}))\n```\n\nFor API usage (assoc-in, get-in, delete-store, etc.), see the [konserve documentation](https://github.com/replikativ/konserve).\n\n## Multitenancy\n\nMultiple independent stores can be housed in the same database. Each store has:\n- `:id` - The virtual/global identifier for the store (required, UUID)\n- `:table` - The physical database table where data is stored (optional, defaults to \"konserve\")\n\n``` clojure\n(def db  {:dbtype  \"postgresql\"\n          :dbname  \"konserve\"\n          :host \"localhost\"\n          :user \"user\"\n          :password \"password\"})\n\n;; Store A - uses table \"app_a\"\n(def store-a-config\n  (assoc db\n    :backend :jdbc\n    :table \"app_a\"\n    :id #uuid \"11111111-1111-1111-1111-111111111111\"))\n\n;; Store B - uses table \"app_b\"\n(def store-b-config\n  (assoc db\n    :backend :jdbc\n    :table \"app_b\"\n    :id #uuid \"22222222-2222-2222-2222-222222222222\"))\n\n;; Store C - uses default \"konserve\" table with different ID\n(def store-c-config\n  (assoc db\n    :backend :jdbc\n    :id #uuid \"33333333-3333-3333-3333-333333333333\"))\n```\n\nIn terms of priority a table specified using the keyword argument takes priority, followed\nby the one specified in the `db-spec`. If no table is specified `konserve` is used as the table name.\n\n``` clojure\n(def cfg-a  {:dbtype  \"postgresql\"\n             :jdbcUrl \"postgresql://user:password@localhost/konserve\"\n             :id #uuid \"11111111-1111-1111-1111-111111111111\"})\n\n(def cfg-b  {:dbtype  \"postgresql\"\n             :jdbcUrl \"postgresql://user:password@localhost/konserve\"\n             :table   \"water\"\n             :id #uuid \"22222222-2222-2222-2222-222222222222\"})\n\n(def store-a (connect-jdbc-store cfg-a :opts {:sync? true})) ;; table name =\u003e konserve\n(def store-b (connect-jdbc-store cfg-b :opts {:sync? true}))  ;; table name =\u003e water \n(def store-c (connect-jdbc-store cfg-b :table \"fire\" :opts {:sync? true})) ;;table name =\u003e fire\n``````\n\n## Multi-key Operations\n\nThis backend supports atomic multi-key operations (`multi-assoc`, `multi-get`, `multi-dissoc`), allowing you to read, write, or delete multiple keys in a single operation. All operations use JDBC transactions for atomicity.\n\n``` clojure\n;; Write multiple keys atomically\n(k/multi-assoc store {:user1 {:name \"Alice\"}\n                      :user2 {:name \"Bob\"}}\n               {:sync? true})\n\n;; Read multiple keys in one request\n(k/multi-get store [:user1 :user2 :user3] {:sync? true})\n;; =\u003e {:user1 {:name \"Alice\"}, :user2 {:name \"Bob\"}}\n;; Note: Returns sparse map - only found keys are included\n\n;; Delete multiple keys atomically\n(k/multi-dissoc store [:user1 :user2] {:sync? true})\n;; =\u003e {:user1 true, :user2 true}\n;; Returns map indicating which keys existed before deletion\n\n;; Async versions\n(\u003c! (k/multi-assoc store {:user1 {:name \"Alice\"}}))\n(\u003c! (k/multi-get store [:user1 :user2]))\n(\u003c! (k/multi-dissoc store [:user1 :user2]))\n```\n\n### Batch Limits\n\nOperations exceeding batch limits are **automatically batched** within a single transaction - this is transparent to the caller.\n\n**Write batch limits** (rows per batch, based on SQL parameter limits):\n\n| Database | Rows per batch |\n|----------|----------------|\n| PostgreSQL | 2500 |\n| SQL Server/MSSQL | 450 |\n| SQLite/MySQL/H2 | 225 |\n\n**Read batch limits** (keys per SELECT IN clause):\n\n| Database | Keys per batch |\n|----------|----------------|\n| PostgreSQL | 5000 |\n| H2 | 2000 |\n| SQL Server/MSSQL | 1500 |\n| MySQL | 1000 |\n| SQLite | 500 |\n\n**Delete batch limits** (keys per DELETE IN clause):\n\n| Database | Keys per batch |\n|----------|----------------|\n| PostgreSQL | 10000 |\n| SQL Server/MSSQL | 1800 |\n| SQLite/MySQL/H2 | 900 |\n\n### Implementation Details\n\n- All operations are wrapped in JDBC transactions for atomicity (all-or-nothing)\n- Uses bulk INSERT/UPSERT statements for efficient writes\n- Connection pooling via c3p0 is used by default for concurrent access\n- Database-specific SQL syntax is handled automatically (MERGE, ON CONFLICT, REPLACE, etc.)\n\n(def store-a (k/create-store store-a-config {:sync? true}))\n(def store-b (k/create-store store-b-config {:sync? true}))\n(def store-c (k/create-store store-c-config {:sync? true}))\n```\n\nThe `:id` is the authoritative virtual store identity used for global identification. The `:table` determines the physical storage location. Multiple stores can share the same table but must have different `:id` values.\n\n## Implementation Details\n\n### Multi-key Operations\n\nThis backend supports atomic multi-key operations (`multi-assoc`, `multi-get`, `multi-dissoc`). All operations use JDBC transactions for atomicity.\n\n**Automatic Batching**: Operations exceeding database-specific limits are automatically batched within a single transaction - this is transparent to the caller.\n\n**Write batch limits** (rows per batch, based on SQL parameter limits):\n\n| Database | Rows per batch |\n|----------|----------------|\n| PostgreSQL | 2500 |\n| SQL Server/MSSQL | 450 |\n| SQLite/MySQL/H2 | 225 |\n\n**Read batch limits** (keys per SELECT IN clause):\n\n| Database | Keys per batch |\n|----------|----------------|\n| PostgreSQL | 5000 |\n| H2 | 2000 |\n| SQL Server/MSSQL | 1500 |\n| MySQL | 1000 |\n| SQLite | 500 |\n\n**Delete batch limits** (keys per DELETE IN clause):\n\n| Database | Keys per batch |\n|----------|----------------|\n| PostgreSQL | 10000 |\n| SQL Server/MSSQL | 1800 |\n| SQLite/MySQL/H2 | 900 |\n\n**Transaction Support**: All operations are wrapped in JDBC transactions for atomicity. Uses bulk INSERT/UPSERT statements for efficient writes. Connection pooling via c3p0 is used by default for concurrent access. Database-specific SQL syntax is handled automatically.\n\n## Supported Databases\n\n**BREAKING CHANGE**: konserve-jdbc versions after `0.1.79` no longer include\nactual JDBC drivers. Before you upgrade please make sure your application\nprovides the necessary dependencies.\n\nNot all databases available for JDBC have been tested to work with this implementation.\nOther databases might still work, but there is no guarantee. Please see working\ndrivers in the dev-alias in the `deps.edn` file.\nIf you are interested in another database, please feel free to contact us.\n\nFully supported so far are the following databases:\n\n1) PostgreSQL\n\n``` clojure\n(def pg-cfg  {:dbtype \"postgresql\"\n              :dbname \"konserve\"\n              :host \"localhost\"\n              :user \"user\"\n              :password \"password\"})\n\n(def pg-url  {:dbtype  \"postgresql\"\n              :jdbcUrl \"postgresql://user:password@localhost/konserve\"})\n```\n\n2) MySQL\n\n``` clojure\n(def mysql-cfg {:dbtype \"mysql\"\n                :dbname \"konserve\"\n                :host \"localhost\"\n                :user \"user\"\n                :password \"password\"})\n\n(def mysql-url {:dbtype  \"mysql\"\n                :jdbcUrl \"mysql://user:password@localhost/konserve\"})\n```\n\n3) SQlite\n\n``` clojure\n(def sqlite {:dbtype \"sqlite\"\n             :dbname \"/konserve\"})\n```\n\n4) SQLserver\n\n``` clojure\n(def sqlserver {:dbtype \"sqlserver\"\n                :dbname \"konserve\"\n                :host \"localhost\"\n                :user \"sa\"\n                :password \"password\"})\n```\n\n5) MSSQL\n\n``` clojure\n(def mssql {:dbtype \"mssql\"\n            :dbname \"konserve\"\n            :host \"localhost\"\n            :user \"sa\"\n            :password \"password\"})\n```\n\n6) H2\n\n``` clojure\n(def h2 {:dbtype \"h2\"\n         :dbname \"tmp/konserve;DB_CLOSE_ON_EXIT=FALSE\"\n         :user \"sa\"\n         :password \"\"})\n```\n\n## License\n\nCopyright © 2021-2026 Alexander Oloo, Christian Weilbach, Judith Massa\n\nLicensed under Eclipse Public License (see [LICENSE](LICENSE)).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Freplikativ%2Fkonserve-jdbc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Freplikativ%2Fkonserve-jdbc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Freplikativ%2Fkonserve-jdbc/lists"}