jdbc-helper 0.1.3 → 0.2.0

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/README.rdoc CHANGED
@@ -4,7 +4,7 @@ A JDBC helper for Ruby/Database developers.
4
4
  JDBCHelper::Connection object wraps around a JDBC connection and provides much nicer interface to
5
5
  crucial database operations from primitive selects and updates to more complex ones involving
6
6
  batch updates, prepared statements and transactions.
7
- As the name implies, only works on JRuby.
7
+ As the name implies, this gem only works on JRuby.
8
8
 
9
9
  = Examples
10
10
 
@@ -108,6 +108,22 @@ Add JDBC driver of the DBMS you're willing to use to your CLASSPATH
108
108
  p_upd.execute_batch
109
109
  p_upd.close
110
110
 
111
+ == Using table wrappers (since 0.2.0)
112
+ # For more complex examples, refer to test/test_object_wrapper.rb
113
+
114
+ conn.table('test.data').count
115
+ conn.table('test.data').empty?
116
+ conn.table('test.data').select(:c => 3) do |row|
117
+ puts row.a
118
+ end
119
+ conn.table('test.data').update(:a => 1, :b => 2, :where => { :c => 3 })
120
+ conn.table('test.data').insert(:a => 10, :b => 20, :c => 30)
121
+ conn.table('test.data').insert_ignore(:a => 10, :b => 20, :c => 30)
122
+ conn.table('test.data').replace(:a => 10, :b => 20, :c => 30)
123
+ conn.table('test.data').delete(:c => 3)
124
+ conn.table('test.data').truncate_table!
125
+ conn.table('test.data').drop_table!
126
+
111
127
  == Contributing to jdbc-helper
112
128
 
113
129
  * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
@@ -2,18 +2,18 @@
2
2
  # Junegunn Choi (junegunn.c@gmail.com)
3
3
 
4
4
  module JDBCHelper
5
+ class Connection
5
6
  # An encapsulation of Java PreparedStatment object.
6
7
  # Used to execute parameterized queries efficiently.
7
- # Has a very similar set of interface as JDBCHelper::Connection.
8
- #
9
- # e.g.
8
+ # Has a very similar set of interface to that of JDBCHelper::Connection.
10
9
  #
10
+ # @example
11
11
  # pstmt = conn.prepare("SELECT * FROM T WHERE a = ? and b = ?")
12
12
  # rows = pstmt.query(10, 20)
13
13
  # enum = pstmt.enumerate(10, 20)
14
- #
15
- class Connection
16
14
  class PreparedStatement
15
+ # SQL string
16
+ # @return [String]
17
17
  attr_reader :sql
18
18
 
19
19
  # Returns the encapsulated JDBC PreparedStatement object.
@@ -22,50 +22,58 @@ class PreparedStatement
22
22
  end
23
23
 
24
24
  # Returns the number of parameters required
25
+ # @return [Fixnum]
25
26
  def parameter_count
26
27
  @pmd ||= @pstmt.get_parameter_meta_data
27
28
  @pmd.get_parameter_count
28
29
  end
29
30
 
31
+ # @return [Fixnum]
30
32
  def update(*params)
31
33
  check_closed
32
34
 
33
35
  set_params(params)
34
- measure(:p_update) { @pstmt.execute_update }
36
+ measure_exec(:p_update) { @pstmt.execute_update }
35
37
  end
36
38
 
39
+ # @return [Array] Returns an Array if block is not given
37
40
  def query(*params, &blk)
38
41
  check_closed
39
42
 
40
43
  set_params(params)
41
44
  # sorry, ignoring privacy
42
45
  @conn.send(:process_and_close_rset,
43
- measure(:p_query) { @pstmt.execute_query }, &blk)
46
+ measure_exec(:p_query) { @pstmt.execute_query }, &blk)
44
47
  end
45
48
 
49
+ # @return [JDBCHelper::Connection::ResultSetEnumerator]
46
50
  def enumerate(*params, &blk)
47
51
  check_closed
48
52
 
49
53
  return query(*params, &blk) if block_given?
50
54
 
51
55
  set_params(params)
52
- ResultSetEnumerator.new(measure(:p_query) { @pstmt.execute_query })
56
+ ResultSetEnumerator.new(measure_exec(:p_query) { @pstmt.execute_query })
53
57
  end
54
58
 
55
- # batch interface
59
+ # Adds to the batch
60
+ # @return [NilClass]
56
61
  def add_batch(*params)
57
62
  check_closed
58
63
 
59
64
  set_params(params)
60
65
  @pstmt.add_batch
61
66
  end
67
+ # Executes the batch
62
68
  def execute_batch
63
69
  check_closed
64
70
 
65
- measure(:p_execute_batch) {
71
+ measure_exec(:p_execute_batch) {
66
72
  @pstmt.executeBatch
67
73
  }
68
74
  end
75
+ # Clears the batch
76
+ # @return [NilClass]
69
77
  def clear_batch
70
78
  check_closed
71
79
 
@@ -74,12 +82,15 @@ class PreparedStatement
74
82
 
75
83
  # Gives the JDBC driver a hint of the number of rows to fetch from the database by a single interaction.
76
84
  # This is only a hint. It may no effect at all.
85
+ # @return [NilClass]
77
86
  def set_fetch_size(fsz)
78
87
  check_closed
79
88
 
80
89
  @pstmt.set_fetch_size fsz
81
90
  end
82
91
 
92
+ # Closes the prepared statement
93
+ # @return [NilClass]
83
94
  def close
84
95
  return if closed?
85
96
  @pstmt.close
@@ -87,6 +98,7 @@ class PreparedStatement
87
98
  @pstmt = @pstmts = nil
88
99
  end
89
100
 
101
+ # @return [Boolean]
90
102
  def closed?
91
103
  @pstmt.nil?
92
104
  end
@@ -117,8 +129,8 @@ private
117
129
  end
118
130
  end
119
131
 
120
- def measure(type, &blk) # :nodoc:
121
- @conn.send(:measure, type, &blk)
132
+ def measure_exec(type, &blk) # :nodoc:
133
+ @conn.send(:measure_exec, type, &blk)
122
134
  end
123
135
 
124
136
  def check_closed
@@ -127,10 +139,10 @@ private
127
139
 
128
140
  SETTER_MAP =
129
141
  {
130
- 'Java::JavaSql::Date' => :setDate,
131
- 'Java::JavaSql::Time' => :setTime,
132
- 'Java::JavaSql::Timestamp' => :setTimestamp,
133
- 'Time' => :setTimestamp,
142
+ 'Java::JavaSql::Date' => :setDate,
143
+ 'Java::JavaSql::Time' => :setTime,
144
+ 'Java::JavaSql::Timestamp' => :setTimestamp,
145
+ 'Time' => :setTimestamp,
134
146
  'Java::JavaSql::Blob' => :setBinaryStream,
135
147
 
136
148
  # Only available when MySQL JDBC driver is loaded.
@@ -2,12 +2,12 @@
2
2
  # Junegunn Choi (junegunn.c@gmail.com)
3
3
 
4
4
  module JDBCHelper
5
+ class Connection
5
6
  # The result of database queries.
6
7
  # Designed to be very flexible on its interface.
7
8
  # You can access it like an array, a hash or an ORM object.
8
9
  #
9
- # e.g.
10
- #
10
+ # @example
11
11
  # conn.query('SELECT a, b, c FROM t') do | row |
12
12
  # puts row.a
13
13
  # puts row[1]
@@ -22,26 +22,36 @@ module JDBCHelper
22
22
  #
23
23
  # puts row.rownum
24
24
  # end
25
- #
26
- class Connection
27
25
  class Row
28
- attr_reader :labels, :values, :rownum
26
+ # @return [Array] Labels of the columns
27
+ attr_reader :labels
28
+ # @return [Array] Values in Array
29
+ attr_reader :values
30
+ # @return [Fixnum] Sequential row number assigned within the scope of the query
31
+ attr_reader :rownum
29
32
  alias_method :keys, :labels
30
33
 
31
34
  include Enumerable
32
35
 
36
+ # @param [Fixnum/String] idx
37
+ # @return [Object]
33
38
  def [](idx)
34
- if idx.is_a? Fixnum
39
+ case idx
40
+ when Fixnum
35
41
  raise RangeError.new("Index out of bound") if idx >= @values.length
36
42
  @values[idx]
37
- else
43
+ when String
38
44
  # case-insensitive, assuming no duplicate labels
39
45
  vidx = @labels_d.index(idx.downcase) or
40
46
  raise NameError.new("Unknown column label: #{idx}")
41
47
  @values[vidx]
48
+ else
49
+ # See how it goes...
50
+ @values[idx]
42
51
  end
43
52
  end
44
53
 
54
+ # @yield [Object]
45
55
  def each(&blk)
46
56
  @values.each { | v | yield v }
47
57
 
@@ -50,6 +60,7 @@ class Row
50
60
  # end
51
61
  end
52
62
 
63
+ # @return [String]
53
64
  def inspect
54
65
  strs = []
55
66
  @labels.each do | col |
@@ -58,18 +69,22 @@ class Row
58
69
  '[' + strs.join(', ') + ']'
59
70
  end
60
71
 
72
+ # @return [String]
61
73
  def to_s
62
74
  @values.to_s
63
75
  end
64
76
 
77
+ # @return [Array]
65
78
  def to_a
66
79
  @values
67
80
  end
68
81
 
82
+ # @return [String]
69
83
  def join(sep = $,)
70
84
  to_a.join(sep)
71
85
  end
72
86
 
87
+ # @return [Boolean]
73
88
  def eql?(other)
74
89
  self.hash == other.hash
75
90
  end
@@ -86,15 +101,28 @@ private
86
101
  @labels_d = col_labels.map { | l | l.downcase }
87
102
  @values = values
88
103
  @rownum = rownum
104
+
105
+ # @labels_d.each do | l |
106
+ # (class << self; self; end).instance_eval do
107
+ # define_method l do
108
+ # self[l]
109
+ # end
110
+ # end
111
+ # end
89
112
  end
90
113
 
114
+ # Performs better than defining methods
91
115
  def method_missing(symb, *args)
92
116
  if vidx = @labels_d.index(symb.to_s.downcase)
93
- @values[vidx]
117
+ begin
118
+ @values[vidx]
119
+ rescue NameError
120
+ raise NoMethodError.new("undefined method or attribute `#{symb}'")
121
+ end
94
122
  elsif @values.respond_to?(symb)
95
123
  @values.send(symb, *args)
96
124
  else
97
- raise NameError.new("Unknown method: #{symb}")
125
+ raise NoMethodError.new("undefined method or attribute `#{symb}'")
98
126
  end
99
127
  end
100
128
 
@@ -2,10 +2,11 @@
2
2
  # Junegunn Choi (junegunn.c@gmail.com)
3
3
 
4
4
  module JDBCHelper
5
- # Internal implementation for supporting query nesting.
6
- # Not thread-safe. Well, sharing a JDBC connection between threads is not the best idea anyway.
7
5
  class Connection
8
- class StatementPool # :nodoc:
6
+ # Internal implementation for supporting query nesting.
7
+ # Not thread-safe. (Sharing a JDBC connection between threads is not the best idea anyway.)
8
+ # @private
9
+ class StatementPool
9
10
  def initialize(conn, max_size = 20)
10
11
  @conn = conn
11
12
  @max_size = max_size # TODO
@@ -10,15 +10,12 @@ module JDBCHelper
10
10
  # Encapsulates JDBC database connection.
11
11
  # Lets you easily execute SQL statements and access their results.
12
12
  #
13
- #
14
- # = Examples
15
- #
16
- # == Prerequisites
17
- # Add JDBC driver of the DBMS you're willing to use to your CLASSPATH
13
+ # @example Prerequisites
14
+ # # Add JDBC driver of the DBMS you're willing to use to your CLASSPATH
18
15
  # export CLASSPATH=$CLASSPATH:~/lib/mysql-connector-java.jar
19
16
  #
20
17
  #
21
- # == Connecting to a database
18
+ # @example Connecting to a database
22
19
  #
23
20
  # # :driver and :url must be given
24
21
  # conn = JDBCHelper::Connection.new(
@@ -40,7 +37,7 @@ module JDBCHelper
40
37
  # conn = JDBCHelper::MySQLConnector.connect('localhost', 'mysql', '', 'test')
41
38
  # conn.close
42
39
  #
43
- # == Querying database table
40
+ # @example Querying database table
44
41
  #
45
42
  # conn.query("SELECT a, b, c FROM T") do | row |
46
43
  # p row.labels
@@ -61,10 +58,10 @@ module JDBCHelper
61
58
  # # ...
62
59
  # end
63
60
  # end
64
- # == Updating database table
61
+ # @example Updating database table
65
62
  # del_count = conn.update("DELETE FROM T")
66
63
  #
67
- # == Transaction
64
+ # @example Transaction
68
65
  # committed = conn.transaction do | tx |
69
66
  # # ...
70
67
  # # Transaction logic here
@@ -77,13 +74,13 @@ module JDBCHelper
77
74
  # end
78
75
  # end
79
76
  #
80
- # == Using batch interface
77
+ # @example Using batch interface
81
78
  # conn.add_batch("DELETE FROM T");
82
79
  # conn.execute_batch
83
80
  # conn.add_batch("DELETE FROM T");
84
81
  # conn.clear_batch
85
82
  #
86
- # == Using prepared statements
83
+ # @example Using prepared statements
87
84
  # p_sel = conn.prepare("SELECT * FROM T WHERE b = ? and c = ?")
88
85
  # p_sel.query(100, 200) do | row |
89
86
  # p row
@@ -102,14 +99,14 @@ module JDBCHelper
102
99
  # p_upd.execute_batch
103
100
  # p_upd.close
104
101
  class Connection
105
- Stat = Struct.new("DBExecStat", :type, :elapsed, :success_count, :fail_count) # :nodoc:
106
-
107
102
  # Returns the statistics of the previous operation
103
+ # @return [JDBCHelper::Connection::Stat] The statistics of the previous operation.
108
104
  def prev_stat
109
105
  @prev_stat.dup
110
106
  end
111
107
 
112
108
  # Returns the accumulated statistics of each operation
109
+ # @return [Hash] Accumulated statistics of each type of operation
113
110
  attr_reader :stats
114
111
 
115
112
  # Returns the underlying JDBC Connection object.
@@ -126,6 +123,7 @@ class Connection
126
123
  #
127
124
  # Must be closed explicitly if not used.
128
125
  # If a block is given, the connection is automatically closed after executing the block.
126
+ # @param [Hash] args
129
127
  def initialize(args = {})
130
128
  # String-tolerance..
131
129
  %w[driver url user password timeout].each do | strk |
@@ -168,13 +166,14 @@ class Connection
168
166
  end
169
167
 
170
168
  # Creates a prepared statement, which is also an encapsulation of Java PreparedStatement object
169
+ # @param [String] qstr SQL string
171
170
  def prepare(qstr)
172
171
  check_closed
173
172
 
174
173
  return @pstmts[qstr] if @pstmts.has_key? qstr
175
174
 
176
175
  pstmt = PreparedStatement.send(:new, self, @pstmts, qstr,
177
- measure(:prepare) { @conn.prepare_statement(qstr) })
176
+ measure_exec(:prepare) { @conn.prepare_statement(qstr) })
178
177
  @pstmts[qstr] = pstmt
179
178
  pstmt
180
179
  end
@@ -182,6 +181,8 @@ class Connection
182
181
  # Executes the given code block as a transaction. Returns true if the transaction is committed.
183
182
  # A transaction object is passed to the block, which only has commit and rollback methods.
184
183
  # The execution breaks out of the code block when either of the methods is called.
184
+ # @yield [JDBCHelper::Connection::Transaction] Responds to commit and rollback.
185
+ # @return [Boolean] True if committed
185
186
  def transaction
186
187
  check_closed
187
188
 
@@ -206,11 +207,13 @@ class Connection
206
207
  end
207
208
 
208
209
  # Executes an update and returns the count of the updated rows.
210
+ # @param [String] qstr SQL string
211
+ # @return [Fixnum] Count of affected records
209
212
  def update(qstr)
210
213
  check_closed
211
214
 
212
215
  @spool.with do | stmt |
213
- ret = measure(:update) { stmt.execute_update(qstr) }
216
+ ret = measure_exec(:update) { stmt.execute_update(qstr) }
214
217
  end
215
218
  end
216
219
 
@@ -227,11 +230,14 @@ class Connection
227
230
  # # ... and so on ...
228
231
  # end
229
232
  # end
233
+ # @param [String] qstr SQL string
234
+ # @yield [JDBCHelper::Connection::Row]
235
+ # @return [Array]
230
236
  def query(qstr, &blk)
231
237
  check_closed
232
238
 
233
239
  @spool.with do | stmt |
234
- measure(:query) { stmt.execute(qstr) }
240
+ measure_exec(:query) { stmt.execute(qstr) }
235
241
  process_and_close_rset(stmt.get_result_set, &blk)
236
242
  end
237
243
  end
@@ -247,6 +253,9 @@ class Connection
247
253
  # puts
248
254
  # end
249
255
  #
256
+ # @param [String] qstr SQL string
257
+ # @yield [JDBCHelper::Connection::Row] Yields each record if block is given
258
+ # @return [JDBCHelper::Connection::ResultSetEnumerator] Returns an enumerator if block is not given
250
259
  def enumerate(qstr, &blk)
251
260
  check_closed
252
261
 
@@ -254,7 +263,7 @@ class Connection
254
263
 
255
264
  stmt = @spool.take
256
265
  begin
257
- measure(:query) { stmt.execute(qstr) }
266
+ measure_exec(:query) { stmt.execute(qstr) }
258
267
  rescue Exception
259
268
  @spool.give stmt
260
269
  raise
@@ -264,6 +273,9 @@ class Connection
264
273
  end
265
274
 
266
275
  # Adds a statement to be executed in batch
276
+ # Adds to the batch
277
+ # @param [String] qstr
278
+ # @return [NilClass]
267
279
  def add_batch(qstr)
268
280
  check_closed
269
281
 
@@ -272,17 +284,19 @@ class Connection
272
284
  end
273
285
 
274
286
  # Executes batched statements. No effect when no statment is added
287
+ # @return [NilClass]
275
288
  def execute_batch
276
289
  check_closed
277
290
 
278
291
  return unless @bstmt
279
- ret = measure(:execute_batch) { @bstmt.execute_batch }
292
+ ret = measure_exec(:execute_batch) { @bstmt.execute_batch }
280
293
  @spool.give @bstmt
281
294
  @bstmt = nil
282
295
  ret
283
296
  end
284
297
 
285
298
  # Clears the batched statements
299
+ # @return [NilClass]
286
300
  def clear_batch
287
301
  check_closed
288
302
 
@@ -294,6 +308,8 @@ class Connection
294
308
 
295
309
  # Gives the JDBC driver a hint of the number of rows to fetch from the database by a single interaction.
296
310
  # This is only a hint. It may have no effect at all.
311
+ # @param [Fixnum] fsz
312
+ # @return [NilClass]
297
313
  def set_fetch_size(fsz)
298
314
  check_closed
299
315
 
@@ -302,6 +318,7 @@ class Connection
302
318
  end
303
319
 
304
320
  # Closes the connection
321
+ # @return [NilClass]
305
322
  def close
306
323
  return if closed?
307
324
  @pstmts.each { | q, pstmt | pstmt.close }
@@ -311,19 +328,40 @@ class Connection
311
328
  end
312
329
 
313
330
  # Returns if this connection is closed or not
331
+ # @return [Boolean]
314
332
  def closed?
315
333
  @conn.nil?
316
334
  end
317
335
 
336
+ # @param [String] table_name Name of the table to be wrapped
337
+ # @return [JDBCHelper::TableWrapper]
338
+ def table table_name
339
+ JDBCHelper::TableWrapper.new self, table_name
340
+ end
341
+
342
+ # Statistics
343
+ class Stat
344
+ attr_accessor :type, :elapsed, :success_count, :fail_count
345
+
346
+ def initialize(t, e, s, f)
347
+ self.type = t
348
+ self.elapsed = e
349
+ self.success_count = s
350
+ self.fail_count = f
351
+ end
352
+ end
353
+
318
354
  private
319
355
  # Transaction object passed to the code block given to transaction method
320
356
  class Transaction
321
357
  # Commits the transaction
358
+ # @raise [JDBCHelper::Transaction::Commit]
322
359
  def commit
323
360
  @conn.commit
324
361
  raise Commit
325
362
  end
326
363
  # Rolls back this transaction
364
+ # @raise [JDBCHelper::Transaction::Rollback]
327
365
  def rollback
328
366
  @conn.rollback
329
367
  raise Rollback
@@ -374,7 +412,7 @@ private
374
412
  accum.fail_count += fail_count
375
413
  end
376
414
 
377
- def measure(type)
415
+ def measure_exec(type)
378
416
  begin
379
417
  st = Time.now
380
418
  ret = yield
@@ -7,6 +7,13 @@ class MySQLConnector
7
7
  include Constants
8
8
  include Constants::Connector
9
9
 
10
+ # @param [String] host
11
+ # @param [String] user
12
+ # @param [String] password
13
+ # @param [String] db
14
+ # @param [Fixnum] timeout
15
+ # @param [Hash] extra_params
16
+ # @return [JDBCHelper::Connection]
10
17
  def self.connect(host, user, password, db,
11
18
  timeout = DEFAULT_LOGIN_TIMEOUT,
12
19
  extra_params = DEFAULT_PARAMETERS[:mysql])
@@ -7,6 +7,12 @@ module OracleConnector
7
7
  include Constants
8
8
  include Constants::Connector
9
9
 
10
+ # @param [String] host
11
+ # @param [String] user
12
+ # @param [String] password
13
+ # @param [String] service_name
14
+ # @param [Fixnum] timeout
15
+ # @return [JDBCHelper::Connection]
10
16
  def self.connect(host, user, password, service_name, timeout = DEFAULT_LOGIN_TIMEOUT)
11
17
  Connection.new(
12
18
  :driver => JDBC_DRIVER[:oracle],
@@ -16,6 +22,12 @@ module OracleConnector
16
22
  :timeout => timeout)
17
23
  end
18
24
 
25
+ # @param [String] host
26
+ # @param [String] user
27
+ # @param [String] password
28
+ # @param [String] sid
29
+ # @param [Fixnum] timeout
30
+ # @return [JDBCHelper::Connection]
19
31
  def self.connect_by_sid(host, user, password, sid, timeout = DEFAULT_LOGIN_TIMEOUT)
20
32
  Connection.new(
21
33
  :driver => JDBC_DRIVER[:oracle],