|claim|login
RepoCritics — Review. Share. Archive. Every open-source repo.

seancorfield/next-jdbc

Wiki: seancorfield/next-jdbc

Source: https://github.com/seancorfield/next-jdbc

Last synced 2026-07-18 · 1138 words · Edit wiki on GitHub →

seancorfield/next-jdbc

> The de facto standard Clojure wrapper for JDBC — a deliberately low-level, > protocol-based successor to clojure.java.jdbc.

GitHub repo · Official docs · License: EPL-2.0

Overview

next.jdbc (repo name next-jdbc) is Sean Corfield's ground-up rewrite of clojure.java.jdbc, the Clojure contrib library he previously maintained. It reached 1.0.0 in June 20191 and has since displaced its predecessor as the default way Clojure programs talk to relational databases over JDBC. The design goals were explicit: less overhead when realizing ResultSets (via IReduceInit/transducers), a smaller and more consistent API surface, and qualified keywords (:table/column) as the default row representation1.

The defining tension is that next.jdbc is low-level on purpose. It hands you raw javax.sql.DataSource and java.sql.Connection objects rather than wrapping them, does no SQL generation beyond a thin "friendly functions" namespace, and pushes decisions like connection pooling and streaming configuration onto you. That makes it predictable and fast, but it means most real applications pair it with a SQL builder (HoneySQL) and a pool (HikariCP) rather than using it alone.

At 861 stars it looks small next to mainstream-language libraries, but star counts undercount Clojure infrastructure: this is the community-consensus choice, and maintenance is unusually healthy for a solo project — zero open issues and commits within the last week as of mid-2026. Versioning is MAJOR.MINOR.COMMITS (not semver); the stated policy since 1.0 is accretive and fixative changes only1.

Getting Started

;; deps.edn
{:deps {com.github.seancorfield/next.jdbc {:mvn/version "1.3.1118"}
        org.postgresql/postgresql        {:mvn/version "42.7.3"}}}
(require '[next.jdbc :as jdbc])

(def ds (jdbc/get-datasource
          {:dbtype "postgresql" :dbname "app" :user "app" :password "..."}))

(jdbc/execute! ds ["create table address (id serial primary key, name text)"])

(jdbc/execute-one! ds ["insert into address (name) values (?)" "Ada"]
                   {:return-keys true})
;; => #:address{:id 1, :name "Ada"}

(jdbc/execute! ds ["select * from address where id = ?" 1])
;; => [#:address{:id 1, :name "Ada"}]  ; qualified keywords by default

Architecture / How It Works

The library is a thin protocol layer over java.sql:

  • Sourceable turns things (db-spec maps, JDBC URL strings) into a

DataSource; Connectable yields Connections. Because these are protocols, they extend to third-party pool objects and even via metadata. The deliberate path is db-spec → DataSourceConnection, steering users toward connection reuse1.

  • Three execution functions cover everything: plan returns an

IReduceInit that executes the SQL only when reduced and processes the live ResultSet with minimal allocation; execute! realizes a vector of hash maps; execute-one! realizes a single row (or {:next.jdbc/update-count N} for non-queries). This replaces the query/execute!/db-do-commands split that made clojure.java.jdbc confusing1.

  • Result-set builders (:builder-fn) control row shape: qualified maps

(default), unqualified maps, arrays, lower-case variants, or your own.

  • datafy/nav support is built in: rows from execute! are

navigable, so tools like Portal or Reveal can lazily walk foreign-key relationships out of a query result2.

  • prepare exposes PreparedStatements; transact /

with-transaction manage transactions; next.jdbc.sql holds the syntactic-sugar insert!/update!/delete!/find-by-keys helpers, intentionally outside the core API.

There is no dialect abstraction. SQL strings pass through verbatim, and database-specific behavior (streaming, generated keys, type coercion) is handled by options and documented per-database quirks rather than by an adapter layer.

Production Notes

  • get-datasource does not pool. The stock DataSource opens a fresh

Connection per operation, which is fine at a REPL and slow in production. Use next.jdbc.connection/->pool with HikariCP or c3p0, and remember the pool object itself needs closing on shutdown2.

  • plan rows are not real maps. Inside the reduction you get a

lightweight view over the current ResultSet row; keyword access never builds a map. If a row escapes the reduction (returned, logged, assoc'd) you get errors or garbage. Call next.jdbc.result-set/datafiable-row (or select-keys) to realize a row you intend to keep2.

  • Nested transactions are a footgun. JDBC has no true nested

transactions. By default a with-transaction inside another one simply commits/rolls back on the same connection, which can silently commit the outer transaction's work early. next.jdbc.transaction/*nested-tx* can be set to :ignore or :prohibit to make the behavior explicit3.

  • Streaming large result sets is database-specific. PostgreSQL requires

:fetch-size plus a connection with auto-commit off; MySQL needs useCursorFetch=true and its own incantations. Without this, drivers buffer the entire result set in memory even under plan4.

  • Date/time coercion varies by driver. Requiring next.jdbc.date-time

enables reading SQL date/timestamp columns as Java Time types and adds parameter coercions some drivers lack. Skipping it is a common source of "works on H2, breaks on X" surprises4.

  • Upgrades are boring by design. The accretion-only policy means jumps

across the 1.3 line are expected to be drop-in; the real migration effort in this ecosystem is from clojure.java.jdbc, which has a dedicated guide5.

When to Use / When Not

Use when:

  • You are writing Clojure on the JVM against any JDBC-accessible database

and want the community-standard, actively maintained option.

  • You process large result sets and care about reduction without

intermediate sequence allocation (plan).

  • You want direct access to JDBC objects (with-open, prepared statements,

driver options) instead of an abstraction that hides them.

Avoid when:

  • You want an ORM-style entity layer, lifecycle hooks, or model definitions

— this is a wrapper, not a data mapper.

  • You expect SQL generation; next.jdbc.sql is minimal sugar, and complex

queries mean hand-written SQL or a companion DSL.

  • You are not on the JVM (ClojureScript has no JDBC) or your data store is

not relational.

Alternatives

  • clojure/java.jdbc — the predecessor, in maintenance mode; use only when

stuck on legacy code, and follow the official migration guide off it.

  • seancorfield/honeysql — complement rather than replacement: generates SQL

from Clojure data structures; most next.jdbc apps pair the two.

  • metabase/toucan2 — use instead when you want a higher-level entity/model

layer; it builds on next.jdbc and HoneySQL underneath.

  • metosin/porsas — experimental performance-oriented JDBC mapping; use for

benchmarking-grade hot paths, not as a maintained general library.

  • funcool/clojure.jdbc — abandoned alternative wrapper from the mid-2010s;

listed only so you avoid it in old tutorials.

History

VersionDateNotes
pre-1.02019-01Repo created; alpha/beta cycle through spring 20191.
1.0.02019-06-12"Gold" release; API declared stable after last rename (reducible!plan)1.
1.2.6742021First release under the com.github.seancorfield group ID after Clojars' verified-group policy; older releases live under seancorfield6.
1.3.x2022–Current line; accretive changes only (batch execution, docs, driver quirk fixes)6.
1.3.11182026Latest release as of 2026-07; repo pushed 2026-07-17.

References

  1. ^ next.jdbc README — motivation, release timeline, versioning policy. https://github.com/seancorfield/next-jdbc#motivation
  2. ^ next.jdbc, "Getting Started" (cljdoc). https://cljdoc.org/d/com.github.seancorfield/next.jdbc/CURRENT/doc/getting-started
  3. ^ next.jdbc, "Transactions". https://github.com/seancorfield/next-jdbc/blob/develop/doc/transactions.md
  4. ^ next.jdbc, "Tips & Tricks" (per-database streaming and type quirks). https://github.com/seancorfield/next-jdbc/blob/develop/doc/tips-and-tricks.md
  5. ^ next.jdbc, "Migrating from clojure.java.jdbc". https://cljdoc.org/d/com.github.seancorfield/next.jdbc/CURRENT/doc/migration-from-clojure-java-jdbc
  6. ^ next.jdbc CHANGELOG. https://github.com/seancorfield/next-jdbc/blob/develop/CHANGELOG.md

Tags

clojure, jvm, jdbc, sql, database, data-access, library, functional-programming, relational-database