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    }