Coverage Report - org.apache.ojb.broker.accesslayer.RowReaderDefaultImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
RowReaderDefaultImpl
N/A
N/A
3.545
 
 1  
 package org.apache.ojb.broker.accesslayer;
 2  
 
 3  
 /* Copyright 2002-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.lang.reflect.Method;
 19  
 import java.sql.ResultSet;
 20  
 import java.sql.SQLException;
 21  
 import java.util.Map;
 22  
 
 23  
 import org.apache.ojb.broker.PersistenceBrokerException;
 24  
 import org.apache.ojb.broker.metadata.ClassDescriptor;
 25  
 import org.apache.ojb.broker.metadata.FieldDescriptor;
 26  
 import org.apache.ojb.broker.util.ClassHelper;
 27  
 import org.apache.ojb.broker.util.SqlHelper;
 28  
 
 29  
 /**
 30  
  * Default implementation of the {@link RowReader} interface.
 31  
  *
 32  
  * @version $Id: RowReaderDefaultImpl.java,v 1.1 2007-08-24 22:17:30 ewestfal Exp $
 33  
  */
 34  
 
 35  
 public class RowReaderDefaultImpl implements RowReader
 36  
 {
 37  
     /**
 38  
      * Used as key in result set row map.
 39  
      */
 40  
     private static final String OJB_CONCRETE_CLASS_KEY = "ojbTemporaryNoneColumnKey";
 41  
     /**
 42  
      * represents a zero sized parameter array
 43  
      */
 44  
     private static final Object[] NO_ARGS = {};
 45  
 
 46  
     private ClassDescriptor m_cld;
 47  
 
 48  
     public RowReaderDefaultImpl(ClassDescriptor cld)
 49  
     {
 50  
         this.m_cld = cld;
 51  
     }
 52  
 
 53  
     /**
 54  
      * materialize a single object, described by cld,
 55  
      * from the first row of the ResultSet rs.
 56  
      * There are two possible strategies:
 57  
      * 1. The persistent class defines a public constructor with arguments matching the persistent
 58  
      * primitive attributes of the class. In this case we build an array args of arguments from rs
 59  
      * and call Constructor.newInstance(args) to build an object.
 60  
      * 2. The persistent class does not provide such a constructor, but only a public default
 61  
      * constructor. In this case we create an empty instance with Class.newInstance().
 62  
      * This empty instance is then filled by calling Field::set(obj,getObject(matchingColumn))
 63  
      * for each attribute.
 64  
      * The second strategy needs n calls to Field::set() which are much more expensive
 65  
      * than the filling of the args array in the first strategy.
 66  
      * client applications should therefore define adequate constructors to benefit from
 67  
      * performance gain of the first strategy.
 68  
      *
 69  
      * MBAIRD: The rowreader is told what type of object to materialize, so we have to trust
 70  
      * it is asked for the right type. It is possible someone marked an extent in the repository,
 71  
      * but not in java, or vice versa and this could cause problems in what is returned.
 72  
      *
 73  
      * we *have* to be able to materialize an object from a row that has a objConcreteClass, as we
 74  
      * retrieve ALL rows belonging to that table. The objects using the rowReader will make sure they
 75  
      * know what they are asking for, so we don't have to make sure a descriptor is assignable from the
 76  
      * selectClassDescriptor. This allows us to map both inherited classes and unrelated classes to the
 77  
      * same table.
 78  
      *
 79  
      */
 80  
     public Object readObjectFrom(Map row) throws PersistenceBrokerException
 81  
     {
 82  
         // allow to select a specific classdescriptor
 83  
         ClassDescriptor cld = selectClassDescriptor(row);
 84  
         return buildOrRefreshObject(row, cld, null);
 85  
     }
 86  
 
 87  
     /**
 88  
      * @see org.apache.ojb.broker.accesslayer.RowReader#refreshObject(Object, Map)
 89  
      */
 90  
     public void refreshObject(Object instance, Map row)
 91  
     {
 92  
         // 1. select target ClassDescriptor
 93  
         ClassDescriptor targetClassDescriptor = selectClassDescriptor(row);
 94  
         // 2. fill all scalar attributes of the existing object
 95  
         buildOrRefreshObject(row, targetClassDescriptor, instance);
 96  
     }
 97  
 
 98  
     /**
 99  
      * Creates an object instance according to clb, and fills its fileds width data provided by row.
 100  
      * @param row A {@link Map} contain the Object/Row mapping for the object.
 101  
      * @param targetClassDescriptor If the "ojbConcreteClass" feature was used, the target
 102  
      * {@link org.apache.ojb.broker.metadata.ClassDescriptor} could differ from the descriptor
 103  
      * this class was associated - see {@link #selectClassDescriptor}.
 104  
      * @param targetObject If 'null' a new object instance is build, else fields of object will
 105  
      * be refreshed.
 106  
      * @throws PersistenceBrokerException if there ewas an error creating the new object
 107  
      */
 108  
     protected Object buildOrRefreshObject(Map row, ClassDescriptor targetClassDescriptor, Object targetObject)
 109  
     {
 110  
         Object result = targetObject;
 111  
         FieldDescriptor fmd;
 112  
         FieldDescriptor[] fields = targetClassDescriptor.getFieldDescriptor(true);
 113  
 
 114  
         if(targetObject == null)
 115  
         {
 116  
             // 1. create new object instance if needed
 117  
             result = ClassHelper.buildNewObjectInstance(targetClassDescriptor);
 118  
         }
 119  
 
 120  
         // 2. fill all scalar attributes of the new object
 121  
         for (int i = 0; i < fields.length; i++)
 122  
         {
 123  
             fmd = fields[i];
 124  
             fmd.getPersistentField().set(result, row.get(fmd.getColumnName()));
 125  
         }
 126  
 
 127  
         if(targetObject == null)
 128  
         {
 129  
             // 3. for new build objects, invoke the initialization method for the class if one is provided
 130  
             Method initializationMethod = targetClassDescriptor.getInitializationMethod();
 131  
             if (initializationMethod != null)
 132  
             {
 133  
                 try
 134  
                 {
 135  
                     initializationMethod.invoke(result, NO_ARGS);
 136  
                 }
 137  
                 catch (Exception ex)
 138  
                 {
 139  
                     throw new PersistenceBrokerException("Unable to invoke initialization method:" + initializationMethod.getName() + " for class:" + m_cld.getClassOfObject(), ex);
 140  
                 }
 141  
             }
 142  
         }
 143  
         return result;
 144  
     }
 145  
 
 146  
     /**
 147  
      * materialize a single object, described by cld,
 148  
      * from the first row of the ResultSet rs.
 149  
      * There are two possible strategies:
 150  
      * 1. The persistent class defines a public constructor with arguments matching the persistent
 151  
      * primitive attributes of the class. In this case we build an array args of arguments from rs
 152  
      * and call Constructor.newInstance(args) to build an object.
 153  
      * 2. The persistent class does not provide such a constructor, but only a public default
 154  
      * constructor. In this case we create an empty instance with Class.newInstance().
 155  
      * This empty instance is then filled by calling Field::set(obj,getObject(matchingColumn))
 156  
      * for each attribute.
 157  
      * The second strategy needs n calls to Field::set() which are much more expensive
 158  
      * than the filling of the args array in the first strategy.
 159  
      * client applications should therefore define adequate constructors to benefit from
 160  
      * performance gain of the first strategy.
 161  
      *
 162  
      * @throws PersistenceBrokerException if there is an error accessing the access layer
 163  
      */
 164  
     public void readObjectArrayFrom(ResultSetAndStatement rs_stmt, Map row)
 165  
     {
 166  
         FieldDescriptor[] fields;
 167  
 /*
 168  
 arminw:
 169  
 TODO: this feature doesn't work, so remove this in future
 170  
 */
 171  
         if (m_cld.getSuperClass() != null)
 172  
         {
 173  
             /**
 174  
              * treeder
 175  
              * append super class fields if exist
 176  
              */
 177  
             fields = m_cld.getFieldDescriptorsInHeirarchy();
 178  
         }
 179  
         else
 180  
         {
 181  
             String ojbConcreteClass = extractOjbConcreteClass(m_cld, rs_stmt.m_rs, row);
 182  
             /*
 183  
             arminw:
 184  
             if multiple classes were mapped to the same table, lookup the concrete
 185  
             class and use these fields, attach ojbConcreteClass in row map for later use
 186  
             */
 187  
             if(ojbConcreteClass != null)
 188  
             {
 189  
                 ClassDescriptor cld = m_cld.getRepository().getDescriptorFor(ojbConcreteClass);
 190  
                 row.put(OJB_CONCRETE_CLASS_KEY, cld.getClassOfObject());
 191  
                 fields = cld.getFieldDescriptor(true);
 192  
             }
 193  
             else
 194  
             {
 195  
                 String ojbClass = SqlHelper.getOjbClassName(rs_stmt.m_rs);
 196  
                 if (ojbClass != null)
 197  
                 {
 198  
                     ClassDescriptor cld = m_cld.getRepository().getDescriptorFor(ojbClass);
 199  
                     row.put(OJB_CONCRETE_CLASS_KEY, cld.getClassOfObject());
 200  
                     fields = cld.getFieldDescriptor(true);
 201  
                 }
 202  
                 else
 203  
                 {
 204  
                     fields = m_cld.getFieldDescriptor(true);
 205  
                 }           
 206  
             }         
 207  
         }
 208  
         readValuesFrom(rs_stmt, row, fields);
 209  
     }
 210  
 
 211  
     /*
 212  
      * @see RowReader#readPkValuesFrom(ResultSet, ClassDescriptor, Map)
 213  
      * @throws PersistenceBrokerException if there is an error accessing the access layer
 214  
      */
 215  
     public void readPkValuesFrom(ResultSetAndStatement rs_stmt, Map row)
 216  
     {
 217  
         String ojbClass = SqlHelper.getOjbClassName(rs_stmt.m_rs);
 218  
         ClassDescriptor cld;
 219  
         
 220  
         if (ojbClass != null)
 221  
         {
 222  
             cld = m_cld.getRepository().getDescriptorFor(ojbClass);
 223  
         }
 224  
         else
 225  
         {
 226  
             cld = m_cld;
 227  
         }
 228  
 
 229  
         FieldDescriptor[] pkFields = cld.getPkFields();
 230  
         readValuesFrom(rs_stmt, row, pkFields);
 231  
     }
 232  
 
 233  
     protected void readValuesFrom(ResultSetAndStatement rs_stmt, Map row, FieldDescriptor[] fields)
 234  
     {
 235  
         int size = fields.length;
 236  
         Object val;
 237  
         FieldDescriptor fld = null;
 238  
         try
 239  
         {
 240  
             for (int j = 0; j < size; j++)
 241  
             {
 242  
                 fld = fields[j];
 243  
                 if(!row.containsKey(fld.getColumnName()))
 244  
                 {
 245  
                     int idx = rs_stmt.m_sql.getColumnIndex(fld);
 246  
                     val = fld.getJdbcType().getObjectFromColumn(rs_stmt.m_rs, null, fld.getColumnName(), idx);
 247  
                     row.put(fld.getColumnName(), fld.getFieldConversion().sqlToJava(val));
 248  
                 }
 249  
             }
 250  
         }
 251  
         catch (SQLException t)
 252  
         {
 253  
             throw new PersistenceBrokerException("Error reading class '"
 254  
                     + (fld != null ? fld.getClassDescriptor().getClassNameOfObject() : m_cld.getClassNameOfObject())
 255  
                     + "' from result set, current read field was '"
 256  
                     + (fld != null ? fld.getPersistentField().getName() + "'" : null), t);
 257  
         }
 258  
     }
 259  
 
 260  
     protected String extractOjbConcreteClass(ClassDescriptor cld, ResultSet rs, Map row)
 261  
     {
 262  
         FieldDescriptor fld = m_cld.getOjbConcreteClassField();
 263  
         if (fld == null)
 264  
         {
 265  
             return null;
 266  
         }
 267  
         try
 268  
         {
 269  
             Object tmp = fld.getJdbcType().getObjectFromColumn(rs, fld.getColumnName());
 270  
             // allow field-conversion for discriminator column too
 271  
             String result = (String) fld.getFieldConversion().sqlToJava(tmp);
 272  
             result = result != null ? result.trim() : null;
 273  
             if (result == null || result.length() == 0)
 274  
             {
 275  
                 throw new PersistenceBrokerException(
 276  
                         "ojbConcreteClass field for class " + cld.getClassNameOfObject()
 277  
                         + " returned null or 0-length string");
 278  
             }
 279  
             else
 280  
             {
 281  
                 /*
 282  
                 arminw: Make sure that we don't read discriminator field twice from the ResultSet.
 283  
                 */
 284  
                 row.put(fld.getColumnName(), result);
 285  
                 return result;
 286  
             }
 287  
         }
 288  
         catch(SQLException e)
 289  
         {
 290  
             throw new PersistenceBrokerException("Unexpected error while try to read 'ojbConcretClass'" +
 291  
                     " field from result set using column name " + fld.getColumnName() + " main class" +
 292  
                     " was " + m_cld.getClassNameOfObject(), e);
 293  
         }
 294  
     }
 295  
 
 296  
     /**
 297  
      * Check if there is an attribute which tells us which concrete class is to be instantiated.
 298  
      */
 299  
     protected ClassDescriptor selectClassDescriptor(Map row) throws PersistenceBrokerException
 300  
     {
 301  
         ClassDescriptor result = m_cld;
 302  
         Class ojbConcreteClass = (Class) row.get(OJB_CONCRETE_CLASS_KEY);
 303  
         if(ojbConcreteClass != null)
 304  
         {
 305  
             result = m_cld.getRepository().getDescriptorFor(ojbConcreteClass);
 306  
             // if we can't find class-descriptor for concrete class, something wrong with mapping
 307  
             if (result == null)
 308  
             {
 309  
                 throw new PersistenceBrokerException("Can't find class-descriptor for ojbConcreteClass '"
 310  
                         + ojbConcreteClass + "', the main class was " + m_cld.getClassNameOfObject());
 311  
             }
 312  
         }
 313  
         return result;
 314  
     }
 315  
 
 316  
     public void setClassDescriptor(ClassDescriptor cld)
 317  
     {
 318  
         this.m_cld = cld;
 319  
     }
 320  
 
 321  
     public ClassDescriptor getClassDescriptor()
 322  
     {
 323  
         return m_cld;
 324  
     }
 325  
 }