1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package net.wotonomy.jdbcadaptor;
19
20 import java.math.BigDecimal;
21 import java.sql.Blob;
22 import java.sql.CallableStatement;
23 import java.sql.ResultSet;
24 import java.sql.ResultSetMetaData;
25 import java.sql.SQLException;
26 import java.sql.Statement;
27
28 import net.wotonomy.access.EOAdaptorChannel;
29 import net.wotonomy.access.EOAttribute;
30 import net.wotonomy.access.EOEntity;
31 import net.wotonomy.access.EOGeneralAdaptorException;
32 import net.wotonomy.access.EOSQLExpression;
33 import net.wotonomy.access.EOStoredProcedure;
34 import net.wotonomy.control.EOFetchSpecification;
35 import net.wotonomy.control.EOQualifier;
36 import net.wotonomy.foundation.NSArray;
37 import net.wotonomy.foundation.NSData;
38 import net.wotonomy.foundation.NSDictionary;
39 import net.wotonomy.foundation.NSKeyValueCoding;
40 import net.wotonomy.foundation.NSMutableDictionary;
41 import net.wotonomy.foundation.NSTimestamp;
42
43 /***
44 * Concrete implementation of EOAdaptorChannel for use with JDBC.
45 *
46 * @author ezamudio@nasoft.com
47 * @author $Author: cgruber $
48 * @version $Revision: 903 $
49 */
50 public class JDBCChannel extends EOAdaptorChannel {
51
52 protected boolean _fetchInProgress;
53 protected ResultSet _resultSet;
54 protected Statement _statement;
55 protected NSArray _attsToFetch;
56 protected NSArray _resultAttributes;
57 protected boolean _transactionWasOpen;
58 protected NSDictionary _spReturnValues;
59 protected int _resultCount;
60
61 /***
62 * Creates a new JDBCChannel.
63 * @param context The JDBCContext this channel belongs to.
64 */
65 public JDBCChannel(JDBCContext context) {
66 super(context);
67 }
68
69 protected JDBCContext _context() {
70 return (JDBCContext)adaptorContext();
71 }
72
73
74
75
76 public void setAttributesToFetch(NSArray atts) {
77 _attsToFetch = atts;
78 }
79
80
81
82
83 public NSArray attributesToFetch() {
84 return _attsToFetch;
85 }
86
87
88
89
90 public void cancelFetch() {
91 if (_statement == null || _resultSet == null)
92 return;
93 try {
94 _resultSet.close();
95 _statement.cancel();
96 } catch (SQLException ex) {
97 throw new JDBCAdaptorException("Cannot cancel fetch in database.", ex);
98 }
99 }
100
101
102
103
104 public void closeChannel() {
105 if (_statement == null)
106 return;
107 try {
108 _statement.close();
109 } catch (SQLException ex) {
110 throw new JDBCAdaptorException("While trying to close the channel.", ex);
111 }
112 }
113
114
115
116
117
118
119 public NSArray describeResults() {
120 if (_resultSet == null || !_fetchInProgress)
121 throw new EOGeneralAdaptorException("Cannot describe results without a result set.");
122 if (_resultAttributes == null) {
123 try {
124 ResultSetMetaData _rsmeta = _resultSet.getMetaData();
125 EOAttribute[] attarr = new EOAttribute[_rsmeta.getColumnCount()];
126 for (int i = 1; i <= attarr.length; i++) {
127 EOAttribute a = new EOAttribute();
128 a.setName("Attribute " + (i));
129 a.setColumnName(_rsmeta.getColumnName(i));
130 a.setClassName(_rsmeta.getColumnClassName(i));
131 a.setExternalType(_rsmeta.getColumnTypeName(i));
132 a.setPrecision(_rsmeta.getPrecision(i));
133 a.setScale(_rsmeta.getScale(i));
134 a.setAllowsNull(_rsmeta.isNullable(i) == ResultSetMetaData.columnNullable);
135 a.setWidth(_rsmeta.getColumnDisplaySize(i));
136 a.setReadOnly(_rsmeta.isReadOnly(i));
137 attarr[i-1] = a;
138 }
139 _resultAttributes = new NSArray(attarr);
140 } catch (SQLException ex) {
141 throw new JDBCAdaptorException("While trying to get the result set metadata.", ex);
142 }
143 }
144 return _resultAttributes;
145 }
146
147
148
149
150
151 public int deleteRowsDescribedByQualifier(EOQualifier q, EOEntity entity) {
152 EOSQLExpression exp = adaptorContext().adaptor().expressionFactory().createExpression(entity);
153 exp.prepareDeleteExpressionForQualifier(q);
154 evaluateExpression(exp);
155 return _resultCount;
156 }
157
158
159
160
161
162
163
164 public void evaluateExpression(EOSQLExpression sql) {
165 if (!isOpen())
166 throw new EOGeneralAdaptorException("Attempt to evaluate expression without opening the channel first.");
167 try {
168 _statement = _context().connection().createStatement();
169 } catch (SQLException ex) {
170 throw new JDBCAdaptorException("Cannot create java.sql.Statement", ex);
171 }
172 _resultSet = null;
173 boolean isQuery = false;
174 String text = sql.statement();
175 try {
176
177 if (text.startsWith("INSERT") || text.startsWith("DELETE") || text.startsWith("UPDATE")) {
178 conditionalBeginTransaction();
179 _resultCount = _statement.executeUpdate(text);
180 conditionalCommitTransaction();
181 return;
182 } else if (text.startsWith("SELECT")) {
183
184 if (_resultCount > 0)
185 _statement.setMaxRows(_resultCount);
186 _resultSet = _statement.executeQuery(text);
187 _fetchInProgress = true;
188 return;
189 } else {
190 conditionalBeginTransaction();
191 isQuery = _statement.execute(text);
192 }
193 } catch (SQLException ex) {
194 throw new JDBCAdaptorException("While trying to execute expression '" + text + "'", ex);
195 }
196 try {
197 if (isQuery) {
198 if (_resultCount > 0)
199 _statement.setMaxRows(_resultCount);
200 _resultSet = _statement.getResultSet();
201 } else {
202 _resultCount = _statement.getUpdateCount();
203 conditionalCommitTransaction();
204 }
205 } catch (SQLException ex) {
206 throw new JDBCAdaptorException("While trying to get the result set.", ex);
207 }
208 }
209
210
211
212
213
214
215 public void executeStoredProcedure(
216 EOStoredProcedure proc, NSDictionary values) {
217 if (!isOpen())
218 throw new EOGeneralAdaptorException("Attempt to execute a stored procedure on a closed channel.");
219 conditionalBeginTransaction();
220 try {
221
222 StringBuffer buf = new StringBuffer("{ call ");
223 buf.append(proc.externalName());
224 NSArray args = proc.arguments();
225 if (args != null && args.count() > 0) {
226 buf.append("[");
227 for (int i = 0; i < args.count(); i++) {
228 EOAttribute a = (EOAttribute)args.objectAtIndex(i);
229 if (a.parameterDirection() != EOAttribute.OutParameter) {
230 buf.append('?');
231 buf.append(", ");
232 }
233 }
234 buf.delete(buf.length()-2, buf.length());
235 buf.append("]");
236 }
237 buf.append(" }");
238
239 CallableStatement sp = _context().connection().prepareCall(buf.toString());
240 if (args != null && args.count() > 0) {
241 int pos = 1;
242
243 for (int i = 0; i < args.count(); i++) {
244 EOAttribute a = (EOAttribute)args.objectAtIndex(i);
245 if (a.parameterDirection() != EOAttribute.OutParameter) {
246 Object val = values.objectForKey(a.name());
247 if (val == NSKeyValueCoding.NullValue)
248 sp.setNull(pos, 0);
249 if (val instanceof String)
250 sp.setString(pos, (String)val);
251 else if (val instanceof BigDecimal)
252 sp.setBigDecimal(pos, (BigDecimal)val);
253 else if (val instanceof NSTimestamp)
254 sp.setTimestamp(pos, (NSTimestamp)val);
255 else if (val instanceof NSData)
256 sp.setBytes(pos, ((NSData)val).bytes());
257 else if (val instanceof Integer)
258 sp.setInt(pos, ((Integer)val).intValue());
259 else if (val instanceof Long)
260 sp.setLong(pos, ((Long)val).longValue());
261 else
262 sp.setObject(pos, val);
263 pos++;
264 }
265 }
266 }
267
268 sp.execute();
269
270 if (args != null && args.count() > 0) {
271 int pos = 1;
272 NSMutableDictionary retvals = new NSMutableDictionary();
273 for (int i = 0; i < args.count(); i++) {
274 EOAttribute a = (EOAttribute)args.objectAtIndex(i);
275 if (a.parameterDirection() != EOAttribute.InParameter) {
276 Object val = sp.getObject(pos);
277 if (val == null)
278 retvals.setObjectForKey(NSKeyValueCoding.NullValue, a.name());
279 else if (val instanceof Blob) {
280 try {
281 retvals.setObjectForKey(new NSData(((Blob)val).getBinaryStream(), 1024), a.name());
282 } catch (java.io.IOException ex) {
283
284 retvals.setObjectForKey(NSData.EmptyData, a.name());
285 }
286 } else
287 retvals.setObjectForKey(val, a.name());
288 pos++;
289 }
290 }
291 _spReturnValues = retvals;
292 }
293 } catch (SQLException ex) {
294 throw new JDBCAdaptorException("While trying to execute stored procedure.", ex);
295 }
296 conditionalCommitTransaction();
297 }
298
299
300
301
302 public NSMutableDictionary fetchRow() {
303 if (_resultSet == null) {
304 return null;
305 }
306 if (attributesToFetch() == null)
307 throw new EOGeneralAdaptorException("Attempt to fetchRow without setting attributes to fetch first.");
308 try {
309
310 if (!_resultSet.next()) {
311 _resultSet.close();
312 _resultAttributes = null;
313 _fetchInProgress = _statement.getMoreResults();
314 if (_fetchInProgress)
315 _resultSet = _statement.getResultSet();
316 return null;
317 }
318 } catch (SQLException ex) {
319 throw new JDBCAdaptorException("While trying to fetch row.", ex);
320 }
321
322
323 NSMutableDictionary dict = new NSMutableDictionary(attributesToFetch().count());
324 try {
325 for (int i = 0; i < attributesToFetch().count(); i++) {
326 EOAttribute a = (EOAttribute)attributesToFetch().objectAtIndex(i);
327 Object o = _resultSet.getObject(i+1);
328 if (o == null)
329 o = NSKeyValueCoding.NullValue;
330 dict.setObjectForKey(o, a.name());
331 }
332 } catch (SQLException ex) {
333 throw new JDBCAdaptorException("While trying to create row.", ex);
334 }
335 return dict;
336 }
337
338
339
340
341 public void insertRow(NSDictionary row, EOEntity entity) {
342 EOSQLExpression exp = adaptorContext().adaptor().expressionFactory().createExpression(entity);
343 exp.prepareInsertExpressionWithRow(row);
344 evaluateExpression(exp);
345 }
346
347
348
349
350
351 public boolean isFetchInProgress() {
352 return _fetchInProgress;
353 }
354
355
356
357
358 public boolean isOpen() {
359 boolean open = (_context().connection() != null);
360 try {
361 open = open || !_context().connection().isClosed();
362 } catch (SQLException ex) {
363 open = false;
364 }
365 return open;
366 }
367
368
369
370
371
372
373 public void openChannel() {
374 try {
375 if (_context().connection() == null || _context().connection().isClosed())
376 _context().connect();
377 } catch (SQLException ex) {
378 throw new JDBCAdaptorException("Cannot open connection to database.", ex);
379 }
380 }
381
382
383
384
385 public NSDictionary returnValuesForLastStoredProcedureInvocation() {
386 return _spReturnValues;
387 }
388
389
390
391
392
393
394 public void selectAttributes(
395 NSArray atts, EOFetchSpecification fspec,
396 boolean lock, EOEntity entity) {
397 _resultAttributes = atts;
398 EOSQLExpression expr = adaptorContext().adaptor().expressionFactory().createExpression(entity);
399 _fetchInProgress = true;
400 expr.prepareSelectExpressionWithAttributes(atts, lock, fspec);
401
402 if (fspec != null)
403 _resultCount = fspec.fetchLimit();
404 evaluateExpression(expr);
405 }
406
407
408
409
410 public int updateValuesInRowsDescribedByQualifier(
411 NSDictionary row, EOQualifier q, EOEntity entity) {
412 EOSQLExpression exp = adaptorContext().adaptor().expressionFactory().createExpression(entity);
413 exp.prepareUpdateExpressionWithRow(row, q);
414 evaluateExpression(exp);
415 return _resultCount;
416 }
417
418 protected void conditionalBeginTransaction() {
419 _transactionWasOpen = adaptorContext().hasOpenTransaction();
420 if (!_transactionWasOpen)
421 adaptorContext().beginTransaction();
422 }
423
424 protected void conditionalCommitTransaction() {
425 if (!_transactionWasOpen)
426 adaptorContext().commitTransaction();
427 _transactionWasOpen = false;
428 }
429
430 }
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449