View Javadoc

1   package org.apache.ojb.broker.accesslayer;
2   
3   /* Copyright 2003-2005 The Apache Software Foundation
4    *
5    * Licensed under the Apache License, Version 2.0 (the "License");
6    * you may not use this file except in compliance with the License.
7    * You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  import java.sql.CallableStatement;
19  import java.sql.PreparedStatement;
20  import java.sql.ResultSet;
21  import java.sql.SQLException;
22  import java.util.HashMap;
23  import java.util.Iterator;
24  import java.util.Map;
25  
26  import org.apache.ojb.broker.Identity;
27  import org.apache.ojb.broker.OptimisticLockException;
28  import org.apache.ojb.broker.PersistenceBroker;
29  import org.apache.ojb.broker.PersistenceBrokerException;
30  import org.apache.ojb.broker.PersistenceBrokerSQLException;
31  import org.apache.ojb.broker.accesslayer.sql.SelectStatement;
32  import org.apache.ojb.broker.core.ValueContainer;
33  import org.apache.ojb.broker.metadata.ArgumentDescriptor;
34  import org.apache.ojb.broker.metadata.ClassDescriptor;
35  import org.apache.ojb.broker.metadata.FieldDescriptor;
36  import org.apache.ojb.broker.metadata.JdbcType;
37  import org.apache.ojb.broker.metadata.ProcedureDescriptor;
38  import org.apache.ojb.broker.metadata.fieldaccess.PersistentField;
39  import org.apache.ojb.broker.platforms.Platform;
40  import org.apache.ojb.broker.query.Query;
41  import org.apache.ojb.broker.util.ExceptionHelper;
42  import org.apache.ojb.broker.util.logging.Logger;
43  import org.apache.ojb.broker.util.logging.LoggerFactory;
44  import org.apache.ojb.broker.util.sequence.SequenceManagerException;
45  
46  /**
47   * JdbcAccess is responsible for establishing performing
48   * SQL Queries against remote Databases.
49   * It hides all knowledge about JDBC from the BrokerImpl
50   *
51   * @author <a href="mailto:thma@apache.org">Thomas Mahler</a>
52   * @version $Id: JdbcAccessImpl.java,v 1.1 2007-08-24 22:17:30 ewestfal Exp $
53   */
54  public class JdbcAccessImpl implements JdbcAccess
55  {
56      /**
57       * The logger used.
58       */
59      protected Logger logger;
60  
61      /**
62       * The broker in use.
63       */
64      protected PersistenceBroker broker;
65  
66      /**
67       * constructor is private, use getInstance to get
68       * the singleton instance of this class
69       */
70      public JdbcAccessImpl(PersistenceBroker broker)
71      {
72          this.broker = broker;
73          logger = LoggerFactory.getLogger(this.getClass());
74      }
75  
76      /**
77       * Helper Platform accessor method
78       *
79       * @return Platform for the current broker connection manager.
80       */
81      private Platform getPlatform()
82      {
83          return this.broker.serviceConnectionManager().getSupportedPlatform();
84      }
85  
86      /**
87       * performs a DELETE operation against RDBMS.
88       * @param cld ClassDescriptor providing mapping information.
89       * @param obj The object to be deleted.
90       */
91      public void executeDelete(ClassDescriptor cld, Object obj) throws PersistenceBrokerException
92      {
93          if (logger.isDebugEnabled())
94          {
95              logger.debug("executeDelete: " + obj);
96          }
97  
98          final StatementManagerIF sm = broker.serviceStatementManager();
99          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 }