jdbc-helper 0.7.6 → 0.7.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/CHANGELOG.markdown CHANGED
@@ -1,3 +1,11 @@
1
+ ### 0.7.7 / 2013/01/0?
2
+ * `PreparedStatment`s and `TableWrapper`s now inherit the fetch size of the connection
3
+ * Added `JDBCHelper::TableWrapper#fetch_size`
4
+ * Added `JDBCHelper::TableWrapper#execute_batch`
5
+ * Added `JDBCHelper::TableWrapper#clear_batch`
6
+ * `execute_batch` method returns the sum of all update counts
7
+ * Removed undocumented operation statistics
8
+
1
9
  ### 0.7.6 / 2012/08/26
2
10
  * Added FileMaker Pro connector (statonjr)
3
11
 
data/README.markdown CHANGED
@@ -108,13 +108,16 @@ conn.query("SELECT a, b, c FROM T") do |row|
108
108
  row.labels
109
109
  row.rownum
110
110
 
111
- row.a, row.b, row.c # Dot-notation
112
- row[0], row[1], row[2] # Numeric index
113
- row['a'], row['b'], row['c'] # String index. Case-insensitive.
114
- row[:a], row[:b], row[:c] # Symbol index. Case-insensitive.
111
+ a, b, c = row
112
+ a, b, c = row.a, row.b, row.c # Dot-notation
113
+ a, b, c = row[0], row[1], row[2] # Numeric index
114
+ a, b, c = row['a'], row['b'], row['c'] # String index. Case-insensitive.
115
+ a, b, c = row[:a], row[:b], row[:c] # Symbol index. Case-insensitive.
115
116
 
116
117
  row[0..-1] # Range index. Returns an array of values.
117
118
  row[0, 3] # Offset and length. Returns an array of values.
119
+
120
+ row.to_h # Row as a Hash
118
121
  end
119
122
 
120
123
  # Returns an array of rows when block is not given
@@ -199,6 +202,15 @@ p_upd.execute_batch
199
202
  p_upd.close
200
203
  ```
201
204
 
205
+ ### Accessing underlying Java object with `java` method
206
+
207
+ ```ruby
208
+ conn.java.setAutoCommit false
209
+
210
+ pstmt = conn.prepare(sql)
211
+ pstmt.java.getMetaData
212
+ ```
213
+
202
214
  ### Using table wrappers (since 0.2.0)
203
215
  ```ruby
204
216
  # For more complex examples, refer to test/test_object_wrapper.rb
@@ -206,6 +218,8 @@ SQL = JDBCHelper::SQL
206
218
 
207
219
  # Creates a table wrapper
208
220
  table = conn.table('test.data')
221
+ # Or equievalently,
222
+ table = conn['test.data']
209
223
 
210
224
  # Counting the records in the table
211
225
  table.count
@@ -215,8 +229,8 @@ table.where(:a => 10).count
215
229
  table.empty?
216
230
  table.where(:a => 10).empty?
217
231
 
218
- # Selects the table by combining select, where, and order methods
219
- table.select('a apple', :b).where(:c => (1..10)).order('b desc', 'a asc') do |row|
232
+ # Selects the table by combining select, where, order and fetch_size methods
233
+ table.select('a apple', :b).where(:c => (1..10)).order('b desc', 'a asc').fetch_size(100).each do |row|
220
234
  puts row.apple
221
235
  end
222
236
 
@@ -239,7 +253,8 @@ with_defaults.where('a != 10 or b != 20').update # sets a => 10, b => 20
239
253
  # Batch updates with batch method
240
254
  table.batch.insert(:a => 10, :b => 20, :c => SQL.expr('10 + 20'))
241
255
  table.batch.insert_ignore(:a => 10, :b => 20, :c => 30)
242
- conn.execute_batch
256
+ table.batch.where(:a => 10).update(:a => 20)
257
+ table.execute_batch :insert, :update
243
258
 
244
259
  # Delete with conditions
245
260
  table.delete(:c => 3)
@@ -275,27 +290,48 @@ scope = table.where(
275
290
  scope.update(:a => 'xyz')
276
291
  ```
277
292
 
278
- #### Invalid use of dynamic conditions
293
+ #### Invalid use of plain String conditions
279
294
 
280
- TableWrapper object internally creates JDBC PreparedStatements.
281
- If you dynamically build many condition-strings as the following example,
282
- it would soon fail because there will be too many open PreparedStatements.
295
+ A TableWrapper object internally builds SQL strings
296
+ and creates JDBC PreparedStatement object for each distinct SQL.
297
+
298
+ If you build many number of where-clause Strings as shown in the following code,
299
+ soon there will be too many open PreparedStatements,
300
+ and if the number exceeds the system limit, an error will be thrown.
283
301
 
284
302
  ```ruby
303
+ table = connection['table']
304
+
305
+ # Leads to 10000 PreparedStatements !!
285
306
  10000.times do |idx|
286
307
  table.count("id = #{idx}")
308
+ # select count(*) from table where id = 0
309
+ # select count(*) from table where id = 1
310
+ # select count(*) from table where id = 2
311
+ # select count(*) from table where id = 3
312
+ # ...
287
313
  end
288
314
  ```
289
315
 
290
- Correct ways of doing the same would be as follows.
316
+ In that case, you can `close` the table wrapper to close all the open PreparedStatements.
317
+
318
+ ```ruby
319
+ table.close
320
+ ```
321
+
322
+ However, you should always prefer using much more efficient Hash or Array expression over plain String,
323
+ so you don't have to worry about the proliferation of PreparedStatements.
291
324
 
292
325
  ```ruby
326
+ # 20000 queries but only a single PreparedStatement
293
327
  10000.times do |idx|
294
328
  # 1. with Hash
295
329
  table.count('id' => idx)
330
+ # select count(*) from table where id = ?
296
331
 
297
332
  # 2. with Array
298
333
  table.count(["id = ?", idx])
334
+ # select count(*) from table where id = ?
299
335
  end
300
336
  ```
301
337
 
data/Rakefile CHANGED
@@ -14,11 +14,3 @@ Rake::TestTask.new(:performance) do |test|
14
14
  test.verbose = true
15
15
  end
16
16
 
17
- require 'rcov/rcovtask'
18
- Rcov::RcovTask.new do |test|
19
- test.libs << 'test'
20
- test.pattern = 'test/**/test_*.rb'
21
- test.rcov_opts << '--exclude rcov'
22
- test.verbose = true
23
- end
24
-
data/jdbc-helper.gemspec CHANGED
@@ -15,8 +15,8 @@ Gem::Specification.new do |gem|
15
15
  gem.require_paths = ["lib"]
16
16
  gem.version = JDBCHelper::VERSION
17
17
 
18
- gem.add_runtime_dependency 'insensitive_hash', '>= 0.2.4', '< 0.4.0'
19
- gem.add_development_dependency "bundler", "~> 1.1.4"
20
- gem.add_development_dependency "rcov", "~> 0.9"
21
- gem.add_development_dependency "test-unit", ">= 2.3.0"
18
+ gem.add_runtime_dependency 'insensitive_hash', '>= 0.2.4'
19
+ gem.add_development_dependency "bundler"
20
+ gem.add_development_dependency "simplecov"
21
+ gem.add_development_dependency "test-unit"
22
22
  end
@@ -25,17 +25,17 @@ module JDBCHelper
25
25
  # @example Prerequisites
26
26
  # # Add JDBC driver of the DBMS you're willing to use to your CLASSPATH
27
27
  # export CLASSPATH=$CLASSPATH:~/lib/mysql-connector-java.jar
28
- #
29
- #
28
+ #
29
+ #
30
30
  # @example Connecting to a database
31
- #
31
+ #
32
32
  # # :driver and :url must be given
33
33
  # conn = JDBCHelper::Connection.new(
34
34
  # :driver => 'com.mysql.jdbc.Driver',
35
35
  # :url => 'jdbc:mysql://localhost/test')
36
36
  # conn.close
37
- #
38
- #
37
+ #
38
+ #
39
39
  # # Optional :user and :password
40
40
  # conn = JDBCHelper::Connection.new(
41
41
  # :driver => 'com.mysql.jdbc.Driver',
@@ -43,12 +43,12 @@ module JDBCHelper
43
43
  # :user => 'mysql',
44
44
  # :password => '')
45
45
  # conn.close
46
- #
47
- #
46
+ #
47
+ #
48
48
  # # MySQL shortcut connector
49
49
  # conn = JDBCHelper::MySQLConnector.connect('localhost', 'mysql', '', 'test')
50
50
  # conn.close
51
- #
51
+ #
52
52
  # @example Querying database table
53
53
  #
54
54
  # conn.query("SELECT a, b, c FROM T") do | row |
@@ -59,11 +59,11 @@ module JDBCHelper
59
59
  # puts row[0], row[1], row[2]
60
60
  # puts row['a'], row['b'], row['c']
61
61
  # end
62
- #
62
+ #
63
63
  # # Returns an array of rows when block is not given
64
64
  # rows = conn.query("SELECT b FROM T")
65
65
  # uniq_rows = rows.uniq
66
- #
66
+ #
67
67
  # # You can even nest queries
68
68
  # conn.query("SELECT a FROM T") do | row1 |
69
69
  # conn.query("SELECT * FROM T_#{row1.a}") do | row2 |
@@ -72,26 +72,26 @@ module JDBCHelper
72
72
  # end
73
73
  # @example Updating database table
74
74
  # del_count = conn.update("DELETE FROM T")
75
- #
75
+ #
76
76
  # @example Transaction
77
77
  # committed = conn.transaction do | tx |
78
78
  # # ...
79
79
  # # Transaction logic here
80
80
  # # ...
81
- #
81
+ #
82
82
  # if success
83
83
  # tx.commit
84
84
  # else
85
85
  # tx.rollback
86
86
  # end
87
87
  # end
88
- #
88
+ #
89
89
  # @example Using batch interface
90
90
  # conn.add_batch("DELETE FROM T");
91
91
  # conn.execute_batch
92
92
  # conn.add_batch("DELETE FROM T");
93
93
  # conn.clear_batch
94
- #
94
+ #
95
95
  # @example Using prepared statements
96
96
  # p_sel = conn.prepare("SELECT * FROM T WHERE b = ? and c = ?")
97
97
  # p_sel.query(100, 200) do | row |
@@ -111,16 +111,6 @@ module JDBCHelper
111
111
  # p_upd.execute_batch
112
112
  # p_upd.close
113
113
  class Connection
114
- # Returns the statistics of the previous operation
115
- # @return [JDBCHelper::Connection::Stat] The statistics of the previous operation.
116
- def prev_stat
117
- @prev_stat.dup
118
- end
119
-
120
- # Returns the accumulated statistics of each operation
121
- # @return [Hash] Accumulated statistics of each type of operation
122
- attr_reader :stats
123
-
124
114
  # JDBC URL of the connection
125
115
  # @return [String]
126
116
  attr_reader :url
@@ -135,6 +125,7 @@ class Connection
135
125
  @conn
136
126
  end
137
127
  alias java_obj jdbc_conn
128
+ alias java jdbc_conn
138
129
 
139
130
  # Creates a database connection.
140
131
  # - `args` hash must include :driver (or "driver") and :url (or "url")
@@ -163,23 +154,21 @@ class Connection
163
154
  if timeout.is_a?(Fixnum) == false || timeout <= 0
164
155
  raise ArgumentError.new("Timeout must be a positive integer")
165
156
  end
166
- java.sql.DriverManager.setLoginTimeout timeout
157
+ Java::java.sql.DriverManager.setLoginTimeout timeout
167
158
  end
168
159
 
169
- props = java.util.Properties.new
160
+ props = Java::java.util.Properties.new
170
161
  args.each do |k, v|
171
162
  props.setProperty(k.to_s, v.to_s) if v
172
163
  end
173
-
174
- @conn = java.sql.DriverManager.get_connection(@url, props)
164
+
165
+ @conn = Java::java.sql.DriverManager.get_connection(@url, props)
175
166
  @spool = StatementPool.send :new, self
176
167
  @bstmt = nil
177
-
178
- @stats = Hash.new { | h, k | h[k] = Stat.new(k, 0, 0, 0) }
179
- @prev_stat = Stat.new(nil, 0, 0, 0)
168
+ @fetch_size = nil
180
169
 
181
170
  @pstmts = []
182
-
171
+
183
172
  @table_wrappers = {}
184
173
 
185
174
  if block_given?
@@ -204,8 +193,9 @@ class Connection
204
193
  def prepare(qstr)
205
194
  check_closed
206
195
 
207
- pstmt = PreparedStatement.send(:new, self, qstr,
208
- measure_exec(:prepare) { @conn.prepare_statement(qstr) })
196
+ pstmt = PreparedStatement.send(:new, self, qstr, @conn.prepare_statement(qstr))
197
+ pstmt.set_fetch_size @fetch_size if @fetch_size
198
+
209
199
  @pstmts << pstmt
210
200
  pstmt
211
201
  end
@@ -220,8 +210,7 @@ class Connection
220
210
  def prepare_call(qstr)
221
211
  check_closed
222
212
 
223
- CallableStatement.send(:new, self, qstr,
224
- measure_exec(:prepare_call) { @conn.prepare_call qstr })
213
+ CallableStatement.send(:new, self, qstr, @conn.prepare_call(qstr))
225
214
  end
226
215
 
227
216
  # Executes the given code block as a transaction. Returns true if the transaction is committed.
@@ -252,7 +241,7 @@ class Connection
252
241
  status == :committed
253
242
  end
254
243
 
255
- # Executes an SQL and returns the count of the update rows or a ResultSetEnumerator object
244
+ # Executes an SQL and returns the count of the update rows or a ResultSetEnumerator object
256
245
  # depending on the type of the given statement.
257
246
  # If a ResultSetEnumerator is returned, it must be enumerated or closed.
258
247
  # @param [String] qstr SQL string
@@ -262,7 +251,7 @@ class Connection
262
251
 
263
252
  stmt = @spool.take
264
253
  begin
265
- if measure_exec(:execute) { stmt.execute(qstr) }
254
+ if stmt.execute(qstr)
266
255
  ResultSetEnumerator.send(:new, stmt.getResultSet) { @spool.give stmt }
267
256
  else
268
257
  rset = stmt.getUpdateCount
@@ -282,7 +271,7 @@ class Connection
282
271
  check_closed
283
272
 
284
273
  @spool.with do | stmt |
285
- ret = measure_exec(:update) { stmt.execute_update(qstr) }
274
+ ret = stmt.execute_update(qstr)
286
275
  end
287
276
  end
288
277
 
@@ -306,7 +295,7 @@ class Connection
306
295
  check_closed
307
296
 
308
297
  @spool.with do | stmt |
309
- rset = measure_exec(:query) { stmt.execute_query(qstr) }
298
+ rset = stmt.execute_query(qstr)
310
299
  process_and_close_rset(rset, &blk)
311
300
  end
312
301
  end
@@ -332,7 +321,7 @@ class Connection
332
321
 
333
322
  stmt = @spool.take
334
323
  begin
335
- rset = measure_exec(:query) { stmt.execute_query(qstr) }
324
+ rset = stmt.execute_query(qstr)
336
325
  return ResultSetEnumerator.send(:new, rset) { @spool.give stmt }
337
326
  rescue Exception
338
327
  @spool.give stmt
@@ -351,20 +340,24 @@ class Connection
351
340
  @bstmt.add_batch qstr
352
341
  end
353
342
 
354
- # Executes batched statements including prepared statements. No effect when no statment is added
355
- # @return [NilClass]
343
+ # Executes batched statements including prepared statements. No effect when no statement is added
344
+ # @return [Fixnum] Sum of all update counts
356
345
  def execute_batch
357
346
  check_closed
358
347
 
348
+ cnt = 0
349
+
359
350
  if @bstmt
360
- measure_exec(:execute_batch) { @bstmt.execute_batch }
351
+ cnt += @bstmt.execute_batch.inject(:+) || 0
361
352
  @spool.give @bstmt
362
353
  @bstmt = nil
363
354
  end
364
355
 
365
- @pstmts.each do |stmt|
366
- measure_exec(:execute_batch) { stmt.execute_batch }
356
+ @pstmts.each do |pstmt|
357
+ cnt += pstmt.execute_batch
367
358
  end
359
+
360
+ cnt
368
361
  end
369
362
 
370
363
  # Clears the batched statements including prepared statements.
@@ -379,7 +372,7 @@ class Connection
379
372
  end
380
373
 
381
374
  @pstmts.each do |stmt|
382
- measure_exec(:execute_batch) { stmt.clear_batch }
375
+ stmt.clear_batch
383
376
  end
384
377
  end
385
378
 
@@ -419,8 +412,11 @@ class Connection
419
412
  # @param [String/Symbol] table_name Name of the table to be wrapped
420
413
  # @return [JDBCHelper::TableWrapper]
421
414
  def table table_name
422
- @table_wrappers[table_name] ||= JDBCHelper::TableWrapper.new self, table_name
415
+ table = JDBCHelper::TableWrapper.new(self, table_name)
416
+ table = table.fetch_size(@fetch_size) if @fetch_size
417
+ @table_wrappers[table_name] ||= table
423
418
  end
419
+ alias [] table
424
420
 
425
421
  # Returns a sequence wrapper for the given name
426
422
  # @since 0.4.2
@@ -446,24 +442,13 @@ class Connection
446
442
  JDBCHelper::ProcedureWrapper.new self, proc_name
447
443
  end
448
444
 
445
+ # @return [String]
449
446
  def inspect
450
447
  InsensitiveHash[@args].merge({ :closed? => closed? }).tap { |c|
451
448
  c.delete(:password)
452
449
  }.inspect
453
450
  end
454
451
 
455
- # Statistics
456
- class Stat
457
- attr_accessor :type, :elapsed, :success_count, :fail_count
458
-
459
- def initialize(t, e, s, f)
460
- self.type = t
461
- self.elapsed = e
462
- self.success_count = s
463
- self.fail_count = f
464
- end
465
- end
466
-
467
452
  private
468
453
  # Transaction object passed to the code block given to transaction method
469
454
  class Transaction
@@ -479,7 +464,7 @@ private
479
464
  @conn.rollback
480
465
  raise Rollback
481
466
  end
482
- private
467
+ private
483
468
  def initialize(conn) # :nodoc:
484
469
  @conn = conn
485
470
  end
@@ -517,31 +502,6 @@ private
517
502
  @pstmts.delete pstmt
518
503
  end
519
504
 
520
- def update_stat(type, elapsed, success_count, fail_count) # :nodoc:
521
- @prev_stat.type = type
522
- @prev_stat.elapsed = elapsed
523
- @prev_stat.success_count = success_count
524
- @prev_stat.fail_count = fail_count
525
-
526
- accum = @stats[type]
527
- accum.elapsed += elapsed
528
- accum.success_count += success_count
529
- accum.fail_count += fail_count
530
- end
531
-
532
- def measure_exec(type)
533
- begin
534
- st = Time.now
535
- ret = yield
536
- elapsed = Time.now - st
537
- update_stat(type, elapsed, 1, 0)
538
- rescue Exception
539
- update_stat(type, 0, 0, 1)
540
- raise
541
- end
542
- ret
543
- end
544
-
545
505
  def check_closed
546
506
  raise RuntimeError.new('Connection already closed') if closed?
547
507
  end