jdbc-helper 0.5.1 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +251 -0
- data/lib/jdbc-helper.rb +1 -10
- data/lib/jdbc-helper/connection.rb +347 -351
- data/lib/jdbc-helper/connection/callable_statement.rb +56 -56
- data/lib/jdbc-helper/connection/parameterized_statement.rb +57 -46
- data/lib/jdbc-helper/connection/prepared_statement.rb +88 -88
- data/lib/jdbc-helper/connection/result_set_enumerator.rb +102 -91
- data/lib/jdbc-helper/connection/row.rb +106 -115
- data/lib/jdbc-helper/connection/statement_pool.rb +44 -44
- data/lib/jdbc-helper/connection/type_map.rb +41 -66
- data/lib/jdbc-helper/connector.rb +10 -10
- data/lib/jdbc-helper/connector/mysql_connector.rb +24 -24
- data/lib/jdbc-helper/connector/oracle_connector.rb +33 -32
- data/lib/jdbc-helper/constants.rb +20 -17
- data/lib/jdbc-helper/sql.rb +168 -175
- data/lib/jdbc-helper/wrapper/function_wrapper.rb +12 -12
- data/lib/jdbc-helper/wrapper/object_wrapper.rb +17 -17
- data/lib/jdbc-helper/wrapper/procedure_wrapper.rb +120 -123
- data/lib/jdbc-helper/wrapper/sequence_wrapper.rb +43 -43
- data/lib/jdbc-helper/wrapper/table_wrapper.rb +172 -169
- data/test/helper.rb +2 -10
- data/test/performance.rb +141 -0
- data/test/test_connection.rb +443 -389
- data/test/test_connectors.rb +47 -47
- data/test/test_object_wrapper.rb +541 -432
- data/test/test_sql.rb +143 -135
- metadata +119 -123
- data/README.rdoc +0 -223
- data/test/test_performance.rb +0 -138
data/README.markdown
ADDED
@@ -0,0 +1,251 @@
|
|
1
|
+
```
|
2
|
+
_ _ _ _ _
|
3
|
+
(_) | | | | | | |
|
4
|
+
_ __| | |__ ___ ______| |__ ___| |_ __ ___ _ __
|
5
|
+
| |/ _` | '_ \ / __|______| '_ \ / _ \ | '_ \ / _ \ '__|
|
6
|
+
| | (_| | |_) | (__ | | | | __/ | |_) | __/ |
|
7
|
+
| |\__,_|_.__/ \___| |_| |_|\___|_| .__/ \___|_|
|
8
|
+
_/ | | |
|
9
|
+
|__/ |_|
|
10
|
+
```
|
11
|
+
|
12
|
+
# jdbc-helper
|
13
|
+
|
14
|
+
A JDBC helper for Ruby/Database developers.
|
15
|
+
JDBCHelper::Connection object wraps around a JDBC connection and provides much nicer interface to
|
16
|
+
crucial database operations from primitive selects and updates to more complex ones involving
|
17
|
+
batch updates, prepared statements and transactions.
|
18
|
+
As the name implies, this gem only works on JRuby.
|
19
|
+
|
20
|
+
## Installation
|
21
|
+
### Install gem
|
22
|
+
```
|
23
|
+
gem install jdbc-helper
|
24
|
+
```
|
25
|
+
|
26
|
+
### Setting up CLASSPATH
|
27
|
+
Add the appropriate JDBC drivers to the CLASSPATH.
|
28
|
+
|
29
|
+
```
|
30
|
+
export CLASSPATH=$CLASSPATH:~/lib/mysql-connector-java-5.1.16-bin.jar:~/lib/ojdbc6.jar
|
31
|
+
```
|
32
|
+
|
33
|
+
### In Ruby
|
34
|
+
```ruby
|
35
|
+
require 'jdbc-helper'
|
36
|
+
```
|
37
|
+
|
38
|
+
## Examples
|
39
|
+
### Connecting to a database
|
40
|
+
```ruby
|
41
|
+
# :driver and :url must be given
|
42
|
+
conn = JDBCHelper::Connection.new(
|
43
|
+
:driver => 'com.mysql.jdbc.Driver',
|
44
|
+
:url => 'jdbc:mysql://localhost/test')
|
45
|
+
conn.close
|
46
|
+
|
47
|
+
|
48
|
+
# Optional :user and :password
|
49
|
+
conn = JDBCHelper::Connection.new(
|
50
|
+
:driver => 'com.mysql.jdbc.Driver',
|
51
|
+
:url => 'jdbc:mysql://localhost/test',
|
52
|
+
:user => 'mysql',
|
53
|
+
:password => '')
|
54
|
+
conn.close
|
55
|
+
|
56
|
+
|
57
|
+
# MySQL shortcut connector
|
58
|
+
conn = JDBCHelper::MySQLConnector.connect('localhost', 'mysql', '', 'test')
|
59
|
+
conn.close
|
60
|
+
|
61
|
+
# Oracle shortcut connector
|
62
|
+
conn = JDBCHelper::OracleConnector.connect(host, user, password, service_name)
|
63
|
+
conn.close
|
64
|
+
```
|
65
|
+
|
66
|
+
### Querying database table
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
conn.query("SELECT a, b, c FROM T") do |row|
|
70
|
+
row.labels
|
71
|
+
row.rownum
|
72
|
+
|
73
|
+
row.a, row.b, row.c # Dot-notation
|
74
|
+
row[0], row[1], row[2] # Numeric index
|
75
|
+
row['a'], row['b'], row['c'] # String index. Case-insensitive.
|
76
|
+
row[:a], row[:b], row[:c] # Symbol index. Case-insensitive.
|
77
|
+
|
78
|
+
row[0..-1] # Range index. Returns an array of values.
|
79
|
+
row[0, 3] # Offset and length. Returns an array of values.
|
80
|
+
end
|
81
|
+
|
82
|
+
# Returns an array of rows when block is not given
|
83
|
+
rows = conn.query("SELECT b FROM T")
|
84
|
+
uniq_rows = rows.uniq
|
85
|
+
|
86
|
+
# You can even nest queries
|
87
|
+
conn.query("SELECT a FROM T") do |row1|
|
88
|
+
conn.query("SELECT * FROM T_#{row1.a}") do |row2|
|
89
|
+
# ...
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# `enumerate' method returns an Enumerable object if block is not given.
|
94
|
+
# When the result set of the query is expected to be large and you wish to
|
95
|
+
# chain enumerators, `enumerate' is much preferred over `query'. (which returns the
|
96
|
+
# array of the entire rows)
|
97
|
+
conn.enumerate("SELECT * FROM LARGE_T").each_slice(1000) do |slice|
|
98
|
+
slice.each do | row |
|
99
|
+
# ...
|
100
|
+
end
|
101
|
+
end
|
102
|
+
```
|
103
|
+
|
104
|
+
### Updating database table
|
105
|
+
```ruby
|
106
|
+
del_count = conn.update("DELETE FROM T")
|
107
|
+
```
|
108
|
+
|
109
|
+
### Transaction
|
110
|
+
```ruby
|
111
|
+
committed = conn.transaction do |tx|
|
112
|
+
# ...
|
113
|
+
# Transaction logic here
|
114
|
+
# ...
|
115
|
+
|
116
|
+
if success
|
117
|
+
tx.commit
|
118
|
+
else
|
119
|
+
tx.rollback
|
120
|
+
end
|
121
|
+
# You never reach here.
|
122
|
+
end
|
123
|
+
```
|
124
|
+
|
125
|
+
### Using batch interface
|
126
|
+
```ruby
|
127
|
+
conn.add_batch("DELETE FROM T");
|
128
|
+
conn.execute_batch
|
129
|
+
conn.add_batch("DELETE FROM T");
|
130
|
+
conn.clear_batch
|
131
|
+
```
|
132
|
+
|
133
|
+
### Using prepared statements
|
134
|
+
```ruby
|
135
|
+
p_sel = conn.prepare("SELECT * FROM T WHERE b = ? and c = ?")
|
136
|
+
p_sel.query(100, 200) do |row|
|
137
|
+
p row
|
138
|
+
end
|
139
|
+
p_sel.close
|
140
|
+
|
141
|
+
p_upd = conn.prepare("UPDATE T SET a = ? WHERE b = ?")
|
142
|
+
count = 0
|
143
|
+
100.times do |i|
|
144
|
+
count += p_upd.update('updated a', i)
|
145
|
+
end
|
146
|
+
|
147
|
+
p_upd.add_batch('pstmt + batch', 10)
|
148
|
+
p_upd.add_batch('pstmt + batch', 20)
|
149
|
+
p_upd.add_batch('pstmt + batch', 30)
|
150
|
+
p_upd.execute_batch
|
151
|
+
p_upd.close
|
152
|
+
```
|
153
|
+
|
154
|
+
### Using table wrappers (since 0.2.0)
|
155
|
+
```ruby
|
156
|
+
# For more complex examples, refer to test/test_object_wrapper.rb
|
157
|
+
|
158
|
+
# Creates a table wrapper
|
159
|
+
table = conn.table('test.data')
|
160
|
+
|
161
|
+
# Counting the records in the table
|
162
|
+
table.count
|
163
|
+
table.count(:a => 10)
|
164
|
+
table.where(:a => 10).count
|
165
|
+
|
166
|
+
table.empty?
|
167
|
+
table.where(:a => 10).empty?
|
168
|
+
|
169
|
+
# Selects the table by combining select, where, and order methods
|
170
|
+
table.select('a apple', :b).where(:c => (1..10)).order('b desc', 'a asc') do |row|
|
171
|
+
puts row.apple
|
172
|
+
end
|
173
|
+
|
174
|
+
# Build select SQL
|
175
|
+
sql = table.select('a apple', :b).where(:c => (1..10)).order('b desc', 'a asc').sql
|
176
|
+
|
177
|
+
# Updates with conditions
|
178
|
+
table.update(:a => 'hello', :b => JDBCHelper::SQL('now()'), :where => { :c => 3 })
|
179
|
+
# Or equivalently,
|
180
|
+
table.where(:c => 3).update(:a => 'hello', :b => JDBCHelper::SQL('now()'))
|
181
|
+
|
182
|
+
# Insert into the table
|
183
|
+
table.insert(:a => 10, :b => 20, :c => JDBCHelper::SQL('10 + 20'))
|
184
|
+
table.insert_ignore(:a => 10, :b => 20, :c => 30)
|
185
|
+
table.replace(:a => 10, :b => 20, :c => 30)
|
186
|
+
|
187
|
+
# Update with common default values
|
188
|
+
with_defaults = table.default(:a => 10, :b => 20)
|
189
|
+
with_defaults.insert(:c => 30)
|
190
|
+
with_defaults.where('a != 10 or b != 20').update # sets a => 10, b => 20
|
191
|
+
|
192
|
+
# Batch updates with batch method
|
193
|
+
table.batch.insert(:a => 10, :b => 20, :c => JDBCHelper::SQL('10 + 20'))
|
194
|
+
table.batch.insert_ignore(:a => 10, :b => 20, :c => 30)
|
195
|
+
conn.execute_batch
|
196
|
+
|
197
|
+
# Delete with conditions
|
198
|
+
table.delete(:c => 3)
|
199
|
+
# Or equivalently,
|
200
|
+
table.where(:c => 3).delete
|
201
|
+
|
202
|
+
# Truncate or drop table (Cannot be undone)
|
203
|
+
table.truncate!
|
204
|
+
table.drop!
|
205
|
+
```
|
206
|
+
|
207
|
+
### Using function wrappers (since 0.2.2)
|
208
|
+
```ruby
|
209
|
+
conn.function(:mod).call 5, 3
|
210
|
+
conn.function(:coalesce).call(nil, nil, 'king')
|
211
|
+
```
|
212
|
+
|
213
|
+
### Using procedure wrappers (since 0.3.0)
|
214
|
+
```ruby
|
215
|
+
# Working with IN/INOUT/OUT parameteres
|
216
|
+
# Bind by ordinal number
|
217
|
+
conn.procedure(:update_and_fetch_something).call(
|
218
|
+
100, # Input parameter
|
219
|
+
["value", String], # Input/Output parameter
|
220
|
+
Fixnum # Output parameter
|
221
|
+
)
|
222
|
+
|
223
|
+
# Bind by parameter name
|
224
|
+
conn.procedure(:update_and_fetch_something).call(
|
225
|
+
:a => 100, :b => ["value", String], :c => Fixnum)
|
226
|
+
```
|
227
|
+
|
228
|
+
### Using sequence wrappers (since 0.4.2)
|
229
|
+
```ruby
|
230
|
+
seq = conn.sequence(:my_seq)
|
231
|
+
next = seq.nextval
|
232
|
+
curr = seq.currval
|
233
|
+
seq.reset!
|
234
|
+
seq.reset! 100
|
235
|
+
```
|
236
|
+
|
237
|
+
## Contributing to jdbc-helper
|
238
|
+
|
239
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
240
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
241
|
+
* Fork the project
|
242
|
+
* Start a feature/bugfix branch
|
243
|
+
* Commit and push until you are happy with your contribution
|
244
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
245
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
246
|
+
|
247
|
+
## Copyright
|
248
|
+
|
249
|
+
Copyright (c) 2011 Junegunn Choi. See LICENSE.txt for
|
250
|
+
further details.
|
251
|
+
|
data/lib/jdbc-helper.rb
CHANGED
@@ -2,19 +2,10 @@
|
|
2
2
|
# Junegunn Choi (junegunn.c@gmail.com)
|
3
3
|
|
4
4
|
if RUBY_PLATFORM.match(/java/).nil?
|
5
|
-
|
5
|
+
raise LoadError, 'JRuby is required for JDBC'
|
6
6
|
end
|
7
7
|
|
8
8
|
require 'java'
|
9
|
-
|
10
|
-
module JavaLang # :nodoc:
|
11
|
-
include_package 'java.lang'
|
12
|
-
end
|
13
|
-
|
14
|
-
module JavaSql # :nodoc:
|
15
|
-
include_package 'java.sql'
|
16
|
-
end
|
17
|
-
|
18
9
|
require 'jdbc-helper/sql'
|
19
10
|
require 'jdbc-helper/sql_prepared'
|
20
11
|
require 'jdbc-helper/constants'
|
@@ -46,7 +46,7 @@ module JDBCHelper
|
|
46
46
|
# # MySQL shortcut connector
|
47
47
|
# conn = JDBCHelper::MySQLConnector.connect('localhost', 'mysql', '', 'test')
|
48
48
|
# conn.close
|
49
|
-
#
|
49
|
+
#
|
50
50
|
# @example Querying database table
|
51
51
|
#
|
52
52
|
# conn.query("SELECT a, b, c FROM T") do | row |
|
@@ -109,89 +109,85 @@ module JDBCHelper
|
|
109
109
|
# p_upd.execute_batch
|
110
110
|
# p_upd.close
|
111
111
|
class Connection
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
112
|
+
# Returns the statistics of the previous operation
|
113
|
+
# @return [JDBCHelper::Connection::Stat] The statistics of the previous operation.
|
114
|
+
def prev_stat
|
115
|
+
@prev_stat.dup
|
116
|
+
end
|
117
|
+
|
118
|
+
# Returns the accumulated statistics of each operation
|
119
|
+
# @return [Hash] Accumulated statistics of each type of operation
|
120
|
+
attr_reader :stats
|
121
|
+
|
122
|
+
# JDBC URL of the connection
|
123
|
+
# @return [String]
|
124
|
+
attr_reader :url
|
125
|
+
|
126
|
+
# JDBC driver of the connection
|
127
|
+
# @return [String]
|
128
|
+
attr_reader :driver
|
129
|
+
|
130
|
+
# Returns the underlying JDBC Connection object.
|
131
|
+
# Only use this when you really need to access it directly.
|
132
|
+
def jdbc_conn
|
133
|
+
@conn
|
134
|
+
end
|
135
|
+
alias java_obj jdbc_conn
|
136
|
+
|
137
|
+
# Creates a database connection.
|
138
|
+
# - `args` hash must include :driver (or "driver") and :url (or "url")
|
139
|
+
# - and takes optional :user and :password tuples (or "user", "password")
|
140
|
+
# - You can also specify :timeout (or "timeout") to override the default connection timeout (60 seconds)
|
141
|
+
#
|
142
|
+
# Must be closed explicitly if not used.
|
143
|
+
# If a block is given, the connection is automatically closed after executing the block.
|
144
|
+
# @param [Hash] args
|
145
|
+
def initialize(args = {})
|
146
|
+
# Subsequent deletes should not affect the input
|
147
147
|
@args = args.dup
|
148
|
-
|
148
|
+
args = @args.dup
|
149
149
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
150
|
+
# String/Symbol
|
151
|
+
%w[driver url user password timeout].each do | strk |
|
152
|
+
args[strk.to_sym] = args.delete strk if args.has_key? strk
|
153
|
+
end
|
154
154
|
|
155
|
-
|
156
|
-
|
155
|
+
raise ArgumentError.new("driver not given") unless args.has_key? :driver
|
156
|
+
raise ArgumentError.new("url not given") unless args.has_key? :url
|
157
157
|
|
158
|
-
|
159
|
-
|
158
|
+
@driver = args.delete :driver
|
159
|
+
@url = args.delete :url
|
160
160
|
|
161
|
-
|
162
|
-
|
163
|
-
rescue Exception
|
164
|
-
# TODO
|
165
|
-
raise
|
166
|
-
end
|
161
|
+
# NameError will be thrown for invalid drivers
|
162
|
+
Java::JavaClass.for_name @driver
|
167
163
|
|
168
|
-
|
169
|
-
|
164
|
+
timeout = args.has_key?(:timeout) ? args.delete(:timeout) : Constants::DEFAULT_LOGIN_TIMEOUT
|
165
|
+
java.sql.DriverManager.setLoginTimeout timeout if timeout
|
170
166
|
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
167
|
+
props = java.util.Properties.new
|
168
|
+
(args.keys - [:url, :driver]).each do | key |
|
169
|
+
props.setProperty(key.to_s, args[key].to_s) if args[key]
|
170
|
+
end
|
171
|
+
|
172
|
+
@conn = java.sql.DriverManager.get_connection(@url, props)
|
173
|
+
@spool = StatementPool.send :new, self
|
174
|
+
@bstmt = nil
|
179
175
|
|
180
|
-
|
181
|
-
|
176
|
+
@stats = Hash.new { | h, k | h[k] = Stat.new(k, 0, 0, 0) }
|
177
|
+
@prev_stat = Stat.new(nil, 0, 0, 0)
|
182
178
|
|
183
179
|
@pstmts = []
|
184
180
|
|
185
181
|
@table_wrappers = {}
|
186
182
|
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
183
|
+
if block_given?
|
184
|
+
begin
|
185
|
+
yield self
|
186
|
+
ensure
|
187
|
+
close rescue nil
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
195
191
|
|
196
192
|
# Creates another connection with the same parameters as this Connection.
|
197
193
|
# @return [JDBCHelper::Connection]
|
@@ -201,140 +197,140 @@ class Connection
|
|
201
197
|
nc
|
202
198
|
end
|
203
199
|
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
200
|
+
# Creates a prepared statement, which is also an encapsulation of Java PreparedStatement object
|
201
|
+
# @param [String] qstr SQL string
|
202
|
+
def prepare(qstr)
|
203
|
+
check_closed
|
208
204
|
|
209
|
-
|
210
|
-
|
205
|
+
pstmt = PreparedStatement.send(:new, self, qstr,
|
206
|
+
measure_exec(:prepare) { @conn.prepare_statement(qstr) })
|
211
207
|
@pstmts << pstmt
|
212
208
|
pstmt
|
213
|
-
|
209
|
+
end
|
214
210
|
|
215
211
|
# @return [Array] Prepared statements currently opened for this connection
|
216
212
|
def prepared_statements
|
217
213
|
@pstmts
|
218
214
|
end
|
219
215
|
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
216
|
+
# Creates a callable statement.
|
217
|
+
# @param [String] qstr SQL string
|
218
|
+
def prepare_call(qstr)
|
219
|
+
check_closed
|
220
|
+
|
221
|
+
CallableStatement.send(:new, self, qstr,
|
222
|
+
measure_exec(:prepare_call) { @conn.prepare_call qstr })
|
223
|
+
end
|
224
|
+
|
225
|
+
# Executes the given code block as a transaction. Returns true if the transaction is committed.
|
226
|
+
# A transaction object is passed to the block, which only has commit and rollback methods.
|
227
|
+
# The execution breaks out of the code block when either of the methods is called.
|
228
|
+
# @yield [JDBCHelper::Connection::Transaction] Responds to commit and rollback.
|
229
|
+
# @return [Boolean] True if committed
|
230
|
+
def transaction
|
231
|
+
check_closed
|
232
|
+
|
233
|
+
raise ArgumentError.new("Transaction block not given") unless block_given?
|
234
|
+
tx = Transaction.send :new, @conn
|
235
|
+
ac = @conn.get_auto_commit
|
236
|
+
status = :unknown
|
237
|
+
begin
|
238
|
+
@conn.set_auto_commit false
|
239
|
+
yield tx
|
240
|
+
@conn.commit
|
241
|
+
status = :committed
|
242
|
+
rescue Transaction::Commit
|
243
|
+
status = :committed
|
244
|
+
rescue Transaction::Rollback
|
245
|
+
status = :rolledback
|
246
|
+
ensure
|
247
|
+
@conn.rollback if status == :unknown
|
248
|
+
@conn.set_auto_commit ac
|
249
|
+
end
|
250
|
+
status == :committed
|
251
|
+
end
|
252
|
+
|
253
|
+
# Executes an update and returns the count of the updated rows.
|
254
|
+
# @param [String] qstr SQL string
|
255
|
+
# @return [Fixnum] Count of affected records
|
256
|
+
def update(qstr)
|
257
|
+
check_closed
|
258
|
+
|
259
|
+
@spool.with do | stmt |
|
260
|
+
ret = measure_exec(:update) { stmt.execute_update(qstr) }
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
# Executes a select query.
|
265
|
+
# When a code block is given, each row of the result is passed to the block one by one.
|
266
|
+
# If a code block not given, this method will return the array of the entire result rows.
|
267
|
+
# (which can be pretty inefficient when the result set is large. In such cases, use enumerate instead.)
|
268
|
+
#
|
269
|
+
# The concept of statement object of JDBC is encapsulated, so there's no need to do additional task,
|
270
|
+
# when you nest select queries, for example.
|
271
|
+
#
|
272
|
+
# conn.query("SELECT a FROM T") do | trow |
|
273
|
+
# conn.query("SELECT * FROM U_#{trow.a}") do | urow |
|
274
|
+
# # ... and so on ...
|
275
|
+
# end
|
276
|
+
# end
|
277
|
+
# @param [String] qstr SQL string
|
278
|
+
# @yield [JDBCHelper::Connection::Row]
|
279
|
+
# @return [Array]
|
280
|
+
def query(qstr, &blk)
|
281
|
+
check_closed
|
282
|
+
|
283
|
+
@spool.with do | stmt |
|
284
|
+
measure_exec(:query) { stmt.execute(qstr) }
|
285
|
+
process_and_close_rset(stmt.get_result_set, &blk)
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
# Returns an enumerable object of the query result.
|
290
|
+
# "enumerate" method is preferable when dealing with a large result set,
|
291
|
+
# since it doesn't have to build a large array.
|
292
|
+
#
|
293
|
+
# The returned enumerator is automatically closed after enumeration.
|
294
|
+
#
|
295
|
+
# conn.enumerate('SELECT * FROM T').each_slice(10) do | slice |
|
296
|
+
# slice.each { | row | print row }
|
297
|
+
# puts
|
298
|
+
# end
|
299
|
+
#
|
300
|
+
# @param [String] qstr SQL string
|
301
|
+
# @yield [JDBCHelper::Connection::Row] Yields each record if block is given
|
302
|
+
# @return [JDBCHelper::Connection::ResultSetEnumerator] Returns an enumerator if block is not given
|
303
|
+
def enumerate(qstr, &blk)
|
304
|
+
check_closed
|
305
|
+
|
306
|
+
return query(qstr, &blk) if block_given?
|
307
|
+
|
308
|
+
stmt = @spool.take
|
309
|
+
begin
|
310
|
+
measure_exec(:query) { stmt.execute(qstr) }
|
311
|
+
rescue Exception
|
312
|
+
@spool.give stmt
|
313
|
+
raise
|
314
|
+
end
|
315
|
+
|
316
|
+
ResultSetEnumerator.send(:new, stmt.get_result_set) { @spool.give stmt }
|
317
|
+
end
|
318
|
+
|
319
|
+
# Adds a statement to be executed in batch
|
320
|
+
# Adds to the batch
|
321
|
+
# @param [String] qstr
|
322
|
+
# @return [NilClass]
|
323
|
+
def add_batch(qstr)
|
324
|
+
check_closed
|
325
|
+
|
326
|
+
@bstmt ||= @spool.take
|
327
|
+
@bstmt.add_batch qstr
|
328
|
+
end
|
329
|
+
|
330
|
+
# Executes batched statements including prepared statements. No effect when no statment is added
|
331
|
+
# @return [NilClass]
|
332
|
+
def execute_batch
|
333
|
+
check_closed
|
338
334
|
|
339
335
|
if @bstmt
|
340
336
|
measure_exec(:execute_batch) { @bstmt.execute_batch }
|
@@ -345,12 +341,12 @@ class Connection
|
|
345
341
|
@pstmts.each do |stmt|
|
346
342
|
measure_exec(:execute_batch) { stmt.execute_batch }
|
347
343
|
end
|
348
|
-
|
344
|
+
end
|
349
345
|
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
346
|
+
# Clears the batched statements including prepared statements.
|
347
|
+
# @return [NilClass]
|
348
|
+
def clear_batch
|
349
|
+
check_closed
|
354
350
|
|
355
351
|
if @bstmt
|
356
352
|
@bstmt.clear_batch
|
@@ -361,164 +357,164 @@ class Connection
|
|
361
357
|
@pstmts.each do |stmt|
|
362
358
|
measure_exec(:execute_batch) { stmt.clear_batch }
|
363
359
|
end
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
360
|
+
end
|
361
|
+
|
362
|
+
# Gives the JDBC driver a hint of the number of rows to fetch from the database by a single interaction.
|
363
|
+
# This is only a hint. It may have no effect at all.
|
364
|
+
# @param [Fixnum] fsz
|
365
|
+
# @return [NilClass]
|
366
|
+
def set_fetch_size(fsz)
|
367
|
+
check_closed
|
368
|
+
|
369
|
+
@fetch_size = fsz
|
370
|
+
@spool.each { | stmt | stmt.set_fetch_size @fetch_size }
|
371
|
+
end
|
372
|
+
alias fetch_size= set_fetch_size
|
373
|
+
|
374
|
+
# Returns the fetch size of the connection. If not set, nil is returned.
|
375
|
+
# @return [Fixnum]
|
376
|
+
attr_reader :fetch_size
|
377
|
+
|
378
|
+
# Closes the connection
|
379
|
+
# @return [NilClass]
|
380
|
+
def close
|
381
|
+
return if closed?
|
382
|
+
@spool.close
|
383
|
+
@conn.close
|
384
|
+
@conn = @spool = nil
|
385
|
+
end
|
386
|
+
|
387
|
+
# Returns if this connection is closed or not
|
388
|
+
# @return [Boolean]
|
389
|
+
def closed?
|
390
|
+
@conn.nil?
|
391
|
+
end
|
392
|
+
|
393
|
+
# Returns a table wrapper for the given table name
|
394
|
+
# @since 0.2.0
|
395
|
+
# @param [String/Symbol] table_name Name of the table to be wrapped
|
396
|
+
# @return [JDBCHelper::TableWrapper]
|
397
|
+
def table table_name
|
402
398
|
@table_wrappers[table_name] ||= JDBCHelper::TableWrapper.new self, table_name
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
399
|
+
end
|
400
|
+
|
401
|
+
# Returns a sequence wrapper for the given name
|
402
|
+
# @since 0.4.2
|
403
|
+
# @param [String/Symbol] sequence_name Name of the sequence to be wrapped
|
404
|
+
# @return [JDBCHelper::SequenceWrapper]
|
405
|
+
def sequence sequence_name
|
406
|
+
JDBCHelper::SequenceWrapper.new self, sequence_name
|
407
|
+
end
|
408
|
+
|
409
|
+
# Returns a function wrapper for the given function name
|
410
|
+
# @since 0.2.2
|
411
|
+
# @param [String/Symbol] func_name Name of the function to be wrapped
|
412
|
+
# @return [JDBCHelper::FunctionWrapper]
|
413
|
+
def function func_name
|
414
|
+
JDBCHelper::FunctionWrapper.new self, func_name
|
415
|
+
end
|
416
|
+
|
417
|
+
# Returns a procedure wrapper for the given procedure name
|
418
|
+
# @since 0.3.0
|
419
|
+
# @param [String/Symbol] proc_name Name of the procedure to be wrapped
|
420
|
+
# @return [JDBCHelper::ProcedureWrapper]
|
421
|
+
def procedure proc_name
|
422
|
+
JDBCHelper::ProcedureWrapper.new self, proc_name
|
423
|
+
end
|
424
|
+
|
425
|
+
# Statistics
|
426
|
+
class Stat
|
427
|
+
attr_accessor :type, :elapsed, :success_count, :fail_count
|
428
|
+
|
429
|
+
def initialize(t, e, s, f)
|
430
|
+
self.type = t
|
431
|
+
self.elapsed = e
|
432
|
+
self.success_count = s
|
433
|
+
self.fail_count = f
|
434
|
+
end
|
435
|
+
end
|
440
436
|
|
441
437
|
private
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
438
|
+
# Transaction object passed to the code block given to transaction method
|
439
|
+
class Transaction
|
440
|
+
# Commits the transaction
|
441
|
+
# @raise [JDBCHelper::Transaction::Commit]
|
442
|
+
def commit
|
443
|
+
@conn.commit
|
444
|
+
raise Commit
|
445
|
+
end
|
446
|
+
# Rolls back this transaction
|
447
|
+
# @raise [JDBCHelper::Transaction::Rollback]
|
448
|
+
def rollback
|
449
|
+
@conn.rollback
|
450
|
+
raise Rollback
|
451
|
+
end
|
452
|
+
private
|
453
|
+
def initialize(conn) # :nodoc:
|
454
|
+
@conn = conn
|
455
|
+
end
|
456
|
+
class Commit < Exception # :nodoc:
|
457
|
+
end
|
458
|
+
class Rollback < Exception # :nodoc:
|
459
|
+
end
|
460
|
+
end
|
461
|
+
|
462
|
+
def create_statement # :nodoc:
|
463
|
+
stmt = @conn.create_statement
|
464
|
+
stmt.set_fetch_size @fetch_size if @fetch_size
|
465
|
+
stmt
|
466
|
+
end
|
467
|
+
|
468
|
+
def process_and_close_rset(rset) # :nodoc:
|
469
|
+
enum = ResultSetEnumerator.send :new, rset
|
470
|
+
rows = []
|
471
|
+
|
472
|
+
begin
|
473
|
+
enum.each do | row |
|
474
|
+
if block_given?
|
475
|
+
yield row
|
476
|
+
else
|
477
|
+
rows << row
|
478
|
+
end
|
479
|
+
end
|
480
|
+
block_given? ? nil : rows
|
481
|
+
ensure
|
482
|
+
enum.close
|
483
|
+
end
|
484
|
+
end
|
489
485
|
|
490
486
|
def close_pstmt pstmt
|
491
487
|
@pstmts.delete pstmt
|
492
488
|
end
|
493
489
|
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
490
|
+
def update_stat(type, elapsed, success_count, fail_count) # :nodoc:
|
491
|
+
@prev_stat.type = type
|
492
|
+
@prev_stat.elapsed = elapsed
|
493
|
+
@prev_stat.success_count = success_count
|
494
|
+
@prev_stat.fail_count = fail_count
|
495
|
+
|
496
|
+
accum = @stats[type]
|
497
|
+
accum.elapsed += elapsed
|
498
|
+
accum.success_count += success_count
|
499
|
+
accum.fail_count += fail_count
|
500
|
+
end
|
501
|
+
|
502
|
+
def measure_exec(type)
|
503
|
+
begin
|
504
|
+
st = Time.now
|
505
|
+
ret = yield
|
506
|
+
elapsed = Time.now - st
|
507
|
+
update_stat(type, elapsed, 1, 0)
|
508
|
+
rescue Exception
|
509
|
+
update_stat(type, 0, 0, 1)
|
510
|
+
raise
|
511
|
+
end
|
512
|
+
ret
|
513
|
+
end
|
514
|
+
|
515
|
+
def check_closed
|
516
|
+
raise RuntimeError.new('Connection already closed') if closed?
|
517
|
+
end
|
522
518
|
end#Connection
|
523
519
|
end#JDBCHelper
|
524
520
|
|