emacs.d/clones/clojure-doc.org/articles/ecosystem/java_jdbc/using_sql.html
2022-08-24 19:36:32 +02:00

510 lines
36 KiB
HTML

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<meta charset="utf-8"/>
<title>Clojure Guides: java.jdbc - Manipulating data with SQL</title>
<meta name="description" content="Contents">
<meta property="og:description" content="Contents">
<meta property="og:url" content="https://clojure-doc.github.io/articles/ecosystem/java_jdbc/using_sql/" />
<meta property="og:title" content="java.jdbc - Manipulating data with SQL" />
<meta property="og:type" content="article" />
<link rel="canonical" href="https://clojure-doc.github.io/articles/ecosystem/java_jdbc/using_sql/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://fonts.googleapis.com/css?family=Alegreya:400italic,700italic,400,700" rel="stylesheet"
type="text/css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/default.min.css">
<link href="../../../css/screen.css" rel="stylesheet" type="text/css" />
</head>
<body>
<nav class="navbar navbar-default">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="../../../index.html">Clojure Guides</a>
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav navbar-right">
<li ><a href="../../../index.html">Home</a></li>
<li><a href="https://github.com/clojure-doc/clojure-doc.github.io">Contribute</a></li>
</ul>
</div><!--/.nav-collapse -->
</div><!--/.container-fluid -->
</nav>
<div class="container">
<div class="row">
<div class="col-lg-9">
<div id="content">
<div id="custom-page">
<div id="page-header">
<h2>java.jdbc - Manipulating data with SQL</h2>
</div>
<h2 id="contents">Contents</h2><ul><li><a href="home.html">Overview</a></li><li><a href="using_sql.html">Using SQL</a></li><li><a href="using_ddl.html">Using DDL</a></li><li><a href="reusing_connections.html">Reusing Connections</a></li></ul><h2 id="using-sql">Using SQL</h2><p>Here are some examples of using <code>java.jdbc</code> to manipulate data with SQL.
These examples assume a simple table called <code>fruit</code> (see <a href="https://clojure-doc.org/articles/ecosystem/java_jdbc/using_sql/using_ddl/">Using DDL and
Metadata</a>). These examples all assume the following in your
<code>ns</code> declaration:</p><pre><code class="clojure">(:require [clojure.java.jdbc :as jdbc])
</code></pre><h2 id="reading-and-processing-rows">Reading and processing rows</h2><p><code>java.jdbc</code> provides a simple <code>query</code> function to allow you to read rows from
tables, as well as optionally performing processing on them at the same time.</p><h3 id="reading-rows">Reading rows</h3><p>To obtain a fully realized result set as a sequence of maps, you can use
<code>query</code> with a vector containing the SQL string and any parameters needed by
the SQL:</p><pre><code class="clojure">(jdbc/query db-spec ["SELECT * FROM fruit"])
;; ({:id 1 :name "Apple" :appearance "red" :cost 59 :grade 87}
;; {:id 2 :name "Banana" :appearance "yellow" :cost 29 :grade 92.2}
;; ...)
(jdbc/query db-spec ["SELECT * FROM fruit WHERE cost &lt; ?" 50])
;; ({:id 2 :name "Banana" :appearance "yellow" :cost 29 :grade 92.2}
;; ...)
</code></pre><p>You can also return the result set as a sequence of vectors. The first vector
will contain the column names, and each subsequent vector will represent a row
of data with values in the same order as the columns.</p><pre><code class="clojure">(jdbc/query db-spec ["SELECT * FROM fruit WHERE cost &lt; ?" 50]
{:as-arrays? true})
;; ([:id :name :appearance :cost :grade]
;; [2 "Banana" "yellow" 29 92.2]
;; ...)
</code></pre><h3 id="processing-a-result-set-lazily">Processing a result set lazily</h3><p>Since <code>query</code> returns a fully realized result set, it can be difficult to
process very large results. Fortunately, <code>java.jdbc</code> provides a number of ways to process a
large result set lazily while the connection is open, either by passing a function via
the <code>:result-set-fn</code> option or, since release 0.7.0, via <code>reducible-query</code>.</p><p><strong><code>query</code> and <code>:result-set-fn</code></strong></p><p><em>If you are using release 0.7.0 or later, consider using <code>reducible-query</code> instead -- see below.</em></p><p>For <code>:result-set-fn</code>, the function you pass must force
realization of the result to avoid the connection closing while the result set
is still being processed. A <code>reduce</code>-based function is a good choice.</p><pre><code class="clojure">(jdbc/query db-spec ["SELECT * FROM fruit WHERE cost &lt; ?" 50]
{:result-set-fn (fn [rs]
(reduce (fn [total row-map]
(+ total (:cost row-map)))
0 rs))})
;; produces the total cost of all the cheap fruits: 437
</code></pre><p>Of course, a simple sum like this could be computed directly in SQL instead:</p><pre><code class="clojure">(jdbc/query db-spec ["SELECT SUM(cost) FROM fruit WHERE cost &lt; ?" 50]
{:result-set-fn first})
;; {:sum(cost) 437}
</code></pre><p>We know we will only get one row back so passing <code>first</code> to <code>:result-set-fn</code> is
a quick way to get just that row.</p><p>Remember that if you also specify <code>:as-arrays? true</code>, your result set function
will be passed a sequence of vectors in which the first vector contains the
column names and subsequent vectors represent the values in the rows, matching
the order of the column names.</p><p><strong><code>reducible-query</code></strong></p><p>This is the recommended approach since release 0.7.0 but it does come with a few
restrictions:</p><p>You cannot use any of the following options that <code>query</code> accepts:
<code>as-arrays?</code>, <code>:explain</code>, <code>:explain-fn</code>, <code>:result-set-fn</code>, or <code>:row-fn</code>.</p><p>On the other hand, you have access to a much faster way to process result sets:
you can specify <code>:raw? true</code> and no conversion from Java's <code>ResultSet</code> to
Clojure's sequence of hash maps will be performed. In particular, it's as if you
specified <code>:identifiers identity :keywordize? false :qualifier nil</code>, and the
sequence representation of each row is not available. That means no <code>keys</code>,
no <code>vals</code>, no <code>seq</code> calls, just simple key lookup (for convenience, you can
still use keyword lookup for columns, but you can also call <code>get</code> with either a
string or a keyword).</p><p>So how does this work? <code>reducible-query</code> produces a <code>clojure.lang.IReduce</code> which,
when reduced with a function <code>f</code>, performs the query and reduces the <code>ResultSet</code>
using <code>f</code>, opening and closing the connection and/or transaction during the
reduction. For example:</p><pre><code class="clojure">;; our reducing function requires two arguments: we must provide initial val
(reduce (fn [total {:keys [cost]}] (+ total cost))
0
(jdbc/reducible-query db-spec
["SELECT * FROM fruit WHERE cost &lt; ?" 50]
{:raw? true}))
;; separating the key selection from the reducing function: we can omit val
(transduce (map :cost)
+ ; can be called with 0, 1, or 2 arguments!
(jdbc/reducible-query db-spec
["SELECT * FROM fruit WHERE cost &lt; ?" 50]
{:raw? true}))
;; 437
</code></pre><p>Since <code>reducible-query</code> doesn't actually run the query until you reduce its result,
you can create it once and run it as many times as you want. This will avoid the
overhead of option and parameter validation and handling for repeated reductions,
since those are performed just once in the call to <code>reducible-query</code>. Note that
the SQL parameters are fixed by that call, so this only works for running the
<em>identical</em> query multiple times.</p><p>A reducible companion to <code>result-set-seq</code> also exists, in case you already have
a Java <code>ResultSet</code> and want to create a <code>clojure.lang.IReduce</code>. <code>reducible-result-set</code>
accept almost the same options as <code>result-set-seq</code>: <code>identifiers</code>, <code>keywordize?</code>,
<code>qualifier</code>, and <code>read-columns</code>. It does not accept <code>as-arrays?</code> (for the same
reason that <code>reducible-query</code> does not). Unlike <code>result-set-seq</code>, which produces
a lazy sequence that can be consumed multiple times (with the first pass realizing
it for subsequent passes), <code>reducible-result-set</code> is reducible just once: the
underlying <code>ResultSet</code> is mutable and is consumed during the first reduction!</p><p>It should go without saying that both <code>reducible-query</code> and
<code>reducible-result-set</code> respect <code>reduced</code> / <code>reduced?</code>.</p><p><strong>Additional Options?</strong></p><p>Note: some databases require additional options to be passed in to ensure that
result sets are chunked and lazy. In particular, you may need to pass
<code>:auto-commit?</code>, set appropriately, as an option to whichever function will open your database
connection (<code>with-db-connection</code>, <code>with-db-transaction</code>, or the <code>query</code> / <code>reducible-query</code> itself
if you are passing a bare database spec and expecting <code>query</code> / <code>reducible-query</code> to open and close
the connection directly). You may also need to specify <code>:fetch-size</code>, <code>:result-type</code>,
and possibly other options -- consult your database's documentation for the JDBC
driver you are using.</p><h3 id="processing-each-row-lazily">Processing each row lazily</h3><p>As seen above, using <code>reduce</code>, <code>transduce</code>, etc with a <code>reducible-query</code> allow
you to easily and efficiently process each row as you process the entire
result set, but sometimes you just want a sequence of transformed rows.</p><p>We can process each row with the <code>:row-fn</code> option. Again, like with <code>:result-set-fn</code>,
we pass a function but this time it will be
invoked on each row, as the result set is realized.</p><pre><code class="clojure">(jdbc/query db-spec ["SELECT name FROM fruit WHERE cost &lt; ?" 50]
{:row-fn :name})
;; ("Apple" "Banana" ...)
</code></pre><p>The result is still a fully realized sequence, but each row has been
transformed by the <code>:name</code> function you passed in.</p><p>You can combine this with <code>:result-set-fn</code> to simplify processing of result
sets:</p><pre><code class="clojure">(jdbc/query db-spec ["SELECT * FROM fruit WHERE cost &lt; ?" 50]
{:row-fn :cost
:result-set-fn (partial reduce +)})
;; produces the total cost of all the cheap fruits
</code></pre><p>or:</p><pre><code class="clojure">(jdbc/query db-spec ["SELECT SUM(cost) AS total FROM fruit WHERE cost &lt; ?" 50]
{:row-fn :total
:result-set-fn first})
;; produces the same result, via SQL
</code></pre><p>Here is an example that manipulates rows to add computed columns:</p><pre><code class="clojure">(defn add-tax [row] (assoc row :tax (* 0.08 (:cost row))))
(jdbc/query db-spec ["SELECT * FROM fruit"]
{:row-fn add-tax})
;; produces all the rows with a new :tax column added
</code></pre><p>All of the above can be achieved via <code>reducible-query</code> and the appropriate
reducing function and/or transducer, but with those simple row/result set
functions, the result is often longer / uglier:</p><pre><code class="clojure">(into [] (map :name) (jdbc/reducible-query db-spec ["SELECT name FROM fruit WHERE cost &lt; ?" 50]))
(transduce (map :cost) + (jdbc/reducible-query db-spec ["SELECT * FROM fruit WHERE cost &lt; ?" 50]))
;; :row-fn :total :result-set-fn first left as an exercise for the reader!
(into [] (map add-tax) (jdbc/reducible-query db-spec ["SELECT * FROM fruit"]))
</code></pre><p>If the result set is likely to be large and the reduction can use a <code>:raw? true</code>
result set, <code>reducible-query</code> may be worth the verbosity for the performance gain.</p><h2 id="inserting-data">Inserting data</h2><p>Rows (and partial rows) can be inserted easily using the <code>insert!</code> function.
You can insert a single row, or multiple rows. Depending on how you call
<code>insert!</code>, the insertion will be done either through multiple SQL statements or
through a single, batched SQL statement. That will also determine whether or
not you get back any generated keys.</p><p>If you need a more complex form of insertion, you can use <code>execute!</code> and, if
your database / driver supports it, you can pass <code>:return-keys</code> as an option
to get back the generated keys. As of <code>java.jdbc</code> 0.7.6, this can be a vector
of column names to return (for drivers that support that) or a simple Boolean.</p><h3 id="inserting-a-row">Inserting a row</h3><p>If you want to insert a single row (or partial row) and get back the generated
keys, you can use <code>insert!</code> and specify the columns and their values as a map.
This performs a single insert statement. A single-element sequence containing a
map of the generated keys will be returned.</p><pre><code class="clojure">(jdbc/insert! db-spec :fruit {:name "Pear" :appearance "green" :cost 99})
;; returns a database-specific map as the only element of a sequence, e.g.,
;; ({:generated_key 50}) might be returned for MySQL
</code></pre><p>Not all databases are able to return generated keys from an insert.</p><h3 id="inserting-multiple-rows">Inserting multiple rows</h3><p>There are two ways to insert multiple rows: as a sequence of maps, or as a
sequence of vectors. In the former case, multiple inserts will be performed and
a map of the generated keys will be returned for each insert (as a sequence).
In the latter case, a single, batched insert will be performed and a sequence
of row insert counts will be returned (generally a sequence of ones). The latter
approach is likely to be substantially faster if you are inserting a large number
of rows.</p><p>If you use <code>insert-multi!</code> and specify each row as a map of columns and their values,
then you can specify a mixture of complete and partial rows, and you will get
back the generated keys for each row (assuming the database has that
capability).</p><pre><code class="clojure">(jdbc/insert-multi! db-spec :fruit
[{:name "Pomegranate" :appearance "fresh" :cost 585}
{:name "Kiwifruit" :grade 93}])
;; returns a sequence of database-specific maps, e.g., for MySQL:
;; ({generated_key 51} {generated_key 52})
</code></pre><p>If you use <code>insert-multi!</code> and specify the columns you wish to insert followed by
each row as a vector of column values, then you must specify the same columns
in each row, and you will not get generated keys back, just row counts. If you
wish to insert complete rows, you may omit the column name vector (passing
<code>nil</code> instead) but your rows must match the natural order of columns in your
table so be careful!</p><pre><code class="clojure">(jdbc/insert-multi! db-spec :fruit
nil ; column names not supplied
[[1 "Apple" "red" 59 87]
[2 "Banana" "yellow" 29 92.2]
[3 "Peach" "fuzzy" 139 90.0]
[4 "Orange" "juicy" 89 88.6]])
;; (1 1 1 1) - row counts modified
</code></pre><p>It is generally safer to specify the columns you wish to insert so you can
control the order, and choose to omit certain columns:</p><pre><code class="clojure">(jdbc/insert-multi! db-spec :fruit
[:name :cost]
[["Mango" 722]
["Feijoa" 441]])
;; (1 1) - row counts modified
</code></pre><h2 id="updating-rows">Updating rows</h2><p>If you want to update simple column values in one or more rows based on a
simple SQL predicate, you can use <code>update!</code> with a map, representing the column
values to set, and a SQL predicate with parameters. If you need a more complex
form of update, you can use the <code>execute!</code> function with arbitrary SQL (and
parameters).</p><pre><code class="clojure">;; update fruit set cost = 49 where grade &lt; ?
(jdbc/update! db-spec :fruit
{:cost 49}
["grade &lt; ?" 75])
;; produces a sequence of the number of rows updated, e.g., (2)
</code></pre><p>For a more complex update:</p><pre><code class="clojure">(jdbc/execute! db-spec
["update fruit set cost = ( 2 * grade ) where grade &gt; ?" 50.0])
;; produces a sequence of the number of rows updated, e.g., (3)
</code></pre><h2 id="deleting-rows">Deleting rows</h2><p>If you want to delete any rows from a table that match a simple predicate, the
<code>delete!</code> function can be used.</p><pre><code class="clojure">(jdbc/delete! db-spec :fruit ["grade &lt; ?" 25.0])
;; produces a sequence of the number of rows deleted, e.g., (1)
</code></pre><p>You can also use <code>execute!</code> for deleting rows:</p><pre><code class="clojure">(jdbc/execute! db-spec ["DELETE FROM fruit WHERE grade &lt; ?" 25.0])
;; produces a sequence of the number of rows deleted, e.g., (1)
</code></pre><h2 id="using-transactions">Using transactions</h2><p>You can write multiple operations in a transaction to ensure they are either
all performed, or all rolled back.</p><pre><code class="clojure">(jdbc/with-db-transaction [t-con db-spec]
(jdbc/update! t-con :fruit
{:cost 49}
["grade &lt; ?" 75])
(jdbc/execute! t-con
["update fruit set cost = ( 2 * grade ) where grade &gt; ?" 50.0]))
</code></pre><p>The <code>with-db-transaction</code> macro creates a transaction-aware connection from the
database specification and that should be used in the body of the transaction
code.</p><p>You can specify the transaction isolation level as part of the
<code>with-db-transction</code> binding:</p><pre><code class="clojure">(jdbc/with-db-transaction [t-con db-spec {:isolation :serializable}]
...)
</code></pre><p>Possible values for <code>:isolation</code> are <code>:none</code>, <code>:read-committed</code>,
<code>:read-uncommitted</code>, <code>:repeatable-read</code>, and <code>:serializable</code>. Be aware that not
all databases support all isolation levels. Inside a transaction, you can call
<code>get-isolation-level</code> to return the current level.</p><p>In addition, you can also set the current transaction-aware connection to
rollback, and reset that setting, as well as test whether the connection is
currently set to rollback, using the following functions:</p><pre><code class="clojure">(jdbc/db-set-rollback-only! t-con) ; this transaction will rollback instead of commit
(jdbc/db-unset-rollback-only! t-con) ; this transaction will commit if successful
(jdbc/db-is-rollback-only t-con) ; returns true if transaction is set to rollback
</code></pre><h2 id="updating-or-inserting-rows-conditionally">Updating or Inserting rows conditionally</h2><p><code>java.jdbc</code> does not provide a built-in function for updating existing rows or
inserting a new row (the older API supported this but the logic was too
simplistic to be generally useful). If you need that functionality, it can
sometimes be done like this:</p><pre><code class="clojure">(defn update-or-insert!
"Updates columns or inserts a new row in the specified table"
[db table row where-clause]
(jdbc/with-db-transaction [t-con db]
(let [result (jdbc/update! t-con table row where-clause)]
(if (zero? (first result))
(jdbc/insert! t-con table row)
result))))
(update-or-insert! mysql-db :fruit
{:name "Cactus" :appearance "Spiky" :cost 2000}
["name = ?" "Cactus"])
;; inserts Cactus (assuming none exists)
(update-or-insert! mysql-db :fruit
{:name "Cactus" :appearance "Spiky" :cost 2500}
["name = ?" "Cactus"])
;; updates the Cactus we just inserted
</code></pre><p>If the <code>where-clause</code> does not uniquely identify a single row, this will update
multiple rows which might not be what you want, so be careful!</p><h2 id="exception-handling-and-transaction-rollback">Exception Handling and Transaction Rollback</h2><p>Transactions are rolled back if an exception is thrown, as shown in these
examples.</p><pre><code class="clojure">(jdbc/with-db-transaction [t-con db-spec]
(jdbc/insert-multi! t-con :fruit
[:name :appearance]
[["Grape" "yummy"]
["Pear" "bruised"]])
;; At this point the insert! call is complete, but the transaction is
;; not. The exception will cause it to roll back leaving the database
;; untouched.
(throw (Exception. "sql/test exception")))
</code></pre><p>As noted above, transactions can also be set explicitly to rollback instead of
commit:</p><pre><code class="clojure">(jdbc/with-db-transaction [t-con db-spec]
(prn "is-rollback-only" (jdbc/db-is-rollback-only t-con))
;; is-rollback-only false
(jdbc/db-set-rollback-only! t-con)
;; the following insert will be rolled back when the transaction ends:
(jdbc/insert!-multi t-con :fruit
[:name :appearance]
[["Grape" "yummy"]
["Pear" "bruised"]])
(prn "is-rollback-only" (jdbc/db-is-rollback-only t-con))
;; is-rollback-only true
;; the following will display the inserted rows:
(jdbc/query t-con ["SELECT * FROM fruit"]
:row-fn println))
(prn)
;; outside the transaction, the following will show the original rows
;; without those two inserted inside the (rolled-back) transaction:
(jdbc/query db-spec ["SELECT * FROM fruit"]
:row-fn println)
</code></pre><h2 id="clojure-identifiers-and-sql-entities">Clojure identifiers and SQL entities</h2><p>As hinted at above, <code>java.jdbc</code> converts SQL entity names in result sets to
keywords in Clojure by making them lowercase, and converts strings and keywords
that specify table and column names (in maps) to SQL entities <em>as-is</em> by
default.</p><p>You can override this behavior by specifying an options map, containing
<code>:identifiers</code>, <code>:keywordize?</code> and/or <code>:qualifier</code>, on any function that
returns or transforms a result set,
and <code>:entities</code>, on any function that transforms Clojure data into SQL.</p><ul><li><code>:identifiers</code> is for converting <code>ResultSet</code> column names to keywords (or strings). It
defaults to <code>clojure.string/lower-case</code>.</li><li><code>:keywordize?</code> controls whether to convert identifiers to keywords (the default) or not.</li><li><code>:qualifier</code> optionally specifies a namespace to qualify keywords with (when
<code>:keywordize?</code> is not falsey).</li><li><code>:entities</code> is for converting Clojure keywords/string to SQL entity names. It
defaults to <code>identity</code> (after calling <code>name</code> on the keyword or string).</li></ul><p>If you want to prevent <code>java.jdbc</code>'s conversion of SQL entity names to lowercase
in a <code>query</code> result, you can specify <code>:identifiers identity</code>:</p><pre><code class="clojure">(jdbc/query db-spec ["SELECT * FROM mixedTable"]
{:identifiers identity})
;; produces result set with column names exactly as they appear in the DB
</code></pre><p>It you're working with a database that has underscores in column names, you
might want to specify a function that converts those to dashes in Clojure
keywords:</p><pre><code class="clojure">(jdbc/query db-spec ["SELECT * FROM mixedTable"]
{:identifiers #(.replace % \_ \-)})
</code></pre><p>For several databases, you will often want entities to be quoted in some way
(sometimes referred to as "stropping"). A utility function <code>quoted</code> is provided
that accepts either a single character, a vector pair of characters, or a keyword
as a symbolic name for the type of quoting you want (<code>:ansi</code>, <code>:mysql</code>,
<code>:oracle</code>, <code>:sqlserver</code>), and
returns a function suitable for use with the <code>:entities</code> option.</p><p>For example:</p><pre><code class="clojure">(jdbc/insert! db-spec :fruit
{:name "Apple" :appearance "Round" :cost 99}
{:entities (jdbc/quoted \`)}) ; or (jdbc/quoted :mysql)
</code></pre><p>will execute:</p><pre><code class="clojure">INSERT INTO `fruit` ( `name`, `appearance`, `cost` )
VALUES ( ?, ?, ? )
</code></pre><p>with the parameters <code>"Apple", "Round", "99"</code> whereas:</p><pre><code class="clojure">(jdbc/insert! db-spec :fruit
{:name "Apple" :appearance "Round" :cost 99}
{:entities (jdbc/quoted [\[ \]])}) ; or (jdbc/quoted :sqlserver)
</code></pre><p>will execute:</p><pre><code class="clojure">INSERT INTO [fruit] ( [name], [appearance], [cost] )
VALUES ( ?, ?, ? )
</code></pre><p>with the parameters <code>"Apple", "Round", "99"</code>.</p><h2 id="protocol-extensions-for-transforming-values">Protocol extensions for transforming values</h2><p>By default, <code>java.jdbc</code> leaves it up to Java interop and the JDBC driver library
to perform the appropriate transformations of Clojure values to SQL values and
vice versa. When Clojure values are passed through to the JDBC driver,
<code>java.jdbc</code> uses <code>PreparedStatement/setObject</code> for all values by default. When
Clojure values are read from a <code>ResultSet</code> they are left untransformed, except
that <code>Boolean</code> values are coerced to canonical <code>true</code> / <code>false</code> values in
Clojure (some driver / data type combinations can produce <code>(Boolean. false)</code>
values otherwise, which do not behave like <code>false</code> in all situations).</p><p><code>java.jdbc</code> provides three protocols that you can extend, in order to modify
these behaviors.</p><ul><li><code>ISQLValue</code> / <code>sql-value</code> - simple transformations of Clojure values to SQL
values</li><li><code>ISQLParameter</code> / <code>set-parameter</code> - a more sophisticated transformation of
Clojure values to SQL values that lets you override how the value is stored
in the <code>PreparedStatement</code></li><li><code>IResultSetReadColumn</code> / <code>result-set-read-column</code> - simple transformations of
SQL values to Clojure values when processing a <code>ResultSet</code> object</li></ul><p>If you are using a database that returns certain SQL types as custom Java types
(e.g., PostgreSQL), you can extend <code>IResultSetReadColumn</code> to that type and
define <code>result-set-read-column</code> to perform the necessary conversion to a usable
Clojure data structure. The <code>result-set-read-column</code> function is called with
three arguments:</p><ul><li>The SQL value itself</li><li>The <code>ResultSet</code> metadata object</li><li>The index of the column in the row / metadata</li></ul><p>By default <code>result-set-read-column</code> just returns its first argument (the
<code>Boolean</code> implementation ensure the result is either <code>true</code> or <code>false</code>).</p><p>If you are using a database that requires special treatment of null values,
e.g., TeraData, you can extend <code>ISQLParameter</code> to <code>nil</code> (and <code>Object</code>) and
define <code>set-parameter</code> to use <code>.setNull</code> instead of <code>.setObject</code>. The
<code>set-parameter</code> function is called with three arguments:</p><ul><li>The Clojure value itself</li><li>The <code>PreparedStatement</code> object</li><li>The index of the parameter being set</li></ul><p>By default <code>set-parameter</code> calls <code>sql-value</code> on the Clojure value and then
calls <code>.setObject</code> to store the result of that call into the specified
parameter in the SQL statement.</p><p>For general transformations of Clojure values to SQL values, extending
<code>ISQLValue</code> and defining <code>sql-value</code> may be sufficient. The <code>sql-value</code>
function is called with a single argument: the Clojure value. By default
<code>sql-value</code> just returns its argument.</p>
<div id="prev-next">
<a href="home.html">&laquo; java.jdbc - Getting Started</a>
||
<a href="using_ddl.html">java.jdbc - Using DDL and Metadata &raquo;</a>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div id="sidebar">
<h3>Links</h3>
<ul id="links">
<li><a href="../../about/index.html">About</a></li>
<li><a href="../../content/index.html">Table of Contents</a></li>
<li><a href="../../tutorials/getting_started/index.html">Getting Started with Clojure</a></li>
<li><a href="../../tutorials/introduction/index.html">Introduction to Clojure</a></li>
<li><a href="../../tutorials/emacs/index.html">Clojure with Emacs</a></li>
<li><a href="../../tutorials/vim_fireplace/index.html">Clojure with Vim and fireplace.vim</a></li>
<li><a href="../../tutorials/eclipse/index.html">Starting with Eclipse and Counterclockwise For Clojure Development</a></li>
<li><a href="../../tutorials/basic_web_development/index.html">Basic Web Development</a></li>
<li><a href="../../tutorials/parsing_xml_with_zippers/index.html">Parsing XML in Clojure</a></li>
<li><a href="../../tutorials/growing_a_dsl_with_clojure/index.html">Growing a DSL with Clojure</a></li>
<li><a href="../../language/core_overview/index.html">Overview of clojure.core, the standard Clojure library</a></li>
<li><a href="../../language/namespaces/index.html">Clojure Namespaces and Vars</a></li>
<li><a href="../../language/collections_and_sequences/index.html">Collections and Sequences in Clojure</a></li>
<li><a href="../../language/functions/index.html">Functions in Clojure</a></li>
<li><a href="../../language/laziness/index.html">Laziness in Clojure</a></li>
<li><a href="../../language/interop/index.html">Clojure interoperability with Java</a></li>
<li><a href="../../language/macros/index.html">Clojure Macros and Metaprogramming</a></li>
<li><a href="../../language/polymorphism/index.html">Polymorphism in Clojure: Protocols and Multimethods</a></li>
<li><a href="../../language/concurrency_and_parallelism/index.html">Concurrency and Parallelism in Clojure</a></li>
<li><a href="../../language/glossary/index.html">Clojure Terminology Guide</a></li>
<li><a href="../libraries_directory/index.html">A Directory of Clojure Libraries</a></li>
<li><a href="../libraries_authoring/index.html">Library Development and Distribution</a></li>
<li><a href="../generating_documentation/index.html">Generating Documentation</a></li>
<li><a href="../data_processing/index.html">Data Processing (Help Wanted)</a></li>
<li><a href="../web_development/index.html">Web Development (Overview)</a></li>
<li><a href="../maven/index.html">How to use Maven to build Clojure projects</a></li>
<li><a href="../community/index.html">Clojure Community</a></li>
<li><a href="../user_groups/index.html">Clojure User Groups</a></li>
<li><a href="../running_cljug/index.html">Running a Clojure User Group</a></li>
<li><a href="../books/index.html">Books about Clojure and ClojureScript</a></li>
<li><a href="../../cookbooks/data_structures/index.html">Data Structures (Help wanted)</a></li>
<li><a href="../../cookbooks/strings/index.html">Strings</a></li>
<li><a href="../../cookbooks/math/index.html">Mathematics with Clojure</a></li>
<li><a href="../../cookbooks/date_and_time/index.html">Date and Time (Help wanted)</a></li>
<li><a href="../../cookbooks/files_and_directories/index.html">Working with Files and Directories in Clojure</a></li>
<li><a href="../../cookbooks/middleware/index.html">Middleware in Clojure</a></li>
<li><a href="home.html">java.jdbc - Getting Started</a></li>
<li><a href="using_sql.html">java.jdbc - Manipulating data with SQL</a></li>
<li><a href="using_ddl.html">java.jdbc - Using DDL and Metadata</a></li>
<li><a href="reusing_connections.html">java.jdbc - How to reuse database connections</a></li>
<li><a href="../core_typed/home/index.html">core.typed - User Documentation Home</a></li>
<li><a href="../core_typed/user_documentation/index.html">core.typed - User Documentation</a></li>
<li><a href="../core_typed/rationale/index.html">core.typed - Rationale</a></li>
<li><a href="../core_typed/quick_guide.html">core.typed - Quick Guide</a></li>
<li><a href="../core_typed/start/introduction_and_motivation/index.html">core.typed - Getting Started: Introduction and Motivation</a></li>
<li><a href="../core_typed/types/index.html">core.typed - Types</a></li>
<li><a href="../core_typed/start/annotations/index.html">core.typed - Annotations</a></li>
<li><a href="../core_typed/poly_fn/index.html">core.typed - Polymorphic Functions</a></li>
<li><a href="../core_typed/filters/index.html">core.typed - Filters</a></li>
<li><a href="../core_typed/mm_protocol_datatypes/index.html">core.typed - Protocols</a></li>
<li><a href="../core_typed/loops/index.html">core.typed - Looping constructs</a></li>
<li><a href="../core_typed/function_types/index.html">core.typed - Functions</a></li>
<li><a href="../core_typed/limitations/index.html">core.typed - Limitations</a></li>
</ul>
</div>
</div>
</div>
<footer>Copyright &copy; 2021 Multiple Authors
<p style="text-align: center;">Powered by <a href="http://cryogenweb.org">Cryogen</a></p></footer>
</div>
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
<script src="../../../js/highlight.pack.js" type="application/javascript"></script>
<script>hljs.initHighlightingOnLoad();</script>
</body>
</html>