KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > ojb > broker > accesslayer > RowReaderDefaultImpl


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 JavaDoc;
19 import java.sql.ResultSet JavaDoc;
20 import java.sql.SQLException JavaDoc;
21 import java.util.Map JavaDoc;
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.30.2.11 2005/12/21 22:22:58 tomdz 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 JavaDoc OJB_CONCRETE_CLASS_KEY = "ojbTemporaryNoneColumnKey";
41     /**
42      * represents a zero sized parameter array
43      */

44     private static final Object JavaDoc[] 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 JavaDoc readObjectFrom(Map JavaDoc 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 JavaDoc instance, Map JavaDoc 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 JavaDoc buildOrRefreshObject(Map JavaDoc row, ClassDescriptor targetClassDescriptor, Object JavaDoc targetObject)
109     {
110         Object JavaDoc 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 JavaDoc initializationMethod = targetClassDescriptor.getInitializationMethod();
131             if (initializationMethod != null)
132             {
133                 try
134                 {
135                     initializationMethod.invoke(result, NO_ARGS);
136                 }
137                 catch (Exception JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc row)
216     {
217         String JavaDoc 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 JavaDoc row, FieldDescriptor[] fields)
234     {
235         int size = fields.length;
236         Object JavaDoc 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 JavaDoc 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 JavaDoc extractOjbConcreteClass(ClassDescriptor cld, ResultSet JavaDoc rs, Map JavaDoc row)
261     {
262         FieldDescriptor fld = m_cld.getOjbConcreteClassField();
263         if (fld == null)
264         {
265             return null;
266         }
267         try
268         {
269             Object JavaDoc tmp = fld.getJdbcType().getObjectFromColumn(rs, fld.getColumnName());
270             // allow field-conversion for discriminator column too
271
String JavaDoc result = (String JavaDoc) 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 JavaDoc 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 JavaDoc row) throws PersistenceBrokerException
300     {
301         ClassDescriptor result = m_cld;
302         Class JavaDoc ojbConcreteClass = (Class JavaDoc) 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 }
326
Popular Tags