001 package org.apache.ojb.broker.accesslayer;
002
003 /* Copyright 2003-2005 The Apache Software Foundation
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018 import java.sql.CallableStatement;
019 import java.sql.PreparedStatement;
020 import java.sql.ResultSet;
021 import java.sql.SQLException;
022 import java.util.HashMap;
023 import java.util.Iterator;
024 import java.util.Map;
025
026 import org.apache.ojb.broker.Identity;
027 import org.apache.ojb.broker.OptimisticLockException;
028 import org.apache.ojb.broker.PersistenceBroker;
029 import org.apache.ojb.broker.PersistenceBrokerException;
030 import org.apache.ojb.broker.PersistenceBrokerSQLException;
031 import org.apache.ojb.broker.accesslayer.sql.SelectStatement;
032 import org.apache.ojb.broker.core.ValueContainer;
033 import org.apache.ojb.broker.metadata.ArgumentDescriptor;
034 import org.apache.ojb.broker.metadata.ClassDescriptor;
035 import org.apache.ojb.broker.metadata.FieldDescriptor;
036 import org.apache.ojb.broker.metadata.JdbcType;
037 import org.apache.ojb.broker.metadata.ProcedureDescriptor;
038 import org.apache.ojb.broker.metadata.fieldaccess.PersistentField;
039 import org.apache.ojb.broker.platforms.Platform;
040 import org.apache.ojb.broker.query.Query;
041 import org.apache.ojb.broker.util.ExceptionHelper;
042 import org.apache.ojb.broker.util.logging.Logger;
043 import org.apache.ojb.broker.util.logging.LoggerFactory;
044 import org.apache.ojb.broker.util.sequence.SequenceManagerException;
045
046 /**
047 * JdbcAccess is responsible for establishing performing
048 * SQL Queries against remote Databases.
049 * It hides all knowledge about JDBC from the BrokerImpl
050 *
051 * @author <a href="mailto:thma@apache.org">Thomas Mahler</a>
052 * @version $Id: JdbcAccessImpl.java,v 1.1 2007-08-24 22:17:30 ewestfal Exp $
053 */
054 public class JdbcAccessImpl implements JdbcAccess
055 {
056 /**
057 * The logger used.
058 */
059 protected Logger logger;
060
061 /**
062 * The broker in use.
063 */
064 protected PersistenceBroker broker;
065
066 /**
067 * constructor is private, use getInstance to get
068 * the singleton instance of this class
069 */
070 public JdbcAccessImpl(PersistenceBroker broker)
071 {
072 this.broker = broker;
073 logger = LoggerFactory.getLogger(this.getClass());
074 }
075
076 /**
077 * Helper Platform accessor method
078 *
079 * @return Platform for the current broker connection manager.
080 */
081 private Platform getPlatform()
082 {
083 return this.broker.serviceConnectionManager().getSupportedPlatform();
084 }
085
086 /**
087 * performs a DELETE operation against RDBMS.
088 * @param cld ClassDescriptor providing mapping information.
089 * @param obj The object to be deleted.
090 */
091 public void executeDelete(ClassDescriptor cld, Object obj) throws PersistenceBrokerException
092 {
093 if (logger.isDebugEnabled())
094 {
095 logger.debug("executeDelete: " + obj);
096 }
097
098 final StatementManagerIF sm = broker.serviceStatementManager();
099 PreparedStatement stmt = null;
100 try
101 {
102 stmt = sm.getDeleteStatement(cld);
103 if (stmt == null)
104 {
105 logger.error("getDeleteStatement returned a null statement");
106 throw new PersistenceBrokerException("JdbcAccessImpl: getDeleteStatement returned a null statement");
107 }
108
109 sm.bindDelete(stmt, cld, obj);
110 if (logger.isDebugEnabled())
111 logger.debug("executeDelete: " + stmt);
112
113 // @todo: clearify semantics
114 // thma: the following check is not secure. The object could be deleted *or* changed.
115 // if it was deleted it makes no sense to throw an OL exception.
116 // does is make sense to throw an OL exception if the object was changed?
117 if (stmt.executeUpdate() == 0 && cld.isLocking()) //BRJ
118 {
119 /**
120 * Kuali Foundation modification -- 6/19/2009
121 */
122 String objToString = "";
123 try {
124 objToString = obj.toString();
125 } catch (Exception ex) {}
126 throw new OptimisticLockException("Object has been modified or deleted by someone else: " + objToString, obj);
127 /**
128 * End of Kuali Foundation modification
129 */
130 }
131
132 // Harvest any return values.
133 harvestReturnValues(cld.getDeleteProcedure(), obj, stmt);
134 }
135 catch (OptimisticLockException e)
136 {
137 // Don't log as error
138 if (logger.isDebugEnabled())
139 logger.debug("OptimisticLockException during the execution of delete: "
140 + e.getMessage(), e);
141 throw e;
142 }
143 catch (PersistenceBrokerException e)
144 {
145 logger.error("PersistenceBrokerException during the execution of delete: "
146 + e.getMessage(), e);
147 throw e;
148 }
149 catch (SQLException e)
150 {
151 final String sql = broker.serviceSqlGenerator().getPreparedDeleteStatement(cld).getStatement();
152 throw ExceptionHelper.generateException(e, sql, cld, logger, obj);
153 }
154 finally
155 {
156 sm.closeResources(stmt, null);
157 }
158 }
159
160 /**
161 * Performs a DELETE operation based on the given {@link Query} against RDBMS.
162 * @param query the query string.
163 * @param cld ClassDescriptor providing JDBC information.
164 */
165 public void executeDelete(Query query, ClassDescriptor cld) throws PersistenceBrokerException
166 {
167 if (logger.isDebugEnabled())
168 {
169 logger.debug("executeDelete (by Query): " + query);
170 }
171 final StatementManagerIF sm = broker.serviceStatementManager();
172 PreparedStatement stmt = null;
173 final String sql = this.broker.serviceSqlGenerator().getPreparedDeleteStatement(query, cld).getStatement();
174 try
175 {
176 stmt = sm.getPreparedStatement(cld, sql,
177 false, StatementManagerIF.FETCH_SIZE_NOT_APPLICABLE, cld.getDeleteProcedure()!=null);
178
179 sm.bindStatement(stmt, query, cld, 1);
180 if (logger.isDebugEnabled())
181 logger.debug("executeDelete (by Query): " + stmt);
182
183 stmt.executeUpdate();
184 }
185 catch (SQLException e)
186 {
187 throw ExceptionHelper.generateException(e, sql, cld, null, logger);
188 }
189 finally
190 {
191 sm.closeResources(stmt, null);
192 }
193 }
194
195 /**
196 * performs an INSERT operation against RDBMS.
197 * @param obj The Object to be inserted as a row of the underlying table.
198 * @param cld ClassDescriptor providing mapping information.
199 */
200 public void executeInsert(ClassDescriptor cld, Object obj) throws PersistenceBrokerException
201 {
202 if (logger.isDebugEnabled())
203 {
204 logger.debug("executeInsert: " + obj);
205 }
206 final StatementManagerIF sm = broker.serviceStatementManager();
207 PreparedStatement stmt = null;
208 try
209 {
210 stmt = sm.getInsertStatement(cld);
211 if (stmt == null)
212 {
213 logger.error("getInsertStatement returned a null statement");
214 throw new PersistenceBrokerException("getInsertStatement returned a null statement");
215 }
216 // before bind values perform autoincrement sequence columns
217 assignAutoincrementSequences(cld, obj);
218 sm.bindInsert(stmt, cld, obj);
219 if (logger.isDebugEnabled())
220 logger.debug("executeInsert: " + stmt);
221 stmt.executeUpdate();
222 // after insert read and assign identity columns
223 assignAutoincrementIdentityColumns(cld, obj);
224
225 // Harvest any return values.
226 harvestReturnValues(cld.getInsertProcedure(), obj, stmt);
227 }
228 catch (PersistenceBrokerException e)
229 {
230 logger.error("PersistenceBrokerException during the execution of the insert: " + e.getMessage(), e);
231 throw e;
232 }
233 catch(SequenceManagerException e)
234 {
235 throw new PersistenceBrokerException("Error while try to assign identity value", e);
236 }
237 catch (SQLException e)
238 {
239 final String sql = broker.serviceSqlGenerator().getPreparedInsertStatement(cld).getStatement();
240 throw ExceptionHelper.generateException(e, sql, cld, logger, obj);
241 }
242 finally
243 {
244 sm.closeResources(stmt, null);
245 }
246 }
247
248 /**
249 * performs a SELECT operation against RDBMS.
250 * @param query the query string.
251 * @param cld ClassDescriptor providing JDBC information.
252 */
253 public ResultSetAndStatement executeQuery(Query query, ClassDescriptor cld) throws PersistenceBrokerException
254 {
255 if (logger.isDebugEnabled())
256 {
257 logger.debug("executeQuery: " + query);
258 }
259 /*
260 * MBAIRD: we should create a scrollable resultset if the start at
261 * index or end at index is set
262 */
263 boolean scrollable = ((query.getStartAtIndex() > Query.NO_START_AT_INDEX) || (query.getEndAtIndex() > Query.NO_END_AT_INDEX));
264 /*
265 * OR if the prefetching of relationships is being used.
266 */
267 if (query != null && query.getPrefetchedRelationships() != null && !query.getPrefetchedRelationships().isEmpty())
268 {
269 scrollable = true;
270 }
271 final StatementManagerIF sm = broker.serviceStatementManager();
272 final SelectStatement sql = broker.serviceSqlGenerator().getPreparedSelectStatement(query, cld);
273 PreparedStatement stmt = null;
274 ResultSet rs = null;
275 try
276 {
277 final int queryFetchSize = query.getFetchSize();
278 final boolean isStoredProcedure = isStoredProcedure(sql.getStatement());
279 stmt = sm.getPreparedStatement(cld, sql.getStatement() ,
280 scrollable, queryFetchSize, isStoredProcedure);
281 if (isStoredProcedure)
282 {
283 // Query implemented as a stored procedure, which must return a result set.
284 // Query sytax is: { ?= call PROCEDURE_NAME(?,...,?)}
285 getPlatform().registerOutResultSet((CallableStatement) stmt, 1);
286 sm.bindStatement(stmt, query, cld, 2);
287
288 if (logger.isDebugEnabled())
289 logger.debug("executeQuery: " + stmt);
290
291 stmt.execute();
292 rs = (ResultSet) ((CallableStatement) stmt).getObject(1);
293 }
294 else
295 {
296 sm.bindStatement(stmt, query, cld, 1);
297
298 if (logger.isDebugEnabled())
299 logger.debug("executeQuery: " + stmt);
300
301 rs = stmt.executeQuery();
302 }
303
304 return new ResultSetAndStatement(sm, stmt, rs, sql);
305 }
306 catch (PersistenceBrokerException e)
307 {
308 // release resources on exception
309 sm.closeResources(stmt, rs);
310 logger.error("PersistenceBrokerException during the execution of the query: " + e.getMessage(), e);
311 throw e;
312 }
313 catch (SQLException e)
314 {
315 // release resources on exception
316 sm.closeResources(stmt, rs);
317 throw ExceptionHelper.generateException(e, sql.getStatement(), null, logger, null);
318 }
319 }
320
321 public ResultSetAndStatement executeSQL(
322 String sqlStatement,
323 ClassDescriptor cld,
324 boolean scrollable)
325 throws PersistenceBrokerException
326 {
327 return executeSQL(sqlStatement, cld, null, scrollable);
328 }
329
330 /**
331 * performs a SQL SELECT statement against RDBMS.
332 * @param sql the query string.
333 * @param cld ClassDescriptor providing meta-information.
334 */
335 public ResultSetAndStatement executeSQL(
336 final String sql,
337 ClassDescriptor cld,
338 ValueContainer[] values,
339 boolean scrollable)
340 throws PersistenceBrokerException
341 {
342 if (logger.isDebugEnabled()) logger.debug("executeSQL: " + sql);
343 final boolean isStoredprocedure = isStoredProcedure(sql);
344 final StatementManagerIF sm = broker.serviceStatementManager();
345 PreparedStatement stmt = null;
346 ResultSet rs = null;
347 try
348 {
349 stmt = sm.getPreparedStatement(cld, sql,
350 scrollable, StatementManagerIF.FETCH_SIZE_NOT_EXPLICITLY_SET, isStoredprocedure);
351 if (isStoredprocedure)
352 {
353 // Query implemented as a stored procedure, which must return a result set.
354 // Query sytax is: { ?= call PROCEDURE_NAME(?,...,?)}
355 getPlatform().registerOutResultSet((CallableStatement) stmt, 1);
356 sm.bindValues(stmt, values, 2);
357 stmt.execute();
358 rs = (ResultSet) ((CallableStatement) stmt).getObject(1);
359 }
360 else
361 {
362 sm.bindValues(stmt, values, 1);
363 rs = stmt.executeQuery();
364 }
365
366 // as we return the resultset for further operations, we cannot release the statement yet.
367 // that has to be done by the JdbcAccess-clients (i.e. RsIterator, ProxyRsIterator and PkEnumeration.)
368 return new ResultSetAndStatement(sm, stmt, rs, new SelectStatement()
369 {
370 public Query getQueryInstance()
371 {
372 return null;
373 }
374
375 public int getColumnIndex(FieldDescriptor fld)
376 {
377 return JdbcType.MIN_INT;
378 }
379
380 public String getStatement()
381 {
382 return sql;
383 }
384 });
385 }
386 catch (PersistenceBrokerException e)
387 {
388 // release resources on exception
389 sm.closeResources(stmt, rs);
390 logger.error("PersistenceBrokerException during the execution of the SQL query: " + e.getMessage(), e);
391 throw e;
392 }
393 catch (SQLException e)
394 {
395 // release resources on exception
396 sm.closeResources(stmt, rs);
397 throw ExceptionHelper.generateException(e, sql, cld, values, logger, null);
398 }
399 }
400
401 public int executeUpdateSQL(String sqlStatement, ClassDescriptor cld)
402 throws PersistenceBrokerException
403 {
404 return executeUpdateSQL(sqlStatement, cld, null, null);
405 }
406
407 /**
408 * performs a SQL UPDTE, INSERT or DELETE statement against RDBMS.
409 * @param sqlStatement the query string.
410 * @param cld ClassDescriptor providing meta-information.
411 * @return int returncode
412 */
413 public int executeUpdateSQL(
414 String sqlStatement,
415 ClassDescriptor cld,
416 ValueContainer[] values1,
417 ValueContainer[] values2)
418 throws PersistenceBrokerException
419 {
420 if (logger.isDebugEnabled())
421 logger.debug("executeUpdateSQL: " + sqlStatement);
422
423 int result;
424 int index;
425 PreparedStatement stmt = null;
426 final StatementManagerIF sm = broker.serviceStatementManager();
427 try
428 {
429 stmt = sm.getPreparedStatement(cld, sqlStatement,
430 Query.NOT_SCROLLABLE, StatementManagerIF.FETCH_SIZE_NOT_APPLICABLE, isStoredProcedure(sqlStatement));
431 index = sm.bindValues(stmt, values1, 1);
432 sm.bindValues(stmt, values2, index);
433 result = stmt.executeUpdate();
434 }
435 catch (PersistenceBrokerException e)
436 {
437 logger.error("PersistenceBrokerException during the execution of the Update SQL query: " + e.getMessage(), e);
438 throw e;
439 }
440 catch (SQLException e)
441 {
442 ValueContainer[] tmp = addValues(values1, values2);
443 throw ExceptionHelper.generateException(e, sqlStatement, cld, tmp, logger, null);
444 }
445 finally
446 {
447 sm.closeResources(stmt, null);
448 }
449 return result;
450 }
451
452 /** Helper method, returns the addition of both arrays (add source to target array) */
453 private ValueContainer[] addValues(ValueContainer[] target, ValueContainer[] source)
454 {
455 ValueContainer[] newArray;
456 if(source != null && source.length > 0)
457 {
458 if(target != null)
459 {
460 newArray = new ValueContainer[target.length + source.length];
461 System.arraycopy(target, 0, newArray, 0, target.length);
462 System.arraycopy(source, 0, newArray, target.length, source.length);
463 }
464 else
465 {
466 newArray = source;
467 }
468 }
469 else
470 {
471 newArray = target;
472 }
473 return newArray;
474 }
475
476 /**
477 * performs an UPDATE operation against RDBMS.
478 * @param obj The Object to be updated in the underlying table.
479 * @param cld ClassDescriptor providing mapping information.
480 */
481 public void executeUpdate(ClassDescriptor cld, Object obj) throws PersistenceBrokerException
482 {
483 if (logger.isDebugEnabled())
484 {
485 logger.debug("executeUpdate: " + obj);
486 }
487
488 // obj with nothing but key fields is not updated
489 if (cld.getNonPkRwFields().length == 0)
490 {
491 return;
492 }
493
494 final StatementManagerIF sm = broker.serviceStatementManager();
495 PreparedStatement stmt = null;
496 // BRJ: preserve current locking values
497 // locking values will be restored in case of exception
498 ValueContainer[] oldLockingValues;
499 oldLockingValues = cld.getCurrentLockingValues(obj);
500 try
501 {
502 stmt = sm.getUpdateStatement(cld);
503 if (stmt == null)
504 {
505 logger.error("getUpdateStatement returned a null statement");
506 throw new PersistenceBrokerException("getUpdateStatement returned a null statement");
507 }
508
509 sm.bindUpdate(stmt, cld, obj);
510 if (logger.isDebugEnabled())
511 logger.debug("executeUpdate: " + stmt);
512
513 if ((stmt.executeUpdate() == 0) && cld.isLocking()) //BRJ
514 {
515 /**
516 * Kuali Foundation modification -- 6/19/2009
517 */
518 String objToString = "";
519 try {
520 objToString = obj.toString();
521 } catch (Exception ex) {}
522 throw new OptimisticLockException("Object has been modified by someone else: " + objToString, obj);
523 /**
524 * End of Kuali Foundation modification
525 */
526 }
527
528 // Harvest any return values.
529 harvestReturnValues(cld.getUpdateProcedure(), obj, stmt);
530 }
531 catch (OptimisticLockException e)
532 {
533 // Don't log as error
534 if (logger.isDebugEnabled())
535 logger.debug(
536 "OptimisticLockException during the execution of update: " + e.getMessage(),
537 e);
538 throw e;
539 }
540 catch (PersistenceBrokerException e)
541 {
542 // BRJ: restore old locking values
543 setLockingValues(cld, obj, oldLockingValues);
544
545 logger.error(
546 "PersistenceBrokerException during the execution of the update: " + e.getMessage(),
547 e);
548 throw e;
549 }
550 catch (SQLException e)
551 {
552 final String sql = broker.serviceSqlGenerator().getPreparedUpdateStatement(cld).getStatement();
553 throw ExceptionHelper.generateException(e, sql, cld, logger, obj);
554 }
555 finally
556 {
557 sm.closeResources(stmt, null);
558 }
559 }
560
561 /**
562 * performs a primary key lookup operation against RDBMS and materializes
563 * an object from the resulting row. Only skalar attributes are filled from
564 * the row, references are not resolved.
565 * @param oid contains the primary key info.
566 * @param cld ClassDescriptor providing mapping information.
567 * @return the materialized object, null if no matching row was found or if
568 * any error occured.
569 */
570 public Object materializeObject(ClassDescriptor cld, Identity oid)
571 throws PersistenceBrokerException
572 {
573 final StatementManagerIF sm = broker.serviceStatementManager();
574 final SelectStatement sql = broker.serviceSqlGenerator().getPreparedSelectByPkStatement(cld);
575 Object result = null;
576 PreparedStatement stmt = null;
577 ResultSet rs = null;
578 try
579 {
580 stmt = sm.getSelectByPKStatement(cld);
581 if (stmt == null)
582 {
583 logger.error("getSelectByPKStatement returned a null statement");
584 throw new PersistenceBrokerException("getSelectByPKStatement returned a null statement");
585 }
586 /*
587 arminw: currently a select by PK could never be a stored procedure,
588 thus we can always set 'false'. Is this correct??
589 */
590 sm.bindSelect(stmt, oid, cld, false);
591 rs = stmt.executeQuery();
592 // data available read object, else return null
593 ResultSetAndStatement rs_stmt = new ResultSetAndStatement(broker.serviceStatementManager(), stmt, rs, sql);
594 if (rs.next())
595 {
596 Map row = new HashMap();
597 cld.getRowReader().readObjectArrayFrom(rs_stmt, row);
598 result = cld.getRowReader().readObjectFrom(row);
599 }
600 // close resources
601 rs_stmt.close();
602 }
603 catch (PersistenceBrokerException e)
604 {
605 // release resources on exception
606 sm.closeResources(stmt, rs);
607 logger.error("PersistenceBrokerException during the execution of materializeObject: " + e.getMessage(), e);
608 throw e;
609 }
610 catch (SQLException e)
611 {
612 // release resources on exception
613 sm.closeResources(stmt, rs);
614 throw ExceptionHelper.generateException(e, sql.getStatement(), cld, logger, null);
615 }
616 return result;
617 }
618
619 /**
620 * Set the locking values
621 * @param cld
622 * @param obj
623 * @param oldLockingValues
624 */
625 private void setLockingValues(ClassDescriptor cld, Object obj, ValueContainer[] oldLockingValues)
626 {
627 FieldDescriptor fields[] = cld.getLockingFields();
628
629 for (int i=0; i<fields.length; i++)
630 {
631 PersistentField field = fields[i].getPersistentField();
632 Object lockVal = oldLockingValues[i].getValue();
633
634 field.set(obj, lockVal);
635 }
636 }
637
638 /**
639 * Harvest any values that may have been returned during the execution
640 * of a procedure.
641 *
642 * @param proc the procedure descriptor that provides info about the procedure
643 * that was invoked.
644 * @param obj the object that was persisted
645 * @param stmt the statement that was used to persist the object.
646 *
647 * @throws PersistenceBrokerSQLException if a problem occurs.
648 */
649 private void harvestReturnValues(
650 ProcedureDescriptor proc,
651 Object obj,
652 PreparedStatement stmt)
653 throws PersistenceBrokerSQLException
654 {
655 // If the procedure descriptor is null or has no return values or
656 // if the statement is not a callable statment, then we're done.
657 if ((proc == null) || (!proc.hasReturnValues()))
658 {
659 return;
660 }
661
662 // Set up the callable statement
663 CallableStatement callable = (CallableStatement) stmt;
664
665 // This is the index that we'll use to harvest the return value(s).
666 int index = 0;
667
668 // If the proc has a return value, then try to harvest it.
669 if (proc.hasReturnValue())
670 {
671
672 // Increment the index
673 index++;
674
675 // Harvest the value.
676 this.harvestReturnValue(obj, callable, proc.getReturnValueFieldRef(), index);
677 }
678
679 // Check each argument. If it's returned by the procedure,
680 // then harvest the value.
681 Iterator iter = proc.getArguments().iterator();
682 while (iter.hasNext())
683 {
684 index++;
685 ArgumentDescriptor arg = (ArgumentDescriptor) iter.next();
686 if (arg.getIsReturnedByProcedure())
687 {
688 this.harvestReturnValue(obj, callable, arg.getFieldRef(), index);
689 }
690 }
691 }
692
693 /**
694 * Harvest a single value that was returned by a callable statement.
695 *
696 * @param obj the object that will receive the value that is harvested.
697 * @param callable the CallableStatement that contains the value to harvest
698 * @param fmd the FieldDescriptor that identifies the field where the
699 * harvested value will be stord.
700 * @param index the parameter index.
701 *
702 * @throws PersistenceBrokerSQLException if a problem occurs.
703 */
704 private void harvestReturnValue(
705 Object obj,
706 CallableStatement callable,
707 FieldDescriptor fmd,
708 int index)
709 throws PersistenceBrokerSQLException
710 {
711
712 try
713 {
714 // If we have a field descriptor, then we can harvest
715 // the return value.
716 if ((callable != null) && (fmd != null) && (obj != null))
717 {
718 // Get the value and convert it to it's appropriate
719 // java type.
720 Object value = fmd.getJdbcType().getObjectFromColumn(callable, index);
721
722 // Set the value of the persistent field.
723 fmd.getPersistentField().set(obj, fmd.getFieldConversion().sqlToJava(value));
724
725 }
726 }
727 catch (SQLException e)
728 {
729 String msg = "SQLException during the execution of harvestReturnValue"
730 + " class="
731 + obj.getClass().getName()
732 + ","
733 + " field="
734 + fmd.getAttributeName()
735 + " : "
736 + e.getMessage();
737 logger.error(msg,e);
738 throw new PersistenceBrokerSQLException(msg, e);
739 }
740 }
741
742 /**
743 * Check if the specified sql-string is a stored procedure
744 * or not.
745 * @param sql The sql query to check
746 * @return <em>True</em> if the query is a stored procedure, else <em>false</em> is returned.
747 */
748 protected boolean isStoredProcedure(String sql)
749 {
750 /*
751 Stored procedures start with
752 {?= call <procedure-name>[<arg1>,<arg2>, ...]}
753 or
754 {call <procedure-name>[<arg1>,<arg2>, ...]}
755 but also statements with white space like
756 { ?= call <procedure-name>[<arg1>,<arg2>, ...]}
757 are possible.
758 */
759 int k = 0, i = 0;
760 char c;
761 while(k < 3 && i < sql.length())
762 {
763 c = sql.charAt(i);
764 if(c != ' ')
765 {
766 switch (k)
767 {
768 case 0:
769 if(c != '{') return false;
770 break;
771 case 1:
772 if(c != '?' && c != 'c') return false;
773 break;
774 case 2:
775 if(c != '=' && c != 'a') return false;
776 break;
777 }
778 k++;
779 }
780 i++;
781 }
782 return true;
783 }
784
785 protected void assignAutoincrementSequences(ClassDescriptor cld, Object target) throws SequenceManagerException
786 {
787 // TODO: refactor auto-increment handling, auto-increment should only be supported by PK fields?
788 // FieldDescriptor[] fields = cld.getPkFields();
789 FieldDescriptor[] fields = cld.getFieldDescriptor(false);
790 FieldDescriptor field;
791 for(int i = 0; i < fields.length; i++)
792 {
793 field = fields[i];
794 if(field.isAutoIncrement() && !field.isAccessReadOnly())
795 {
796 Object value = field.getPersistentField().get(target);
797 if(broker.serviceBrokerHelper().representsNull(field, value))
798 {
799 Object id = broker.serviceSequenceManager().getUniqueValue(field);
800 field.getPersistentField().set(target, id);
801 }
802 }
803 }
804 }
805
806 protected void assignAutoincrementIdentityColumns(ClassDescriptor cld, Object target) throws SequenceManagerException
807 {
808 // if database Identity Columns are used, query the id from database
809 // other SequenceManager implementations will ignore this call
810 if(cld.useIdentityColumnField()) broker.serviceSequenceManager().afterStore(this, cld, target);
811 }
812 }