001    package org.apache.ojb.broker.accesslayer;
002    
003    /* Copyright 2002-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.sql.Statement;
023    import java.sql.Types;
024    import java.util.Collection;
025    import java.util.Enumeration;
026    import java.util.Iterator;
027    
028    import org.apache.ojb.broker.Identity;
029    import org.apache.ojb.broker.PersistenceBroker;
030    import org.apache.ojb.broker.PersistenceBrokerException;
031    import org.apache.ojb.broker.PersistenceBrokerSQLException;
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.ProcedureDescriptor;
037    import org.apache.ojb.broker.platforms.Platform;
038    import org.apache.ojb.broker.platforms.PlatformException;
039    import org.apache.ojb.broker.platforms.PlatformFactory;
040    import org.apache.ojb.broker.query.BetweenCriteria;
041    import org.apache.ojb.broker.query.Criteria;
042    import org.apache.ojb.broker.query.ExistsCriteria;
043    import org.apache.ojb.broker.query.FieldCriteria;
044    import org.apache.ojb.broker.query.InCriteria;
045    import org.apache.ojb.broker.query.NullCriteria;
046    import org.apache.ojb.broker.query.Query;
047    import org.apache.ojb.broker.query.SelectionCriteria;
048    import org.apache.ojb.broker.query.SqlCriteria;
049    import org.apache.ojb.broker.util.logging.Logger;
050    import org.apache.ojb.broker.util.logging.LoggerFactory;
051    
052    /**
053     * manages JDBC Connection and Statement resources.
054     *
055     * @author Thomas Mahler
056     * @author <a href="mailto:rburt3@mchsi.com">Randall Burt</a>
057     * @version $Id: StatementManager.java,v 1.1 2007-08-24 22:17:30 ewestfal Exp $
058     */
059    public class StatementManager implements StatementManagerIF
060    {
061        private Logger m_log = LoggerFactory.getLogger(StatementManager.class);
062    
063        /** the associated broker */
064        private final PersistenceBroker m_broker;
065        private Platform m_platform;
066        /**
067         * Used when OJB run in JBoss
068         * TODO: Find a better solution to handle OJB within JBoss
069         * --> the JCA implementation should solve this problem
070         *
071         * arminw:
072         * Seems with JBoss 3.2.2 or higher the problem is gone, so we
073         * can deprecate this attribute sooner or later
074         */
075        private boolean m_eagerRelease;
076        private ConnectionManagerIF m_conMan;
077    
078        public StatementManager(final PersistenceBroker pBroker)
079        {
080            this.m_broker = pBroker;
081            this.m_conMan = m_broker.serviceConnectionManager();
082            m_eagerRelease = m_conMan.getConnectionDescriptor().getEagerRelease();
083            m_platform = PlatformFactory.getPlatformFor(m_conMan.getConnectionDescriptor());
084        }
085    
086        public void closeResources(Statement stmt, ResultSet rs)
087        {
088            if (m_log.isDebugEnabled())
089                m_log.debug("closeResources was called");
090            try
091            {
092                m_platform.beforeStatementClose(stmt, rs);
093                //close statement on wrapped statement class, or real statement
094                if (stmt != null)
095                {
096                    //log.info("## close: "+stmt);
097                    stmt.close();
098    
099                    /*
100                    *********************************************
101                    special stuff for OJB within JBoss
102                    ********************************************
103                    */
104                    if (m_eagerRelease)
105                    {
106                        m_conMan.releaseConnection();
107                    }
108    
109                }
110                m_platform.afterStatementClose(stmt, rs);
111            }
112            catch (PlatformException e)
113            {
114                m_log.error("Platform dependent operation failed", e);
115            }
116            catch (SQLException ignored)
117            {
118                if (m_log.isDebugEnabled())
119                    m_log.debug("Statement closing failed", ignored);
120            }
121        }
122    
123        /**
124         * binds the Identities Primary key values to the statement
125         */
126        public void bindDelete(PreparedStatement stmt, Identity oid, ClassDescriptor cld) throws SQLException
127        {
128            Object[] pkValues = oid.getPrimaryKeyValues();
129            FieldDescriptor[] pkFields = cld.getPkFields();
130            int i = 0;
131            try
132            {
133                for (; i < pkValues.length; i++)
134                {
135                    setObjectForStatement(stmt, i + 1, pkValues[i], pkFields[i].getJdbcType().getType());
136                }
137            }
138            catch (SQLException e)
139            {
140                m_log.error("bindDelete failed for: " + oid.toString() + ", while set value '" +
141                        pkValues[i] + "' for column " + pkFields[i].getColumnName());
142                throw e;
143            }
144        }
145    
146        /**
147         * binds the objects primary key and locking values to the statement, BRJ
148         */
149        public void bindDelete(PreparedStatement stmt, ClassDescriptor cld, Object obj) throws SQLException
150        {
151            if (cld.getDeleteProcedure() != null)
152            {
153                this.bindProcedure(stmt, cld, obj, cld.getDeleteProcedure());
154            }
155            else
156            {
157                int index = 1;
158                ValueContainer[] values, currentLockingValues;
159    
160                currentLockingValues = cld.getCurrentLockingValues(obj);
161                // parameters for WHERE-clause pk
162                values = getKeyValues(m_broker, cld, obj);
163                for (int i = 0; i < values.length; i++)
164                {
165                    setObjectForStatement(stmt, index, values[i].getValue(), values[i].getJdbcType().getType());
166                    index++;
167                }
168    
169                // parameters for WHERE-clause locking
170                values = currentLockingValues;
171                for (int i = 0; i < values.length; i++)
172                {
173                    setObjectForStatement(stmt, index, values[i].getValue(), values[i].getJdbcType().getType());
174                    index++;
175                }
176            }
177        }
178    
179        /**
180         * bind attribute and value
181         * @param stmt
182         * @param index
183         * @param attributeOrQuery
184         * @param value
185         * @param cld
186         * @return
187         * @throws SQLException
188         */
189        private int bindStatementValue(PreparedStatement stmt, int index, Object attributeOrQuery, Object value, ClassDescriptor cld)
190                throws SQLException
191        {
192            FieldDescriptor fld = null;
193            // if value is a subQuery bind it
194            if (value instanceof Query)
195            {
196                Query subQuery = (Query) value;
197                return bindStatement(stmt, subQuery, cld.getRepository().getDescriptorFor(subQuery.getSearchClass()), index);
198            }
199    
200            // if attribute is a subQuery bind it
201            if (attributeOrQuery instanceof Query)
202            {
203                Query subQuery = (Query) attributeOrQuery;
204                bindStatement(stmt, subQuery, cld.getRepository().getDescriptorFor(subQuery.getSearchClass()), index);
205            }
206            else
207            {
208                fld = cld.getFieldDescriptorForPath((String) attributeOrQuery);
209            }
210    
211            if (fld != null)
212            {
213                // BRJ: use field conversions and platform
214                if (value != null)
215                {
216                    m_platform.setObjectForStatement(stmt, index, fld.getFieldConversion().javaToSql(value), fld.getJdbcType().getType());
217                }
218                else
219                {
220                    m_platform.setNullForStatement(stmt, index, fld.getJdbcType().getType());
221                }
222            }
223            else
224            {
225                if (value != null)
226                {
227                    stmt.setObject(index, value);
228                }
229                else
230                {
231                    stmt.setNull(index, Types.NULL);
232                }
233            }
234    
235            return ++index; // increment before return
236        }
237    
238        /**
239         * bind SelectionCriteria
240         * @param stmt the PreparedStatement
241         * @param index the position of the parameter to bind
242         * @param crit the Criteria containing the parameter
243         * @param cld the ClassDescriptor
244         * @return next index for PreparedStatement
245         */
246        private int bindStatement(PreparedStatement stmt, int index, SelectionCriteria crit, ClassDescriptor cld) throws SQLException
247        {
248            return bindStatementValue(stmt, index, crit.getAttribute(), crit.getValue(), cld);
249        }
250    
251        /**
252         * bind NullCriteria
253         * @param stmt the PreparedStatement
254         * @param index the position of the parameter to bind
255         * @param crit the Criteria containing the parameter
256         * @return next index for PreparedStatement
257         */
258        private int bindStatement(PreparedStatement stmt, int index, NullCriteria crit)
259        {
260            return index;
261        }
262    
263        /**
264         * bind FieldCriteria
265         * @param stmt , the PreparedStatement
266         * @param index , the position of the parameter to bind
267         * @param crit , the Criteria containing the parameter
268         * @return next index for PreparedStatement
269         */
270        private int bindStatement(PreparedStatement stmt, int index, FieldCriteria crit)
271        {
272            return index;
273        }
274    
275        /**
276         * bind SqlCriteria
277         * @param stmt the PreparedStatement
278         * @param index the position of the parameter to bind
279         * @param crit the Criteria containing the parameter
280         * @return next index for PreparedStatement
281         */
282        private int bindStatement(PreparedStatement stmt, int index, SqlCriteria crit)
283        {
284            return index;
285        }
286    
287        /**
288         * bind BetweenCriteria
289         * @param stmt the PreparedStatement
290         * @param index the position of the parameter to bind
291         * @param crit the Criteria containing the parameter
292         * @param cld the ClassDescriptor
293         * @return next index for PreparedStatement
294         */
295        private int bindStatement(PreparedStatement stmt, int index, BetweenCriteria crit, ClassDescriptor cld) throws SQLException
296        {
297            index = bindStatementValue(stmt, index, crit.getAttribute(), crit.getValue(), cld);
298    
299            return bindStatementValue(stmt, index, crit.getAttribute(), crit.getValue2(), cld);
300        }
301    
302        /**
303         * bind InCriteria
304         * @param stmt the PreparedStatement
305         * @param index the position of the parameter to bind
306         * @param crit the Criteria containing the parameter
307         * @param cld the ClassDescriptor
308         * @return next index for PreparedStatement
309         */
310        private int bindStatement(PreparedStatement stmt, int index, InCriteria crit, ClassDescriptor cld) throws SQLException
311        {
312            if (crit.getValue() instanceof Collection)
313            {
314                Collection values = (Collection) crit.getValue();
315                Iterator iter = values.iterator();
316    
317                while (iter.hasNext())
318                {
319                    index = bindStatementValue(stmt, index, crit.getAttribute(), iter.next(), cld);
320                }
321            }
322            else
323            {
324                index = bindStatementValue(stmt, index, crit.getAttribute(), crit.getValue(), cld);
325            }
326            return index;
327        }
328    
329        /**
330         * bind ExistsCriteria
331         * @param stmt the PreparedStatement
332         * @param index the position of the parameter to bind
333         * @param crit the Criteria containing the parameter
334         * @param cld the ClassDescriptor
335         * @return next index for PreparedStatement
336         */
337        private int bindStatement(PreparedStatement stmt, int index, ExistsCriteria crit, ClassDescriptor cld) throws SQLException
338        {
339            Query subQuery = (Query) crit.getValue();
340    
341            // if query has criteria, bind them
342            if (subQuery.getCriteria() != null && !subQuery.getCriteria().isEmpty())
343            {
344                return bindStatement(stmt, subQuery.getCriteria(), cld.getRepository().getDescriptorFor(subQuery.getSearchClass()), index);
345    
346                // otherwise, just ignore it
347            }
348            else
349            {
350                return index;
351            }
352        }
353    
354        /**
355         * bind a Query based Select Statement
356         */
357        public int bindStatement(PreparedStatement stmt, Query query, ClassDescriptor cld, int param) throws SQLException
358        {
359            int result;
360    
361            result = bindStatement(stmt, query.getCriteria(), cld, param);
362            result = bindStatement(stmt, query.getHavingCriteria(), cld, result);
363    
364            return result;
365        }
366    
367        /**
368         * bind a Query based Select Statement
369         */
370        protected int bindStatement(PreparedStatement stmt, Criteria crit, ClassDescriptor cld, int param) throws SQLException
371        {
372            if (crit != null)
373            {
374                Enumeration e = crit.getElements();
375    
376                while (e.hasMoreElements())
377                {
378                    Object o = e.nextElement();
379                    if (o instanceof Criteria)
380                    {
381                        Criteria pc = (Criteria) o;
382                        param = bindStatement(stmt, pc, cld, param);
383                    }
384                    else
385                    {
386                        SelectionCriteria c = (SelectionCriteria) o;
387                        // BRJ : bind once for the criterion's main class
388                        param = bindSelectionCriteria(stmt, param, c, cld);
389    
390                        // BRJ : and once for each extent
391                        for (int i = 0; i < c.getNumberOfExtentsToBind(); i++)
392                        {
393                            param = bindSelectionCriteria(stmt, param, c, cld);
394                        }
395                    }
396                }
397            }
398            return param;
399        }
400    
401        /**
402         * bind SelectionCriteria
403         * @param stmt the PreparedStatement
404         * @param index the position of the parameter to bind
405         * @param crit the Criteria containing the parameter
406         * @param cld the ClassDescriptor
407         * @return next index for PreparedStatement
408         */
409        private int bindSelectionCriteria(PreparedStatement stmt, int index, SelectionCriteria crit, ClassDescriptor cld) throws SQLException
410        {
411            if (crit instanceof NullCriteria)
412                index = bindStatement(stmt, index, (NullCriteria) crit);
413            else if (crit instanceof BetweenCriteria)
414                index = bindStatement(stmt, index, (BetweenCriteria) crit, cld);
415            else if (crit instanceof InCriteria)
416                index = bindStatement(stmt, index, (InCriteria) crit, cld);
417            else if (crit instanceof SqlCriteria)
418                index = bindStatement(stmt, index, (SqlCriteria) crit);
419            else if (crit instanceof FieldCriteria)
420                index = bindStatement(stmt, index, (FieldCriteria) crit);
421            else if (crit instanceof ExistsCriteria)
422                index = bindStatement(stmt, index, (ExistsCriteria) crit, cld);
423            else
424                index = bindStatement(stmt, index, crit, cld);
425    
426            return index;
427        }
428    
429        /**
430         * binds the values of the object obj to the statements parameters
431         */
432        public void bindInsert(PreparedStatement stmt, ClassDescriptor cld, Object obj) throws java.sql.SQLException
433        {
434            ValueContainer[] values;
435            cld.updateLockingValues(obj); // BRJ : provide useful defaults for locking fields
436    
437            if (cld.getInsertProcedure() != null)
438            {
439                this.bindProcedure(stmt, cld, obj, cld.getInsertProcedure());
440            }
441            else
442            {
443                values = getAllValues(cld, obj);
444                for (int i = 0; i < values.length; i++)
445                {
446                    setObjectForStatement(stmt, i + 1, values[i].getValue(), values[i].getJdbcType().getType());
447                }
448            }
449        }
450    
451        /**
452         * Binds the Identities Primary key values to the statement.
453         */
454        public void bindSelect(PreparedStatement stmt, Identity oid, ClassDescriptor cld, boolean callableStmt) throws SQLException
455        {
456            ValueContainer[] values = null;
457            int i = 0;
458            int j = 0;
459    
460            if (cld == null)
461            {
462                cld = m_broker.getClassDescriptor(oid.getObjectsRealClass());
463            }
464            try
465            {
466                if(callableStmt)
467                {
468                    // First argument is the result set
469                    m_platform.registerOutResultSet((CallableStatement) stmt, 1);
470                    j++;
471                }
472    
473                values = getKeyValues(m_broker, cld, oid);
474                for (/*void*/; i < values.length; i++, j++)
475                {
476                    setObjectForStatement(stmt, j + 1, values[i].getValue(), values[i].getJdbcType().getType());
477                }
478            }
479            catch (SQLException e)
480            {
481                m_log.error("bindSelect failed for: " + oid.toString() + ", PK: " + i + ", value: " + values[i]);
482                throw e;
483            }
484        }
485    
486        /**
487         * binds the values of the object obj to the statements parameters
488         */
489        public void bindUpdate(PreparedStatement stmt, ClassDescriptor cld, Object obj) throws java.sql.SQLException
490        {
491            if (cld.getUpdateProcedure() != null)
492            {
493                this.bindProcedure(stmt, cld, obj, cld.getUpdateProcedure());
494            }
495            else
496            {
497                int index = 1;
498                ValueContainer[] values, valuesSnapshot;
499                // first take a snapshot of current locking values
500                valuesSnapshot = cld.getCurrentLockingValues(obj);
501                cld.updateLockingValues(obj); // BRJ
502                values = getNonKeyValues(m_broker, cld, obj);
503    
504                // parameters for SET-clause
505                for (int i = 0; i < values.length; i++)
506                {
507                    setObjectForStatement(stmt, index, values[i].getValue(), values[i].getJdbcType().getType());
508                    index++;
509                }
510                // parameters for WHERE-clause pk
511                values = getKeyValues(m_broker, cld, obj);
512                for (int i = 0; i < values.length; i++)
513                {
514                    setObjectForStatement(stmt, index, values[i].getValue(), values[i].getJdbcType().getType());
515                    index++;
516                }
517                // parameters for WHERE-clause locking
518                // take old locking values
519                values = valuesSnapshot;
520                for (int i = 0; i < values.length; i++)
521                {
522                    setObjectForStatement(stmt, index, values[i].getValue(), values[i].getJdbcType().getType());
523                    index++;
524                }
525            }
526        }
527    
528        /**
529         * binds the given array of values (if not null) starting from the given
530         * parameter index
531         * @return the next parameter index
532         */
533        public int bindValues(PreparedStatement stmt, ValueContainer[] values, int index) throws SQLException
534        {
535            if (values != null)
536            {
537                for (int i = 0; i < values.length; i++)
538                {
539                    setObjectForStatement(stmt, index, values[i].getValue(), values[i].getJdbcType().getType());
540                    index++;
541                }
542            }
543            return index;
544        }
545    
546        /**
547         * return a prepared DELETE Statement fitting for the given ClassDescriptor
548         */
549        public PreparedStatement getDeleteStatement(ClassDescriptor cld) throws PersistenceBrokerSQLException, PersistenceBrokerException
550        {
551            try
552            {
553                return cld.getStatementsForClass(m_conMan).getDeleteStmt(m_conMan.getConnection());
554            }
555            catch (SQLException e)
556            {
557                throw new PersistenceBrokerSQLException("Could not build statement ask for", e);
558            }
559            catch (LookupException e)
560            {
561                throw new PersistenceBrokerException("Used ConnectionManager instance could not obtain a connection", e);
562            }
563        }
564    
565        /**
566         * return a generic Statement for the given ClassDescriptor.
567         * Never use this method for UPDATE/INSERT/DELETE if you want to use the batch mode.
568         */
569        public Statement getGenericStatement(ClassDescriptor cds, boolean scrollable) throws PersistenceBrokerException
570        {
571            try
572            {
573                return cds.getStatementsForClass(m_conMan).getGenericStmt(m_conMan.getConnection(), scrollable);
574            }
575            catch (LookupException e)
576            {
577                throw new PersistenceBrokerException("Used ConnectionManager instance could not obtain a connection", e);
578            }
579        }
580    
581        /**
582         * return a prepared Insert Statement fitting for the given ClassDescriptor
583         */
584        public PreparedStatement getInsertStatement(ClassDescriptor cds) throws PersistenceBrokerSQLException, PersistenceBrokerException
585        {
586            try
587            {
588                return cds.getStatementsForClass(m_conMan).getInsertStmt(m_conMan.getConnection());
589            }
590            catch (SQLException e)
591            {
592                throw new PersistenceBrokerSQLException("Could not build statement ask for", e);
593            }
594            catch (LookupException e)
595            {
596                throw new PersistenceBrokerException("Used ConnectionManager instance could not obtain a connection", e);
597            }
598        }
599    
600        /**
601         * return a generic Statement for the given ClassDescriptor
602         */
603        public PreparedStatement getPreparedStatement(ClassDescriptor cds, String sql,
604                                                      boolean scrollable, int explicitFetchSizeHint, boolean callableStmt)
605                throws PersistenceBrokerException
606        {
607            try
608            {
609                return cds.getStatementsForClass(m_conMan).getPreparedStmt(m_conMan.getConnection(), sql, scrollable, explicitFetchSizeHint, callableStmt);
610            }
611            catch (LookupException e)
612            {
613                throw new PersistenceBrokerException("Used ConnectionManager instance could not obtain a connection", e);
614            }
615        }
616    
617        /**
618         * return a prepared Select Statement for the given ClassDescriptor
619         */
620        public PreparedStatement getSelectByPKStatement(ClassDescriptor cds) throws PersistenceBrokerSQLException, PersistenceBrokerException
621        {
622            try
623            {
624                return cds.getStatementsForClass(m_conMan).getSelectByPKStmt(m_conMan.getConnection());
625            }
626            catch (SQLException e)
627            {
628                throw new PersistenceBrokerSQLException("Could not build statement ask for", e);
629            }
630            catch (LookupException e)
631            {
632                throw new PersistenceBrokerException("Used ConnectionManager instance could not obtain a connection", e);
633            }
634        }
635    
636        /**
637         * return a prepared Update Statement fitting to the given ClassDescriptor
638         */
639        public PreparedStatement getUpdateStatement(ClassDescriptor cds) throws PersistenceBrokerSQLException, PersistenceBrokerException
640        {
641            try
642            {
643                return cds.getStatementsForClass(m_conMan).getUpdateStmt(m_conMan.getConnection());
644            }
645            catch (SQLException e)
646            {
647                throw new PersistenceBrokerSQLException("Could not build statement ask for", e);
648            }
649            catch (LookupException e)
650            {
651                throw new PersistenceBrokerException("Used ConnectionManager instance could not obtain a connection", e);
652            }
653        }
654    
655        /**
656         * returns an array containing values for all the Objects attribute
657         * @throws PersistenceBrokerException if there is an erros accessing obj field values
658         */
659        protected ValueContainer[] getAllValues(ClassDescriptor cld, Object obj) throws PersistenceBrokerException
660        {
661            return m_broker.serviceBrokerHelper().getAllRwValues(cld, obj);
662        }
663    
664        /**
665         * returns an Array with an Objects PK VALUES
666         * @throws PersistenceBrokerException if there is an erros accessing o field values
667         */
668        protected ValueContainer[] getKeyValues(PersistenceBroker broker, ClassDescriptor cld, Object obj) throws PersistenceBrokerException
669        {
670            return broker.serviceBrokerHelper().getKeyValues(cld, obj);
671        }
672    
673        /**
674         * returns an Array with an Identities PK VALUES
675         * @throws PersistenceBrokerException if there is an erros accessing o field values
676         */
677        protected ValueContainer[] getKeyValues(PersistenceBroker broker, ClassDescriptor cld, Identity oid) throws PersistenceBrokerException
678        {
679            return broker.serviceBrokerHelper().getKeyValues(cld, oid);
680        }
681    
682        /**
683         * returns an Array with an Objects NON-PK VALUES
684         * @throws PersistenceBrokerException if there is an erros accessing o field values
685         */
686        protected ValueContainer[] getNonKeyValues(PersistenceBroker broker, ClassDescriptor cld, Object obj) throws PersistenceBrokerException
687        {
688            return broker.serviceBrokerHelper().getNonKeyRwValues(cld, obj);
689        }
690    
691        /**
692         * Bind a prepared statment that represents a call to a procedure or
693         * user-defined function.
694         *
695         * @param stmt the statement to bind.
696         * @param cld the class descriptor of the object that triggered the
697         *        invocation of the procedure or user-defined function.
698         * @param obj the object that triggered the invocation of the procedure
699         *        or user-defined function.
700         * @param proc the procedure descriptor that provides information about
701         *        the arguments that shoudl be passed to the procedure or
702         *        user-defined function
703         */
704        private void bindProcedure(PreparedStatement stmt, ClassDescriptor cld, Object obj, ProcedureDescriptor proc)
705                throws SQLException
706        {
707            int valueSub = 0;
708    
709            // Figure out if we are using a callable statement.  If we are, then we
710            // will need to register one or more output parameters.
711            CallableStatement callable = null;
712            try
713            {
714                callable = (CallableStatement) stmt;
715            }
716            catch(Exception e)
717            {
718                m_log.error("Error while bind values for class '" + (cld != null ? cld.getClassNameOfObject() : null)
719                        + "', using stored procedure: "+ proc, e);
720                if(e instanceof SQLException)
721                {
722                    throw (SQLException) e;
723                }
724                else
725                {
726                    throw new PersistenceBrokerException("Unexpected error while bind values for class '"
727                            + (cld != null ? cld.getClassNameOfObject() : null) + "', using stored procedure: "+ proc);
728                }
729            }
730    
731            // If we have a return value, then register it.
732            if ((proc.hasReturnValue()) && (callable != null))
733            {
734                int jdbcType = proc.getReturnValueFieldRef().getJdbcType().getType();
735                m_platform.setNullForStatement(stmt, valueSub + 1, jdbcType);
736                callable.registerOutParameter(valueSub + 1, jdbcType);
737                valueSub++;
738            }
739    
740            // Process all of the arguments.
741            Iterator iterator = proc.getArguments().iterator();
742            while (iterator.hasNext())
743            {
744                ArgumentDescriptor arg = (ArgumentDescriptor) iterator.next();
745                Object val = arg.getValue(obj);
746                int jdbcType = arg.getJdbcType();
747                setObjectForStatement(stmt, valueSub + 1, val, jdbcType);
748                if ((arg.getIsReturnedByProcedure()) && (callable != null))
749                {
750                    callable.registerOutParameter(valueSub + 1, jdbcType);
751                }
752                valueSub++;
753            }
754        }
755    
756        /**
757         * Sets object for statement at specific index, adhering to platform- and null-rules.
758         * @param stmt the statement
759         * @param index the current parameter index
760         * @param value the value to set
761         * @param sqlType the JDBC SQL-type of the value
762         * @throws SQLException on platform error
763         */
764        private void setObjectForStatement(PreparedStatement stmt, int index, Object value, int sqlType)
765                throws SQLException
766        {
767            if (value == null)
768            {
769                m_platform.setNullForStatement(stmt, index, sqlType);
770            }
771            else
772            {
773                m_platform.setObjectForStatement(stmt, index, value, sqlType);
774            }
775        }
776    
777    }