510 lines
36 KiB
HTML
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 < ?" 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 < ?" 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 < ?" 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 < ?" 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 < ?" 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 < ?" 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 < ?" 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 < ?" 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 < ?" 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 < ?" 50]))
|
|
(transduce (map :cost) + (jdbc/reducible-query db-spec ["SELECT * FROM fruit WHERE cost < ?" 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 < ?
|
|
(jdbc/update! db-spec :fruit
|
|
{:cost 49}
|
|
["grade < ?" 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 > ?" 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 < ?" 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 < ?" 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 < ?" 75])
|
|
(jdbc/execute! t-con
|
|
["update fruit set cost = ( 2 * grade ) where grade > ?" 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">« java.jdbc - Getting Started</a>
|
|
|
|
|
|
||
|
|
|
|
|
|
<a href="using_ddl.html">java.jdbc - Using DDL and Metadata »</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 © 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>
|