KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > ojb > broker > cache > ObjectCacheTwoLevelImpl


1 package org.apache.ojb.broker.cache;
2
3 /* Copyright 2004-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.io.Serializable JavaDoc;
19 import java.lang.ref.ReferenceQueue JavaDoc;
20 import java.lang.ref.SoftReference JavaDoc;
21 import java.util.HashMap JavaDoc;
22 import java.util.Iterator JavaDoc;
23 import java.util.Properties JavaDoc;
24
25 import org.apache.ojb.broker.Identity;
26 import org.apache.ojb.broker.PBStateEvent;
27 import org.apache.ojb.broker.PBStateListener;
28 import org.apache.ojb.broker.PersistenceBroker;
29 import org.apache.ojb.broker.core.DelegatingPersistenceBroker;
30 import org.apache.ojb.broker.core.PersistenceBrokerImpl;
31 import org.apache.ojb.broker.core.proxy.ProxyHelper;
32 import org.apache.ojb.broker.metadata.ClassDescriptor;
33 import org.apache.ojb.broker.metadata.FieldDescriptor;
34 import org.apache.ojb.broker.metadata.MetadataException;
35 import org.apache.ojb.broker.util.ClassHelper;
36 import org.apache.ojb.broker.util.logging.Logger;
37 import org.apache.ojb.broker.util.logging.LoggerFactory;
38 import org.apache.commons.lang.builder.ToStringBuilder;
39
40 /**
41  * A two-level {@link ObjectCache} implementation with a session- and an application cache. The application
42  * cache could be specified by the property <code>applicationCache</code>.
43  * <p/>
44  * The first level is a transactional session
45  * cache which cache objects till {@link org.apache.ojb.broker.PersistenceBroker#close()} or if
46  * a PB-tx is running till {@link org.apache.ojb.broker.PersistenceBroker#abortTransaction()} or
47  * {@link org.apache.ojb.broker.PersistenceBroker#commitTransaction()}. On commit all objects written to
48  * database will be pushed to the application cache.
49  * </p>
50  * <p/>
51  * The session cache act as a temporary storage for all read/store operations of persistent objects
52  * and only on commit or close of the used PB instance the buffered objects of type
53  * {@link #TYPE_WRITE} will be written to the application cache. Except objects of type
54  * {@link #TYPE_NEW_MATERIALIZED} these objects will be immediatly pushed to application cache.
55  * </p>
56  * <p/>
57  * <p/>
58  * </p>
59  * <p/>
60  * The application cache
61  * </p>
62  * <p/>
63  * <table cellspacing="2" cellpadding="2" border="3" frame="box">
64  * <tr>
65  * <td><strong>Property Key</strong></td>
66  * <td><strong>Property Values</strong></td>
67  * </tr>
68  * <p/>
69  * <tr>
70  * <td>applicationCache</td>
71  * <td>
72  * Specifies the {@link ObjectCache} implementation used as application cache (second level cache).
73  * By default {@link ObjectCacheDefaultImpl} was used. It's recommended to use a shared cache implementation
74  * (all used PB instances should access the same pool of objects - e.g. by using a static Map in cache
75  * implementation).
76  * </td>
77  * </tr>
78  * <p/>
79  * <tr>
80  * <td>copyStrategy</td>
81  * <td>
82  * Specifies the implementation class of the {@link ObjectCacheTwoLevelImpl.CopyStrategy}
83  * interface, which was used to copy objects on read and write to application cache. If not
84  * specified a default implementation based was used ({@link ObjectCacheTwoLevelImpl.CopyStrategyImpl}
85  * make field-descriptor based copies of the cached objects).
86  * </td>
87  * </tr>
88  * <p/>
89  * <tr>
90  * <td>forceProxies</td>
91  * <td>
92  * If <em>true</em> on materialization of cached objects, all referenced objects will
93  * be represented by proxy objects (independent from the proxy settings in reference- or
94  * collection-descriptor).
95  * <br/>
96  * <strong>Note:</strong> To use this feature all persistence capable objects have to be
97  * interface based <strong>or</strong> the <em>ProxyFactory</em> and
98  * <em>IndirectionHandler</em> implementation classes supporting dynamic proxy enhancement
99  * for all classes (see OJB.properties file).
100  * </td>
101  * </tr>
102  * </table>
103  * <p/>
104  *
105  * @version $Id: ObjectCacheTwoLevelImpl.java,v 1.1.2.16 2005/12/21 22:24:15 tomdz Exp $
106  */

107 public class ObjectCacheTwoLevelImpl implements ObjectCacheInternal, PBStateListener
108 {
109     private Logger log = LoggerFactory.getLogger(ObjectCacheTwoLevelImpl.class);
110
111     public static final String JavaDoc APPLICATION_CACHE_PROP = "applicationCache";
112     public static final String JavaDoc COPY_STRATEGY_PROP = "copyStrategy";
113     public static final String JavaDoc FORCE_PROXIES = "forceProxies";
114     private static final String JavaDoc DEF_COPY_STRATEGY = ObjectCacheTwoLevelImpl.CopyStrategyImpl.class.getName();
115     private static final String JavaDoc DEF_APP_CACHE = ObjectCacheDefaultImpl.class.getName();
116
117     private HashMap JavaDoc sessionCache;
118     // private boolean enabledReadCache;
119
private int invokeCounter;
120     private ReferenceQueue JavaDoc queue = new ReferenceQueue JavaDoc();
121     private ObjectCacheInternal applicationCache;
122     private CopyStrategy copyStrategy;
123     private PersistenceBrokerImpl broker;
124     private boolean forceProxies = false;
125
126     public ObjectCacheTwoLevelImpl(final PersistenceBroker broker, Properties JavaDoc prop)
127     {
128         // TODO: Fix cast. Cast is needed to get access to ReferenceBroker class in PBImpl, see method #lookup
129
if(broker instanceof PersistenceBrokerImpl)
130         {
131             this.broker = (PersistenceBrokerImpl) broker;
132         }
133         else if(broker instanceof DelegatingPersistenceBroker)
134         {
135             this.broker = (PersistenceBrokerImpl) ((DelegatingPersistenceBroker) broker).getInnermostDelegate();
136         }
137         else
138         {
139             throw new RuntimeCacheException("Can't initialize two level cache, expect instance of"
140                     + PersistenceBrokerImpl.class + " or of " + DelegatingPersistenceBroker.class
141                     + " to setup application cache, but was " + broker);
142         }
143         this.sessionCache = new HashMap JavaDoc(100);
144         // this.enabledReadCache = false;
145
setupApplicationCache(this.broker, prop);
146         // we add this instance as a permanent PBStateListener
147
broker.addListener(this, true);
148     }
149
150     /**
151      * Returns the {@link org.apache.ojb.broker.PersistenceBroker} instance associated with
152      * this cache instance.
153      */

154     public PersistenceBrokerImpl getBroker()
155     {
156         return broker;
157     }
158
159     private void setupApplicationCache(PersistenceBrokerImpl broker, Properties JavaDoc prop)
160     {
161         if(log.isDebugEnabled()) log.debug("Start setup application cache for broker " + broker);
162         if(prop == null)
163         {
164             prop = new Properties JavaDoc();
165         }
166         String JavaDoc copyStrategyName = prop.getProperty(COPY_STRATEGY_PROP, DEF_COPY_STRATEGY).trim();
167         if(copyStrategyName.length() == 0)
168         {
169             copyStrategyName = DEF_COPY_STRATEGY;
170         }
171         String JavaDoc applicationCacheName = prop.getProperty(APPLICATION_CACHE_PROP, DEF_APP_CACHE).trim();
172         if(applicationCacheName.length() == 0)
173         {
174             applicationCacheName = DEF_APP_CACHE;
175         }
176         
177         String JavaDoc forceProxyValue = prop.getProperty(FORCE_PROXIES, "false").trim();
178         forceProxies = Boolean.valueOf(forceProxyValue).booleanValue();
179         
180         if (forceProxies && broker.getProxyFactory().interfaceRequiredForProxyGeneration()){
181             log.warn("'" + FORCE_PROXIES + "' is set to true, however a ProxyFactory implementation " +
182                     "[" + broker.getProxyFactory().getClass().getName() +"] " +
183                     " that requires persistent objects to implement an inteface is being used. Please ensure " +
184                     "that all persistent objects implement an interface, or change the ProxyFactory setting to a dynamic " +
185                     "proxy generator (like ProxyFactoryCGLIBImpl).");
186         }
187         
188         Class JavaDoc[] type = new Class JavaDoc[]{PersistenceBroker.class, Properties JavaDoc.class};
189         Object JavaDoc[] objects = new Object JavaDoc[]{broker, prop};
190         try
191         {
192             this.copyStrategy = (CopyStrategy) ClassHelper.newInstance(copyStrategyName);
193             Class JavaDoc target = ClassHelper.getClass(applicationCacheName);
194             if(target.equals(ObjectCacheDefaultImpl.class))
195             {
196                 // this property doesn't make sense in context of two-level cache
197
prop.setProperty(ObjectCacheDefaultImpl.AUTOSYNC_PROP, "false");
198             }
199             ObjectCache temp = (ObjectCache) ClassHelper.newInstance(target, type, objects);
200             if(!(temp instanceof ObjectCacheInternal))
201             {
202                 log.warn("Specified application cache class doesn't implement '" + ObjectCacheInternal.class.getName()
203                     + "'. For best interaction only specify caches implementing the internal object cache interface.");
204                 temp = new CacheDistributor.ObjectCacheInternalWrapper(temp);
205             }
206             this.applicationCache = (ObjectCacheInternal) temp;
207         }
208         catch(Exception JavaDoc e)
209         {
210             throw new MetadataException("Can't setup application cache. Specified application cache was '"
211                     + applicationCacheName + "', copy strategy was '" + copyStrategyName + "'", e);
212         }
213         if(log.isEnabledFor(Logger.INFO))
214         {
215             ToStringBuilder buf = new ToStringBuilder(this);
216             buf.append("copyStrategy", copyStrategyName)
217                     .append("applicationCache", applicationCacheName);
218             log.info("Setup cache: " + buf.toString());
219         }
220     }
221
222     /**
223      * Returns the application cache that this 2-level cache uses.
224      *
225      * @return The application cache
226      */

227     public ObjectCacheInternal getApplicationCache()
228     {
229         return applicationCache;
230     }
231
232     private Object JavaDoc lookupFromApplicationCache(Identity oid)
233     {
234         Object JavaDoc result = null;
235         Object JavaDoc obj = getApplicationCache().lookup(oid);
236         if(obj != null)
237         {
238             result = copyStrategy.read(broker, obj);
239         }
240         return result;
241     }
242
243     private boolean putToApplicationCache(Identity oid, Object JavaDoc obj, boolean cacheIfNew)
244     {
245         /*
246         we allow to reuse cached objects, so lookup the old cache object
247         and forward it to the CopyStrategy
248         */

249         Object JavaDoc oldTarget = null;
250         if(!cacheIfNew)
251         {
252             oldTarget = getApplicationCache().lookup(oid);
253         }
254         Object JavaDoc target = copyStrategy.write(broker, obj, oldTarget);
255         if(cacheIfNew)
256         {
257             return getApplicationCache().cacheIfNew(oid, target);
258         }
259         else
260         {
261             getApplicationCache().cache(oid, target);
262             return false;
263         }
264     }
265
266     /**
267      * Discard all session cached objects and reset the state of
268      * this class for further usage.
269      */

270     public void resetSessionCache()
271     {
272         sessionCache.clear();
273         invokeCounter = 0;
274     }
275
276     /**
277      * Push all cached objects of the specified type, e.g. like {@link #TYPE_WRITE} to
278      * the application cache and reset type to the specified one.
279      */

280     private void pushToApplicationCache(int typeToProcess, int typeAfterProcess)
281     {
282         for(Iterator JavaDoc iter = sessionCache.values().iterator(); iter.hasNext();)
283         {
284             CacheEntry entry = (CacheEntry) iter.next();
285             // if the cached object was garbage collected, nothing to do
286
Object JavaDoc result = entry.get();
287             if(result == null)
288             {
289                 if(log.isDebugEnabled())
290                     log.debug("Object in session cache was gc, nothing to push to application cache");
291             }
292             else
293             {
294                 // push all objects of the specified type to application cache
295
if(entry.type == typeToProcess)
296                 {
297                     if(log.isDebugEnabled())
298                     {
299                         log.debug("Move obj from session cache --> application cache : " + entry.oid);
300                     }
301                     /*
302                     arminw:
303                     only cache non-proxy or real subject of materialized proxy objects
304                     */

305                     if(ProxyHelper.isMaterialized(result))
306                     {
307                         putToApplicationCache(entry.oid, ProxyHelper.getRealObject(result), false);
308                         // set the new type after the object was pushed to application cache
309
entry.type = typeAfterProcess;
310                     }
311                 }
312             }
313         }
314     }
315
316     /**
317      * Cache the given object. Creates a
318      * {@link org.apache.ojb.broker.cache.ObjectCacheTwoLevelImpl.CacheEntry} and put it
319      * to session cache. If the specified object to cache is of type {@link #TYPE_NEW_MATERIALIZED}
320      * it will be immediately pushed to the application cache.
321      */

322     public void doInternalCache(Identity oid, Object JavaDoc obj, int type)
323     {
324         processQueue();
325         // pass new materialized objects immediately to application cache
326
if(type == TYPE_NEW_MATERIALIZED)
327         {
328             boolean result = putToApplicationCache(oid, obj, true);
329             CacheEntry entry = new CacheEntry(oid, obj, TYPE_CACHED_READ, queue);
330             if(result)
331             {
332                 // as current session says this object is new, put it
333
// in session cache
334
putToSessionCache(oid, entry, false);
335             }
336             else
337             {
338                 // object is not new, but if not in session cache
339
// put it in
340
putToSessionCache(oid, entry, true);
341                 if(log.isDebugEnabled())
342                 {
343                     log.debug("The 'new' materialized object was already in cache," +
344                             " will not push it to application cache: " + oid);
345                 }
346             }
347         }
348         else
349         {
350             // other types of cached objects will only be put to the session
351
// cache.
352
CacheEntry entry = new CacheEntry(oid, obj, type, queue);
353             putToSessionCache(oid, entry, false);
354         }
355     }
356
357     /**
358      * Lookup corresponding object from session cache or if not found from
359      * the underlying real {@link ObjectCache} - Return <em>null</em> if no
360      * object was found.
361      */

362     public Object JavaDoc lookup(Identity oid)
363     {
364         Object JavaDoc result = null;
365         // 1. lookup an instance in session cache
366
CacheEntry entry = (CacheEntry) sessionCache.get(oid);
367         if(entry != null)
368         {
369             result = entry.get();
370         }
371         if(result == null)
372         {
373             result = lookupFromApplicationCache(oid);
374             // 4. if we have a match
375
// put object in session cache
376
if(result != null)
377             {
378                 doInternalCache(oid, result, TYPE_CACHED_READ);
379                 materializeFullObject(result);
380                 if(log.isDebugEnabled()) log.debug("Materialized object from second level cache: " + oid);
381             }
382         }
383         if(result != null && log.isDebugEnabled())
384         {
385             log.debug("Match for: " + oid);
386         }
387         return result;
388     }
389
390     /**
391      * This cache implementation cache only "flat" objects (persistent objects without any
392      * references), so when {@link #lookup(org.apache.ojb.broker.Identity)} a cache object
393      * it needs full materialization (assign all referenced objects) before the cache returns
394      * the object. The materialization of the referenced objects based on the auto-XXX settings
395      * specified in the metadata mapping.
396      * <br/>
397      * Override this method if needed in conjunction with a user-defined
398      * {@link org.apache.ojb.broker.cache.ObjectCacheTwoLevelImpl.CopyStrategy}.
399      *
400      * @param target The "flat" object for full materialization
401      */

402     public void materializeFullObject(Object JavaDoc target)
403     {
404         ClassDescriptor cld = broker.getClassDescriptor(target.getClass());
405         // don't force, let OJB use the user settings
406
final boolean forced = false;
407         if (forceProxies){
408             broker.getReferenceBroker().retrieveProxyReferences(target, cld, forced);
409             broker.getReferenceBroker().retrieveProxyCollections(target, cld, forced);
410         }else{
411             broker.getReferenceBroker().retrieveReferences(target, cld, forced);
412             broker.getReferenceBroker().retrieveCollections(target, cld, forced);
413         }
414     }
415
416     /**
417      * Remove the corresponding object from session AND application cache.
418      */

419     public void remove(Identity oid)
420     {
421         if(log.isDebugEnabled()) log.debug("Remove object " + oid);
422         sessionCache.remove(oid);
423         getApplicationCache().remove(oid);
424     }
425
426     /**
427      * Clear session cache and application cache.
428      */

429     public void clear()
430     {
431         sessionCache.clear();
432         getApplicationCache().clear();
433     }
434
435     /**
436      * Put the specified object to session cache.
437      */

438     public void cache(Identity oid, Object JavaDoc obj)
439     {
440         doInternalCache(oid, obj, TYPE_UNKNOWN);
441     }
442
443     public boolean cacheIfNew(Identity oid, Object JavaDoc obj)
444     {
445         boolean result = putToApplicationCache(oid, obj, true);
446         if(result)
447         {
448             CacheEntry entry = new CacheEntry(oid, obj, TYPE_CACHED_READ, queue);
449             putToSessionCache(oid, entry, true);
450         }
451         return result;
452     }
453
454     /**
455      * Put object to session cache.
456      *
457      * @param oid The {@link org.apache.ojb.broker.Identity} of the object to cache
458      * @param entry The {@link org.apache.ojb.broker.cache.ObjectCacheTwoLevelImpl.CacheEntry} of the object
459      * @param onlyIfNew Flag, if set <em>true</em> only new objects (not already in session cache) be cached.
460      */

461     private void putToSessionCache(Identity oid, CacheEntry entry, boolean onlyIfNew)
462     {
463         if(onlyIfNew)
464         {
465             // no synchronization needed, because session cache was used per broker instance
466
if(!sessionCache.containsKey(oid)) sessionCache.put(oid, entry);
467         }
468         else
469         {
470             sessionCache.put(oid, entry);
471         }
472     }
473
474     /**
475      * Make sure that the Identity objects of garbage collected cached
476      * objects are removed too.
477      */

478     private void processQueue()
479     {
480         CacheEntry sv;
481         while((sv = (CacheEntry) queue.poll()) != null)
482         {
483             sessionCache.remove(sv.oid);
484         }
485     }
486
487     //------------------------------------------------------------
488
// PBStateListener methods
489
//------------------------------------------------------------
490
/**
491      * After committing the transaction push the object
492      * from session cache ( 1st level cache) to the application cache
493      * (2d level cache). Finally, clear the session cache.
494      */

495     public void afterCommit(PBStateEvent event)
496     {
497         if(log.isDebugEnabled()) log.debug("afterCommit() call, push objects to application cache");
498         if(invokeCounter != 0)
499         {
500             log.error("** Please check method calls of ObjectCacheTwoLevelImpl#enableMaterialization and" +
501                     " ObjectCacheTwoLevelImpl#disableMaterialization, number of calls have to be equals **");
502         }
503         try
504         {
505             // we only push "really modified objects" to the application cache
506
pushToApplicationCache(TYPE_WRITE, TYPE_CACHED_READ);
507         }
508         finally
509         {
510             resetSessionCache();
511         }
512     }
513
514     /**
515      * Before closing the PersistenceBroker ensure that the session
516      * cache is cleared
517      */

518     public void beforeClose(PBStateEvent event)
519     {
520         /*
521         arminw:
522         this is a workaround for use in managed environments. When a PB instance is used
523         within a container a PB.close call is done when leave the container method. This close
524         the PB handle (but the real instance is still in use) and the PB listener are notified.
525         But the JTA tx was not committed at
526         this point in time and the session cache should not be cleared, because the updated/new
527         objects will be pushed to the real cache on commit call (if we clear, nothing to push).
528         So we check if the real broker is in a local tx (in this case we are in a JTA tx and the handle
529         is closed), if true we don't reset the session cache.
530         */

531         if(!broker.isInTransaction())
532         {
533             if(log.isDebugEnabled()) log.debug("Clearing the session cache");
534             resetSessionCache();
535         }
536     }
537
538     /**
539      * Before rollbacking clear the session cache (first level cache)
540      */

541     public void beforeRollback(PBStateEvent event)
542     {
543         if(log.isDebugEnabled()) log.debug("beforeRollback()");
544         resetSessionCache();
545     }
546
547     public void afterOpen(PBStateEvent event)
548     {
549     }
550
551     public void beforeBegin(PBStateEvent event)
552     {
553     }
554
555     public void afterBegin(PBStateEvent event)
556     {
557     }
558
559     public void beforeCommit(PBStateEvent event)
560     {
561     }
562
563     public void afterRollback(PBStateEvent event)
564     {
565     }
566     //------------------------------------------------------------
567

568     //-----------------------------------------------------------
569
// inner class
570
//-----------------------------------------------------------
571

572     /**
573      * Helper class to wrap cached objects using {@link java.lang.ref.SoftReference}, which
574      * allows to release objects when they no longer referenced within the PB session.
575      */

576     static final class CacheEntry extends SoftReference JavaDoc implements Serializable JavaDoc
577     {
578         private int type;
579         private Identity oid;
580
581         public CacheEntry(Identity oid, Object JavaDoc obj, int type, final ReferenceQueue JavaDoc q)
582         {
583             super(obj, q);
584             this.oid = oid;
585             this.type = type;
586         }
587     }
588
589
590     public interface CopyStrategy
591     {
592         /**
593          * Called when an object is read from the application cache (second level cache)
594          * before the object is full materialized, see {@link ObjectCacheTwoLevelImpl#materializeFullObject(Object)}.
595          *
596          * @param broker The current used {@link org.apache.ojb.broker.PersistenceBroker} instance.
597          * @param obj The object read from the application cache.
598          * @return A copy of the object.
599          */

600         public Object JavaDoc read(PersistenceBroker broker, Object JavaDoc obj);
601
602         /**
603          * Called before an object is written to the application cache (second level cache).
604          *
605          * @param broker The current used {@link org.apache.ojb.broker.PersistenceBroker} instance.
606          * @param obj The object to cache in application cache.
607          * @param oldObject The old cache object or <em>null</em>
608          * @return A copy of the object to write to application cache.
609          */

610         public Object JavaDoc write(PersistenceBroker broker, Object JavaDoc obj, Object JavaDoc oldObject);
611     }
612
613     public static class CopyStrategyImpl implements CopyStrategy
614     {
615         static final String JavaDoc CLASS_NAME_STR = "ojbClassName11";
616
617         public CopyStrategyImpl()
618         {
619         }
620
621         public Object JavaDoc read(PersistenceBroker broker, Object JavaDoc obj)
622         {
623             HashMap JavaDoc source = (HashMap JavaDoc) obj;
624             String JavaDoc className = (String JavaDoc) source.get(CLASS_NAME_STR);
625             ClassDescriptor cld = broker.getDescriptorRepository().getDescriptorFor(className);
626             Object JavaDoc target = ClassHelper.buildNewObjectInstance(cld);
627             // perform main object values
628
FieldDescriptor[] flds = cld.getFieldDescriptor(true);
629             FieldDescriptor fld;
630             int length = flds.length;
631             for(int i = 0; i < length; i++)
632             {
633                 fld = flds[i];
634                 // read the field value
635
Object JavaDoc value = source.get(fld.getPersistentField().getName());
636                 // copy the field value
637
if(value != null) value = fld.getJdbcType().getFieldType().copy(value);
638                 // now make a field-conversion to java-type, because we only
639
// the sql type of the field
640
value = fld.getFieldConversion().sqlToJava(value);
641                 // set the copied field value in new object
642
fld.getPersistentField().set(target, value);
643             }
644             return target;
645         }
646
647         public Object JavaDoc write(PersistenceBroker broker, Object JavaDoc obj, Object JavaDoc oldObject)
648         {
649             ClassDescriptor cld = broker.getClassDescriptor(obj.getClass());
650             // we store field values by name in a Map
651
HashMap JavaDoc target = oldObject != null ? (HashMap JavaDoc) oldObject : new HashMap JavaDoc();
652             // perform main object values
653
FieldDescriptor[] flds = cld.getFieldDescriptor(true);
654             FieldDescriptor fld;
655             int length = flds.length;
656             for(int i = 0; i < length; i++)
657             {
658                 fld = flds[i];
659                 // get the value
660
Object JavaDoc value = fld.getPersistentField().get(obj);
661                 // convert value to a supported sql type
662
value = fld.getFieldConversion().javaToSql(value);
663                 // copy the sql type
664
value = fld.getJdbcType().getFieldType().copy(value);
665                 target.put(fld.getPersistentField().getName(), value);
666             }
667             target.put(CLASS_NAME_STR, obj.getClass().getName());
668             return target;
669         }
670     }
671 }
672
Popular Tags