Coverage Report - org.apache.ojb.broker.accesslayer.JdbcAccessImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
JdbcAccessImpl
N/A
N/A
6.091
JdbcAccessImpl$1
N/A
N/A
6.091
 
 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  
 }