jdbc-helper 0.1.3 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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],