@@ -5,72 +5,82 @@ by Tom Hicks and Filippo Diotalevi
5
5
6
6
==== Problem
7
7
8
- You would like to connect to an SQL database efficiently using a connection pool.
8
+ You would like to connect to an SQL database efficiently using a
9
+ connection pool.
9
10
10
11
==== Solution
11
12
12
- Use the + BoneCP+ connection and statement pooling library to wrap your JDBC-based
13
- drivers, creating a pooled data source. The pooled data source is then usable
14
- by the +clojure.java.jdbc+ library, as described in the
15
- <<sec_db_connecting_to_a_sql_database>> recipe.
13
+ Use the BoneCP connection and statement pooling library to wrap your
14
+ JDBC-based drivers, creating a pooled data source. The pooled data
15
+ source is then usable by the +clojure.java.jdbc+ library, as described
16
+ in the <<sec_db_connecting_to_a_sql_database>> recipe.
16
17
17
- Add a dependency to your Leiningen project file for the +BoneCP+ library. You
18
- will also need to add dependencies for the +clojure.java.jdbc+ library and the
19
- JDBC library used with the RDBMS you are connecting to (e.g. MySQL):
18
+ [NOTE]
19
+ ====
20
+ You'll need a running SQL database and existing table to connect to
21
+ for this recipe. We suggest PostgreSQL.
20
22
21
- [source,clojure]
23
+ If you're a Mac user and don't have PostgreSQL installed yet, you can
24
+ go here http://postgresapp.com/ for an easy to use DMG.
25
+
26
+ After you have PostgreSQL running (presumably on _localhost:5432_) run
27
+ the following command to create a database for this recipe:
28
+
29
+ [source,bash]
22
30
----
23
- :dependencies [ ; ...
24
- [org.clojure/java.jdbc "0.3.0-alpha4"]
25
- [mysql/mysql-connector-java "5.1.25"]
26
- [com.jolbox/bonecp "0.7.1.RELEASE"]
31
+ $ /Applications/Postgres.app/Contents/MacOS/bin/createdb cookbook_experiments
27
32
----
33
+ ====
28
34
29
- In your +project.clj+ file, and import the +BoneCP+ classes you intend to use:
35
+ Before starting, add the BoneCP dependency (`[com.jolbox/bonecp
36
+ "0.8.0.RELEASE"]`), as well as the appropriate JDBC libraries for your
37
+ RDBMS, to your project's dependencies. You'll also need a valid SLF4J
38
+ logger. Alternatively, you can follow along in a REPL using lein-try:
30
39
31
- [source,clojure ]
40
+ [source,shell ]
32
41
----
33
- (ns myproj.core
34
- (:require [clojure. java.jdbc :as jdbc]
35
- [clojure.java.jdbc.sql :as sql])
36
- (:import com.jolbox.bonecp.BoneCPDataSource))
42
+ $ lein try com.jolbox/bonecp "0.8.0.RELEASE" \
43
+ org.clojure/ java.jdbc "0.3.0-beta1" \
44
+ postgresql/postgresql "8.4-702.jdbc4" \
45
+ org.slf4j/slf4j-nop # Just don't log anything...
37
46
----
38
47
39
-
40
- Create a database specification containing the parameters for accessing the
41
- database:
48
+ First, create a database specification containing the parameters for
49
+ accessing the database. This includes keys for the initial and maximum
50
+ pool sizes, as well as the number of partitions.
42
51
43
52
[source,clojure]
44
53
----
45
- (def db-spec {:classname "com.mysql.jdbc.Driver"
46
- :subprotocol "mysql"
47
- :subname "//localhost:3306/lotr_db"
48
- :user "bilbo"
49
- :password "secret"
54
+ (def db-spec {:classname "org.postgresql.Driver"
55
+ :subprotocol "postgresql"
56
+ :subname "//localhost:5432/cookbook_experiments"
50
57
:init-pool-size 4
51
58
:max-pool-size 20
52
59
:partitions 2})
53
60
----
54
61
55
- Define a function (for convenience) which uses the parameters in the database
56
- specification map to create a pooled data source.
62
+ To create a pooled +BoneCPDataSource+ object define a function (for
63
+ convenience) which uses the parameters in the database
64
+ specification map.
57
65
58
66
[source,clojure]
59
67
----
68
+ (import 'com.jolbox.bonecp.BoneCPDataSource)
69
+
60
70
(defn pooled-datasource [db-spec]
61
- (let [ {:keys [classname subprotocol subname user password
62
- init-pool-size max-pool-size idle-time partitions]} db-spec
71
+ (let [{:keys [classname subprotocol subname user password
72
+ init-pool-size max-pool-size idle-time partitions]} db-spec
63
73
cpds (doto (BoneCPDataSource.)
64
- (.setDriverClass classname)
65
- (.setJdbcUrl (str "jdbc:" subprotocol ":" subname))
66
- (.setUsername user)
67
- (.setPassword password)
68
- (.setMinConnectionsPerPartition (inc (int (/ init-pool-size partitions))))
69
- (.setMaxConnectionsPerPartition (inc (int (/ max-pool partitions))))
70
- (.setPartitionCount partitions)
71
- (.setStatisticsEnabled true)
72
- (.setIdleMaxAgeInMinutes (or idle-time 60))
73
- {:datasource cpds} ))
74
+ (.setDriverClass classname)
75
+ (.setJdbcUrl (str "jdbc:" subprotocol ":" subname))
76
+ (.setUsername user)
77
+ (.setPassword password)
78
+ (.setMinConnectionsPerPartition (inc (int (/ init-pool-size partitions))))
79
+ (.setMaxConnectionsPerPartition (inc (int (/ max-pool-size partitions))))
80
+ (.setPartitionCount partitions)
81
+ (.setStatisticsEnabled true)
82
+ (.setIdleMaxAgeInMinutes (or idle-time 60)))]
83
+ {:datasource cpds}))
74
84
----
75
85
76
86
Use the convenience function to define a pooled data source for connecting to
@@ -79,27 +89,43 @@ your database:
79
89
[source,clojure]
80
90
----
81
91
(def pooled-db-spec (pooled-datasource db-spec))
92
+
93
+ pooled-db-spec
94
+ ;; -> {:datasource #<BoneCPDataSource ...>}
82
95
----
83
96
84
- Pass the database specification as the first argument to several of the
85
- library's other functions which query and manipulate your database.
97
+ Pass the database specification as the first argument to any
98
+ +clojure.java.jdbc+ functions that query or manipulate your database.
86
99
87
100
[source,clojure]
88
101
----
89
- (jdbc/insert! pooled-db-spec :players
90
- {:name "Bilbo" :type "hobbit"} {:name "Galadriel" :type "elf"}
91
- {:name "Frodo" :type "hobbit"} {:name "Ori" :type "dwarf"})
92
- ;; -> (nil nil nil nil)
93
-
94
- (jdbc/query pooled-db-spec (sql/select * :players (sql/where {:type "hobbit"})))
95
- ;; -> ({:type "hobbit", :name "Bilbo"} {:type "hobbit", :name "Frodo"})
102
+ (require '[clojure.java.jdbc :as jdbc]
103
+ '[clojure.java.jdbc.ddl :as ddl]
104
+ '[clojure.java.jdbc.sql :as sql])
105
+
106
+ (jdbc/db-do-commands pooled-db-spec false
107
+ (ddl/create-table
108
+ :blog_posts
109
+ [:id :serial "PRIMARY KEY"]
110
+ [:title "varchar(255)" "NOT NULL"]
111
+ [:body :text]))
112
+ ;; -> (0)
113
+
114
+ (jdbc/insert! pooled-db-spec
115
+ :blog_posts
116
+ {:title "My first post!" :body "This is going to be good!"})
117
+ ;; -> ({:body "This is going to be good!", :title "My first post!", :id 1})
118
+
119
+ (jdbc/query pooled-db-spec
120
+ (sql/select * :blog_posts (sql/where{:title "My first post!"})))
121
+ ;; -> ({:body "This is going to be good!", :title "My first post!", :id 1})
96
122
----
97
123
98
124
==== Discussion
99
125
100
126
As shown above, the +clojure.java.jdbc+ library can create database
101
127
connections from JDBC data sources, which allows connections to be easily
102
- pooled by the +BoneCP+ or other pooling library .
128
+ pooled by the +BoneCP+ or other pooling libraries .
103
129
104
130
The +BoneCP+ library wraps existing JDBC classes to allow the creation of
105
131
efficient data sources. It can adapt traditional unpooled drivers and
@@ -124,7 +150,7 @@ in an SQL exception.
124
150
125
151
[source,clojure]
126
152
----
127
- (.close pooled-db-spec)
153
+ (.close (:datasource pooled-db-spec) )
128
154
;; -> nil
129
155
----
130
156
0 commit comments