• Stars
    star
    714
  • Rank 61,009 (Top 2 %)
  • Language
    Clojure
  • License
    Eclipse Public Li...
  • Created over 5 years ago
  • Updated 3 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A modern low-level Clojure wrapper for JDBC-based access to databases.

next.jdbc Clojure CI Release Clojure CI Develop Clojure CI Pull Request

The next generation of clojure.java.jdbc: a new low-level Clojure wrapper for JDBC-based access to databases.

Featured in Jacek Schae's Learn Reitit Pro online course!

TL;DR

The latest versions on Clojars and on cljdoc:

Clojars cljdoc Slack Join Slack

The documentation on cljdoc.org is for the current version of next.jdbc:

The documentation on GitHub is for develop since the 1.3.925 release -- see the CHANGELOG and then read the corresponding updated documentation on GitHub if you want. Older versions of next.jdbc were published under the seancorfield group ID and you can find older seancorfield/next.jdbc documentation on cljdoc.org.

This project follows the version scheme MAJOR.MINOR.COMMITS where MAJOR and MINOR provide some relative indication of the size of the change, but do not follow semantic versioning. In general, all changes endeavor to be non-breaking (by moving to new names rather than by breaking existing names). COMMITS is an ever-increasing counter of commits since the beginning of this repository.

Note: every commit to the develop branch runs CI (GitHub Actions) and successful runs push a MAJOR.MINOR.999-SNAPSHOT build to Clojars so the very latest version of next.jdbc is always available either via that snapshot on Clojars or via a git dependency on the latest SHA.

Motivation

Why another JDBC library? Why a different API from clojure.java.jdbc?

  • Performance: there's a surprising amount of overhead in how ResultSet objects are converted to sequences of hash maps in clojure.java.jdbc โ€“ which can be really noticeable for large result sets โ€“ so I wanted a better way to handle that. There's also quite a bit of overhead and complexity in all the conditional logic and parsing that is associated with db-spec-as-hash-map.
  • A more modern API, based on using qualified keywords and transducers etc: :qualifier and reducible-query in recent clojure.java.jdbc versions were steps toward that but there's a lot of "legacy" API in the library and I want to present a more focused, more streamlined API so folks naturally use the IReduceInit / transducer approach from day one and benefit from qualified keywords.
  • Simplicity: clojure.java.jdbc uses a variety of ways to execute SQL which can lead to inconsistencies and surprises โ€“ query, execute!, and db-do-commands are all different ways to execute different types of SQL statement so you have to remember which is which and you often have to watch out for restrictions in the underlying JDBC API.

Those were my three primary drivers. In addition, the db-spec-as-hash-map approach in clojure.java.jdbc has caused a lot of frustration and confusion in the past, especially with the wide range of conflicting options that are supported. next.jdbc is heavily protocol-based so it's easier to mix'n'match how you use it with direct Java JDBC code (and the protocol-based approach contributes to the improved performance overall). There's a much clearer path of db-spec -> DataSource -> Connection now, which should steer people toward more connection reuse and better performing apps.

I also wanted datafy/nav support baked right in (it was added to clojure.java.jdbc back in December 2018 as an undocumented, experimental API in a separate namespace). It is the default behavior for execute! and execute-one!. The protocol-based function next.jdbc.result-set/datafiable-row can be used with plan if you need to add datafy/nav support to rows you are creating in your reduction.

As next.jdbc moved from alpha to beta, the last breaking change was made (renaming reducible! to plan) and the API should be considered stable. Only accretive and fixative changes will be made from now on.

After a month of alpha builds being available for testing, the first beta build was released on May 24th, 2019. A release candidate followed on June 4th and the "gold" (1.0.0) release was on June 12th. In addition to the small, core API in next.jdbc, there are "syntactic sugar" SQL functions (insert!, query, update!, and delete!) available in next.jdbc.sql that are similar to the main API in clojure.java.jdbc. See Migrating from clojure.java.jdbc for more detail about the differences.

Usage

The primary concepts behind next.jdbc are that you start by producing a javax.sql.DataSource. You can create a pooled datasource object using your preferred library (c3p0, hikari-cp, etc). You can use next.jdbc's get-datasource function to create a DataSource from a db-spec hash map or from a JDBC URL (string). The underlying protocol, Sourceable, can be extended to allow more things to be turned into a DataSource (and can be extended via metadata on an object as well as via types).

From a DataSource, either you or next.jdbc can create a java.sql.Connection via the get-connection function. You can specify an options hash map to get-connection to modify the connection that is created: :read-only, :auto-commit.

The primary SQL execution API in next.jdbc is:

  • plan -- yields an IReduceInit that, when reduced with an initial value, executes the SQL statement and then reduces over the ResultSet with as little overhead as possible.
  • execute! -- executes the SQL statement and produces a vector of realized hash maps, that use qualified keywords for the column names, of the form :<table>/<column>. If you join across multiple tables, the qualified keywords will reflect the originating tables for each of the columns. If the SQL produces named values that do not come from an associated table, a simple, unqualified keyword will be used. The realized hash maps returned by execute! are Datafiable and thus Navigable (see Clojure 1.10's datafy and nav functions, and tools like Portal, Reveal, and Cognitect's REBL). Alternatively, you can specify {:builder-fn rs/as-arrays} and produce a vector with column names followed by vectors of row values. rs/as-maps is the default for :builder-fn but there are also rs/as-unqualified-maps and rs/as-unqualified-arrays if you want unqualified :<column> column names (and there are also lower-case variants of all of these).
  • execute-one! -- executes the SQL or DDL statement and produces a single realized hash map. The realized hash map returned by execute-one! is Datafiable and thus Navigable.

In addition, there are API functions to create PreparedStatements (prepare) from Connections, which can be passed to plan, execute!, or execute-one!, and to run code inside a transaction (the transact function and the with-transaction macro).

Since next.jdbc uses raw Java JDBC types, you can use with-open directly to reuse connections and ensure they are cleaned up correctly:

  (let [my-datasource (jdbc/get-datasource {:dbtype "..." :dbname "..." ...})]
    (with-open [connection (jdbc/get-connection my-datasource)]
      (jdbc/execute! connection [...])
      (reduce my-fn init-value (jdbc/plan connection [...]))
      (jdbc/execute! connection [...])))

Usage scenarios

There are three intended usage scenarios that have driven the design of the API:

  • Execute a SQL statement and process it in a single eager operation, which may allow for the results to be streamed from the database (how to persuade JDBC to do that is database-specific!), and which cleans up resources before returning the result -- even if the reduction is short-circuited via reduced. This usage is supported by plan. This is likely to be the fastest approach and should be the first option you consider for SQL queries.
  • Execute a SQL or DDL statement to obtain a single, fully-realized, Datafiable hash map that represents either the first row from a ResultSet, the first generated keys result (again, from a ResultSet), or the first result where neither of those are available (next.jdbc yields {:next.jdbc/update-count N} when it can only return an update count). This usage is supported by execute-one!. This is probably your best choice for most non-query operations.
  • Execute a SQL statement to obtain a fully-realized, Datafiable result set -- a vector of hash maps. This usage is supported by execute!. You can also produce a vector of column names/row values (next.jdbc.result-set/as-arrays).

In addition, convenience functions -- "syntactic sugar" -- are provided to insert rows, run queries, update rows, and delete rows, using the same names as in clojure.java.jdbc. These are in next.jdbc.sql since they involve SQL creation -- they are not considered part of the core API.

More Detailed Documentation

License

Copyright ยฉ 2018-2021 Sean Corfield

Distributed under the Eclipse Public License version 1.0.

More Repositories

1

honeysql

Turn Clojure data structures into SQL
Clojure
1,706
star
2

dot-clojure

My .clojure/deps.edn file
Clojure
609
star
3

usermanager-example

A little demo web app in Clojure, using Component, Ring, Compojure, Selmer (and a database)
Clojure
310
star
4

deps-new

A new, simpler alternative to clj-new
Clojure
294
star
5

build-clj

Common build tasks abstracted into a library.
Clojure
154
star
6

readme

A testing library that turns your README into executable Clojure tests!
Clojure
143
star
7

vscode-calva-setup

My VS Code / Calva / Portal / Joyride setup
Clojure
83
star
8

om-sente

Playground to create Om + Sente test app
JavaScript
46
star
9

jsql

Basic DSL for generating SQL/DDL, formerly java.jdbc.sql and java.jdbc.ddl
Clojure
43
star
10

boot-tools-deps

A Boot task (deps) that wraps tools.deps(.alpha) to read deps.edn files
Clojure
40
star
11

engine

A Clojure library to implement a query -> logic -> updates workflow, to separate persistence updates from business logic, to improve testing etc.
Clojure
22
star
12

clj-soap

Fork of https://bitbucket.org/taka2ru/clj-soap updated to latest Clojure version
Clojure
21
star
13

lein-fregec

A Leiningen plugin to compile Frege (http://www.frege-lang.org) code.
Clojure
20
star
14

atom-chlorine-setup

My Atom / Chlorine setup
Clojure
17
star
15

socket-rebl

A Socket REPL that also submits forms to Cognitect's REBL
Clojure
13
star
16

next.jdbc.xt

Experimental extension of next.jdbc to work with XTDB 2.0 (snapshots)
Clojure
12
star
17

ring-cfml

A version of Ring (Clojure) for CFML
ColdFusion
12
star
18

polylith-external-test-runner

An external (subprocess) test runner for Polylith
Clojure
10
star
19

java-clojure-example

Trivial example to show using Java from Clojure in deps.edn project
Clojure
8
star
20

datamapper

A couple of CFCs from the World Singles data mapper to show how we wrap Clojure (vectors of) hashmaps to present a thin OO veneer to our CFML code.
ColdFusion
8
star
21

liquid-setup

Configuration for the Liquid in-process Clojure editor
Clojure
7
star
22

boot-kotlinc

A Kotlin compilation task for Boot
Clojure
6
star
23

macro-day

Code examples I wrote during Amit Rathore's "A day with Clojure macros" training
Clojure
6
star
24

edmund

Edmund is an Event-Driven Model micro-framework for CFML / ColdFusion
ColdFusion
5
star
25

build-uber-log4j2-handler

A conflict handler for log4j2 plugins cache files for the tools.build uber task.
Clojure
4
star
26

avowal

Futures and Promises for modern CFML, inspired by my earlier cfconcurrency library
ColdFusion
3
star
27

datafy-nav-example

Examples of datafy and nav
Clojure
3
star
28

lein-fw1

A Leiningen plugin to create and manage FW/1 projects.
Clojure
3
star
29

cfml-interop

CFML/Clojure interop library extracted from World Singles code and open sourced!
Clojure
3
star
30

clojure-dining-car

A Categorized and Annotated Directory of Clojure Libraries
2
star
31

orm-blog

A very simple blog/cms built in ColdFusion using Framework 1 and ORM.
ColdFusion
2
star
32

intro2fp

Code samples for Introduction to Functional Programming talk, cf.Objective() 2011
ColdFusion
2
star
33

cat-genetics

Utilities to help calculate TICA-specific color cat genetics etc
Clojure
2
star
34

clojure-lucee

A toy example of running CFML pages via Lucee as an embedded engine in a Clojure Ring application.
Clojure
2
star
35

lightstuff

Historical archive of Peter Bell's LightBase and LightGen utility framework/code
ColdFusion
2
star
36

poly-classloader-bug

Repro for a potential classloader bug for poly test
Clojure
1
star
37

dojo-anthem

Team 1's code from the January 2013 San Francisco Clojure Dojo - to play the National Anthem
Clojure
1
star
38

cursive-expectations

Example of using Expectations with Cursive
Clojure
1
star
39

spd1

Introduction to Systematic Program Design Part 1 - some Clojure examples
Clojure
1
star
40

ordered-subset

Clojure
1
star
41

HUnitFrege

A port of HUnit from Haskell to Frege (WIP -- not even compiling yet!)
Frege
1
star
42

cfo2013

Code examples for my cf.Objective() 2013 presentations
ColdFusion
1
star
43

clojure-test

A place to discuss clojure.test concerns and possible enhancements
1
star
44

boot-localrepo

Boot tasks that wrap lein-localrepo functionality
Clojure
1
star
45

seancorfield.github.io

An Architect's View - my blog and personal web site
HTML
1
star
46

polylith-issue146

Repro for the setup-fn problem with the issue-146 branch
Clojure
1
star
47

WeeklyWrongChallenge

The weekly challenge repo for WPW
Clojure
1
star
48

ring-async-bug

SSCCE of Ring with async on Jetty that fails with thread death
Clojure
1
star
49

avaus-tv

Simulation of Avaus-TV prize draw, in Clojure
Clojure
1
star
50

hello-compojure

The Compojure template, but without lein-ring
Clojure
1
star
51

cfbloggers

Clojure scratch code to parse and analyze the feeds at coldfusionbloggers.org
Clojure
1
star
52

clj-kondo

A linter for Clojure code that sparks joy.
Clojure
1
star