KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectstyle > cayenne > access > DataContext


1 /* ====================================================================
2  *
3  * The ObjectStyle Group Software License, version 1.1
4  * ObjectStyle Group - http://objectstyle.org/
5  *
6  * Copyright (c) 2002-2005, Andrei (Andrus) Adamchik and individual authors
7  * of the software. All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  * notice, this list of conditions and the following disclaimer.
15  *
16  * 2. Redistributions in binary form must reproduce the above copyright
17  * notice, this list of conditions and the following disclaimer in
18  * the documentation and/or other materials provided with the
19  * distribution.
20  *
21  * 3. The end-user documentation included with the redistribution, if any,
22  * must include the following acknowlegement:
23  * "This product includes software developed by independent contributors
24  * and hosted on ObjectStyle Group web site (http://objectstyle.org/)."
25  * Alternately, this acknowlegement may appear in the software itself,
26  * if and wherever such third-party acknowlegements normally appear.
27  *
28  * 4. The names "ObjectStyle Group" and "Cayenne" must not be used to endorse
29  * or promote products derived from this software without prior written
30  * permission. For written permission, email
31  * "andrus at objectstyle dot org".
32  *
33  * 5. Products derived from this software may not be called "ObjectStyle"
34  * or "Cayenne", nor may "ObjectStyle" or "Cayenne" appear in their
35  * names without prior written permission.
36  *
37  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40  * DISCLAIMED. IN NO EVENT SHALL THE OBJECTSTYLE GROUP OR
41  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
43  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
44  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
46  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
47  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48  * SUCH DAMAGE.
49  * ====================================================================
50  *
51  * This software consists of voluntary contributions made by many
52  * individuals and hosted on ObjectStyle Group web site. For more
53  * information on the ObjectStyle Group, please see
54  * <http://objectstyle.org/>.
55  */

56
57 package org.objectstyle.cayenne.access;
58
59 import java.io.IOException JavaDoc;
60 import java.io.ObjectInputStream JavaDoc;
61 import java.io.ObjectOutputStream JavaDoc;
62 import java.io.Serializable JavaDoc;
63 import java.util.ArrayList JavaDoc;
64 import java.util.Collection JavaDoc;
65 import java.util.Collections JavaDoc;
66 import java.util.HashMap JavaDoc;
67 import java.util.Iterator JavaDoc;
68 import java.util.List JavaDoc;
69 import java.util.Map JavaDoc;
70
71 import org.apache.log4j.Level;
72 import org.objectstyle.cayenne.CayenneException;
73 import org.objectstyle.cayenne.CayenneRuntimeException;
74 import org.objectstyle.cayenne.DataObject;
75 import org.objectstyle.cayenne.DataRow;
76 import org.objectstyle.cayenne.Fault;
77 import org.objectstyle.cayenne.ObjectId;
78 import org.objectstyle.cayenne.PersistenceState;
79 import org.objectstyle.cayenne.TempObjectId;
80 import org.objectstyle.cayenne.access.event.DataContextEvent;
81 import org.objectstyle.cayenne.access.util.IteratedSelectObserver;
82 import org.objectstyle.cayenne.access.util.PrefetchHelper;
83 import org.objectstyle.cayenne.access.util.QueryUtils;
84 import org.objectstyle.cayenne.conf.Configuration;
85 import org.objectstyle.cayenne.event.EventManager;
86 import org.objectstyle.cayenne.event.EventSubject;
87 import org.objectstyle.cayenne.map.DataMap;
88 import org.objectstyle.cayenne.map.DbJoin;
89 import org.objectstyle.cayenne.map.DbRelationship;
90 import org.objectstyle.cayenne.map.Entity;
91 import org.objectstyle.cayenne.map.EntityResolver;
92 import org.objectstyle.cayenne.map.ObjAttribute;
93 import org.objectstyle.cayenne.map.ObjEntity;
94 import org.objectstyle.cayenne.map.ObjRelationship;
95 import org.objectstyle.cayenne.query.GenericSelectQuery;
96 import org.objectstyle.cayenne.query.ParameterizedQuery;
97 import org.objectstyle.cayenne.query.Query;
98 import org.objectstyle.cayenne.query.SelectQuery;
99 import org.objectstyle.cayenne.util.Util;
100
101 /**
102  * Class that provides applications with access to Cayenne persistence features. In most
103  * cases this is the only access class directly used in the application.
104  * <p>
105  * Most common DataContext use pattern is to create one DataContext per session. "Session"
106  * may be a an HttpSesession in a web application, or any other similar concept in a
107  * multiuser application.
108  * </p>
109  * <p>
110  * DataObjects are registered with DataContext either implicitly when they are fetched via
111  * a query, or read via a relationship from another object, or explicitly via calling
112  * {@link #createAndRegisterNewObject(Class)}during new DataObject creation. DataContext
113  * tracks changes made to its DataObjects in memory, and flushes them to the database when
114  * {@link #commitChanges()}is called. Until DataContext is committed, changes made to its
115  * objects are not visible in other DataContexts.
116  * </p>
117  * <p>
118  * Each DataObject can belong only to a single DataContext. To create a replica of an
119  * object from a different DataContext in a local context, use
120  * {@link #localObjects(java.util.List)}method.
121  * <p>
122  * <i>For more information see <a HREF="../../../../../../userguide/index.html"
123  * target="_top">Cayenne User Guide. </a> </i>
124  * </p>
125  *
126  * @author Andrei Adamchik
127  */

128 public class DataContext implements QueryEngine, Serializable JavaDoc {
129
130     // noop delegate
131
private static final DataContextDelegate defaultDelegate = new DataContextDelegate() {
132
133         public GenericSelectQuery willPerformSelect(
134                 DataContext context,
135                 GenericSelectQuery query) {
136             return query;
137         }
138
139         public boolean shouldMergeChanges(DataObject object, DataRow snapshotInStore) {
140             return true;
141         }
142
143         public boolean shouldProcessDelete(DataObject object) {
144             return true;
145         }
146
147         public void finishedMergeChanges(DataObject object) {
148
149         }
150
151         public void finishedProcessDelete(DataObject object) {
152
153         }
154     };
155
156     // DataContext events
157
public static final EventSubject WILL_COMMIT = EventSubject.getSubject(
158             DataContext.class,
159             "DataContextWillCommit");
160     public static final EventSubject DID_COMMIT = EventSubject.getSubject(
161             DataContext.class,
162             "DataContextDidCommit");
163     public static final EventSubject DID_ROLLBACK = EventSubject.getSubject(
164             DataContext.class,
165             "DataContextDidRollback");
166     
167
168     
169     /**
170      * A holder of a DataContext bound to the current thread.
171      *
172      * @since 1.1
173      */

174     protected static final ThreadLocal JavaDoc threadDataContext = new ThreadLocal JavaDoc();
175
176     // event posting default for new DataContexts
177
private static boolean transactionEventsEnabledDefault;
178
179     // enable/disable event handling for individual instances
180
private boolean transactionEventsEnabled;
181
182     // Set of DataContextDelegates to be notified.
183
private DataContextDelegate delegate;
184
185     protected boolean usingSharedSnaphsotCache;
186     protected boolean validatingObjectsOnCommit;
187     protected ObjectStore objectStore;
188
189     protected transient QueryEngine parent;
190     
191     /**
192      * Stores user defined properties associated with this DataContext.
193      *
194      * @since 1.2
195      */

196     protected Map JavaDoc userProperties;
197
198     /**
199      * Stores the name of parent DataDomain. Used to defer initialization of the parent
200      * QueryEngine after deserialization. This helps avoid an issue with certain servlet
201      * engines (e.g. Tomcat) where HttpSessions with DataContext's are deserialized at
202      * startup before Cayenne stack is fully initialized.
203      */

204     protected transient String JavaDoc lazyInitParentDomainName;
205
206     /**
207      * A factory method of DataObjects. Uses Configuration ClassLoader to instantiate a
208      * new instance of DataObject of a given class.
209      */

210     private static final DataObject newDataObject(String JavaDoc className) throws Exception JavaDoc {
211         return (DataObject) Configuration
212                 .getResourceLoader()
213                 .loadClass(className)
214                 .newInstance();
215     }
216     
217     /**
218      * Returns the DataContext bound to the current thread.
219      *
220      * @since 1.1
221      * @return the DataContext associated with caller thread.
222      * @throws IllegalStateException if there is no DataContext bound to the current
223      * thread.
224      * @see org.objectstyle.cayenne.conf.WebApplicationContextProvider
225      */

226     public static DataContext getThreadDataContext() throws IllegalStateException JavaDoc {
227         DataContext dc = (DataContext) threadDataContext.get();
228         if (dc == null) {
229             throw new IllegalStateException JavaDoc("Current thread has no bound DataContext.");
230         }
231
232         return dc;
233     }
234     
235     /**
236      * Binds a DataContext to the current thread. DataContext can later be retrieved by
237      * users in the same thread by calling {@link DataContext#getThreadDataContext}. Using
238      * null parameter will unbind currently bound DataContext.
239      *
240      * @since 1.1
241      */

242     public static void bindThreadDataContext(DataContext context) {
243         threadDataContext.set(context);
244     }
245
246     /**
247      * Factory method that creates and returns a new instance of DataContext based on
248      * default domain. If more than one domain exists in the current configuration,
249      * {@link DataContext#createDataContext(String)} must be used instead. ObjectStore associated
250      * with created DataContext will have a cache stack configured using parent domain settings.
251      */

252     public static DataContext createDataContext() {
253         return Configuration.getSharedConfiguration().getDomain().createDataContext();
254     }
255
256     /**
257      * Factory method that creates and returns a new instance of DataContext based on
258      * default domain. If more than one domain exists in the current configuration,
259      * {@link DataContext#createDataContext(String, boolean)} must be used instead.
260      * ObjectStore associated with newly created DataContext will have a cache
261      * stack configured according to the specified policy, overriding a parent domain setting.
262      *
263      * @since 1.1
264      */

265     public static DataContext createDataContext(boolean useSharedCache) {
266         return Configuration.getSharedConfiguration().getDomain().createDataContext(
267                 useSharedCache);
268     }
269
270     /**
271      * Factory method that creates and returns a new instance of DataContext using named
272      * domain as its parent. If there is no domain matching the name argument, an
273      * exception is thrown.
274      */

275     public static DataContext createDataContext(String JavaDoc domainName) {
276         DataDomain domain = Configuration.getSharedConfiguration().getDomain(domainName);
277         if (domain == null) {
278             throw new IllegalArgumentException JavaDoc("Non-existent domain: " + domainName);
279         }
280         return domain.createDataContext();
281     }
282
283     /**
284      * Creates and returns new DataContext that will use a named DataDomain as its parent.
285      * ObjectStore associated with newly created DataContext will have a cache
286      * stack configured according to the specified policy, overriding a parent domain
287      * setting.
288      *
289      * @since 1.1
290      */

291     public static DataContext createDataContext(String JavaDoc domainName, boolean useSharedCache) {
292
293         DataDomain domain = Configuration.getSharedConfiguration().getDomain(domainName);
294         if (domain == null) {
295             throw new IllegalArgumentException JavaDoc("Non-existent domain: " + domainName);
296         }
297         return domain.createDataContext(useSharedCache);
298     }
299
300     /**
301      * Default constructor that creates a DataContext that has no association with a
302      * DataDomain.
303      */

304     public DataContext() {
305         this(null, null);
306     }
307
308     /**
309      * Creates a DataContext with parent QueryEngine and a DataRowStore that should be
310      * used by the ObjectStore.
311      *
312      * @since 1.1
313      * @param parent parent QueryEngine used to communicate with the data source.
314      * @param objectStore ObjectStore used by DataContext.
315      */

316     public DataContext(QueryEngine parent, ObjectStore objectStore) {
317         setParent(parent);
318
319         this.objectStore = objectStore;
320         this.setTransactionEventsEnabled(transactionEventsEnabledDefault);
321         this.usingSharedSnaphsotCache = getParentDataDomain() != null
322                 && objectStore.getDataRowCache() == getParentDataDomain()
323                         .getSharedSnapshotCache();
324     }
325
326     /**
327      * Initializes parent if deserialization left it uninitialized.
328      */

329     private final void awakeFromDeserialization() {
330         if (parent == null && lazyInitParentDomainName != null) {
331
332             DataDomain domain = Configuration.getSharedConfiguration().getDomain(
333                     lazyInitParentDomainName);
334
335             this.parent = domain;
336
337             if (isUsingSharedSnapshotCache() && domain != null) {
338                 this.objectStore.setDataRowCache(domain.getSharedSnapshotCache());
339             }
340         }
341     }
342     
343     /**
344      * Returns a map of user-defined properties associated with this DataContext.
345      *
346      * @since 1.2
347      */

348     protected Map JavaDoc getUserProperties() {
349         // do lazy init..
350
// as not all users will take advantage of properties, creating the
351
// map on demand to keep DataContext lean...
352
if (userProperties == null) {
353             userProperties = new HashMap JavaDoc();
354         }
355
356         return userProperties;
357     }
358     
359     /**
360      * Returns a user-defined property previously set via 'setUserProperty'. Note that it
361      * is a caller responsibility to synchronize access to properties.
362      *
363      * @since 1.2
364      */

365     public Object JavaDoc getUserProperty(String JavaDoc key) {
366         return getUserProperties().get(key);
367     }
368
369     /**
370      * Sets a user-defined property. Note that it is a caller responsibility to
371      * synchronize access to properties.
372      *
373      * @since 1.2
374      */

375     public void setUserProperty(String JavaDoc key, Object JavaDoc value) {
376         getUserProperties().put(key, value);
377     }
378     
379
380     /**
381      * Returns parent QueryEngine object. In most cases returned object is an instance of
382      * DataDomain.
383      */

384     public QueryEngine getParent() {
385         awakeFromDeserialization();
386         return parent;
387     }
388
389     /**
390      * <i>Note: currently nested DataContexts are not supported, so this method simply
391      * calls "getParent()". Using this method is preferrable to calling "getParent()"
392      * directly and casting it to DataDomain, since it more likely to be compatible with
393      * the future releases of Cayenne. </i>
394      *
395      * @return DataDomain that is a direct or indirect parent of this DataContext.
396      * @since 1.1
397      */

398     public DataDomain getParentDataDomain() {
399         return (DataDomain) getParent();
400     }
401
402     /**
403      * Sets direct parent of this DataContext.
404      */

405     public void setParent(QueryEngine parent) {
406         this.parent = parent;
407     }
408
409     /**
410      * Sets a DataContextDelegate for this context. Delegate is notified of certain events
411      * in the DataContext lifecycle and can customize DataContext behavior.
412      *
413      * @since 1.1
414      */

415     public void setDelegate(DataContextDelegate delegate) {
416         this.delegate = delegate;
417     }
418
419     /**
420      * Returns a delegate currently associated with this DataContext.
421      *
422      * @since 1.1
423      */

424     public DataContextDelegate getDelegate() {
425         return delegate;
426     }
427
428     /**
429      * @return a delegate instance if it is initialized, or a shared noop implementation
430      * the context has no delegate. Useful to prevent extra null checks and
431      * conditional logic in the code.
432      * @since 1.1
433      */

434     DataContextDelegate nonNullDelegate() {
435         return (delegate != null) ? delegate : DataContext.defaultDelegate;
436     }
437
438     /**
439      * Returns ObjectStore associated with this DataContext.
440      */

441     public ObjectStore getObjectStore() {
442         awakeFromDeserialization();
443         return objectStore;
444     }
445
446     /**
447      * Returns <code>true</code> if there are any modified, deleted or new objects
448      * registered with this DataContext, <code>false</code> otherwise.
449      */

450     public boolean hasChanges() {
451         return getObjectStore().hasChanges();
452     }
453
454     /**
455      * Returns a list of objects that are registered with this DataContext and have a
456      * state PersistenceState.NEW
457      */

458     public Collection JavaDoc newObjects() {
459         return getObjectStore().objectsInState(PersistenceState.NEW);
460     }
461
462     /**
463      * Returns a list of objects that are registered with this DataContext and have a
464      * state PersistenceState.DELETED
465      */

466     public Collection JavaDoc deletedObjects() {
467         return getObjectStore().objectsInState(PersistenceState.DELETED);
468     }
469
470     /**
471      * Returns a list of objects that are registered with this DataContext and have a
472      * state PersistenceState.MODIFIED
473      */

474     public Collection JavaDoc modifiedObjects() {
475         return getObjectStore().objectsInState(PersistenceState.MODIFIED);
476     }
477
478     /**
479      * Returns an object for a given ObjectId. If object is not registered with this
480      * context, a "hollow" object fault is created, registered, and returned to the
481      * caller.
482      */

483     public DataObject registeredObject(ObjectId oid) {
484         // must synchronize on ObjectStore since we must read and write atomically
485
synchronized (getObjectStore()) {
486             DataObject obj = objectStore.getObject(oid);
487             if (obj == null) {
488                 try {
489                     // TODO: shouldn't we replace this with oid.getObjectClass().newInstance()
490
obj = DataContext.newDataObject(oid.getObjectClass().getName());
491                 }
492                 catch (Exception JavaDoc ex) {
493                     String JavaDoc entity = (oid != null) ? getEntityResolver().lookupObjEntity(
494                             oid.getObjectClass()).getName() : null;
495                     throw new CayenneRuntimeException(
496                             "Error creating object for entity '" + entity + "'.",
497                             ex);
498                 }
499
500                 obj.setObjectId(oid);
501                 obj.setPersistenceState(PersistenceState.HOLLOW);
502                 obj.setDataContext(this);
503                 objectStore.addObject(obj);
504             }
505             return obj;
506         }
507     }
508
509     /**
510      * Creates or gets from cache a DataRow reflecting current object state.
511      *
512      * @since 1.1
513      */

514     public DataRow currentSnapshot(DataObject anObject) {
515         ObjEntity entity = getEntityResolver().lookupObjEntity(anObject);
516
517         // for a HOLLOW object return snapshot from cache
518
if (anObject.getPersistenceState() == PersistenceState.HOLLOW
519                 && anObject.getDataContext() != null) {
520
521             ObjectId id = anObject.getObjectId();
522             return getObjectStore().getSnapshot(id, this);
523         }
524
525         DataRow snapshot = new DataRow(10);
526
527         Iterator JavaDoc attributes = entity.getAttributeMap().entrySet().iterator();
528         while (attributes.hasNext()) {
529             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) attributes.next();
530             String JavaDoc attrName = (String JavaDoc) entry.getKey();
531             ObjAttribute objAttr = (ObjAttribute) entry.getValue();
532
533             // processing compound attributes correctly
534
snapshot.put(objAttr.getDbAttributePath(), anObject
535                     .readPropertyDirectly(attrName));
536         }
537
538         Iterator JavaDoc relationships = entity.getRelationshipMap().entrySet().iterator();
539         while (relationships.hasNext()) {
540             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) relationships.next();
541             ObjRelationship rel = (ObjRelationship) entry.getValue();
542
543             // if target doesn't propagates its key value, skip it
544
if (rel.isSourceIndependentFromTargetChange()) {
545                 continue;
546             }
547
548             Object JavaDoc targetObject = anObject.readPropertyDirectly(rel.getName());
549             if (targetObject == null) {
550                 continue;
551             }
552
553             // if target is Fault, get id attributes from stored snapshot
554
// to avoid unneeded fault triggering
555
if (targetObject instanceof Fault) {
556                 DataRow storedSnapshot = getObjectStore().getSnapshot(
557                         anObject.getObjectId(),
558                         this);
559                 if (storedSnapshot == null) {
560                     throw new CayenneRuntimeException(
561                             "No matching objects found for ObjectId "
562                                     + anObject.getObjectId()
563                                     + ". Object may have been deleted externally.");
564                 }
565
566                 DbRelationship dbRel = (DbRelationship) rel.getDbRelationships().get(0);
567                 Iterator JavaDoc joins = dbRel.getJoins().iterator();
568                 while (joins.hasNext()) {
569                     DbJoin join = (DbJoin) joins.next();
570                     String JavaDoc key = join.getSourceName();
571                     snapshot.put(key, storedSnapshot.get(key));
572                 }
573
574                 continue;
575             }
576
577             // target is resolved and we have an FK->PK to it,
578
// so extract it from target...
579
DataObject target = (DataObject) targetObject;
580             Map JavaDoc idParts = target.getObjectId().getIdSnapshot();
581
582             // this may happen in uncommitted objects
583
if (idParts.isEmpty()) {
584                 continue;
585             }
586
587             DbRelationship dbRel = (DbRelationship) rel.getDbRelationships().get(0);
588             Map JavaDoc fk = dbRel.srcFkSnapshotWithTargetSnapshot(idParts);
589             snapshot.putAll(fk);
590         }
591
592         // process object id map
593
// we should ignore any object id values if a corresponding attribute
594
// is a part of relationship "toMasterPK", since those values have been
595
// set above when db relationships where processed.
596
Map JavaDoc thisIdParts = anObject.getObjectId().getIdSnapshot();
597         if (thisIdParts != null) {
598             
599             // put only those that do not exist in the map
600
Iterator JavaDoc idIterator = thisIdParts.entrySet().iterator();
601             while (idIterator.hasNext()) {
602                 Map.Entry JavaDoc entry = (Map.Entry JavaDoc) idIterator.next();
603                 Object JavaDoc nextKey = entry.getKey();
604                 if (!snapshot.containsKey(nextKey)) {
605                     snapshot.put(nextKey, entry.getValue());
606                 }
607             }
608         }
609         
610         return snapshot;
611     }
612
613     /**
614      * Creates a list of DataObjects local to this DataContext from a list of DataObjects
615      * coming from a different DataContext. Note that all objects in the source list must
616      * be either in COMMITTED or in HOLLOW state.
617      *
618      * @since 1.0.3
619      */

620     public List JavaDoc localObjects(List JavaDoc objects) {
621         List JavaDoc localObjects = new ArrayList JavaDoc(objects.size());
622
623         Iterator JavaDoc it = objects.iterator();
624         while (it.hasNext()) {
625             DataObject object = (DataObject) it.next();
626
627             // sanity check
628
if (object.getPersistenceState() != PersistenceState.COMMITTED
629                     && object.getPersistenceState() != PersistenceState.HOLLOW) {
630                 throw new CayenneRuntimeException(
631                         "Only COMMITTED and HOLLOW objects can be transferred between contexts. "
632                                 + "Invalid object state '"
633                                 + PersistenceState.persistenceStateName(object
634                                         .getPersistenceState())
635                                 + "', ObjectId: "
636                                 + object.getObjectId());
637             }
638
639             DataObject localObject = (object.getDataContext() != this)
640                     ? registeredObject(object.getObjectId())
641                     : object;
642             localObjects.add(localObject);
643         }
644
645         return localObjects;
646     }
647
648     /**
649      * Converts a list of data rows to a list of DataObjects.
650      *
651      * @since 1.1
652      */

653     public List JavaDoc objectsFromDataRows(
654             ObjEntity entity,
655             List JavaDoc dataRows,
656             boolean refresh,
657             boolean resolveInheritanceHierarchy) {
658         
659         return new DataContextObjectFactory(this, refresh, resolveInheritanceHierarchy)
660                 .objectsFromDataRows(entity, dataRows);
661     }
662
663     /**
664      * Converts a list of DataRows to a List of DataObject registered with this
665      * DataContext. Internally calls
666      * {@link #objectsFromDataRows(ObjEntity,List,boolean,boolean)}.
667      *
668      * @since 1.1
669      * @see DataRow
670      * @see DataObject
671      */

672     public List JavaDoc objectsFromDataRows(
673             Class JavaDoc objectClass,
674             List JavaDoc dataRows,
675             boolean refresh,
676             boolean resolveInheritanceHierarchy) {
677         ObjEntity entity = this.getEntityResolver().lookupObjEntity(objectClass);
678         return objectsFromDataRows(entity, dataRows, refresh, resolveInheritanceHierarchy);
679     }
680
681     /**
682      * Creates a DataObject from DataRow. This is a convenience shortcut to
683      * {@link #objectsFromDataRows(Class,java.util.List,boolean,boolean)}.
684      *
685      * @see DataRow
686      * @see DataObject
687      */

688     public DataObject objectFromDataRow(
689             Class JavaDoc objectClass,
690             DataRow dataRow,
691             boolean refresh) {
692         List JavaDoc list = objectsFromDataRows(
693                 objectClass,
694                 Collections.singletonList(dataRow),
695                 refresh,
696                 true);
697         return (DataObject) list.get(0);
698     }
699
700
701     /**
702      * Instantiates new object and registers it with itself. Object class is determined
703      * from ObjEntity. Object class must have a default constructor.
704      * <p>
705      * <i>Note: preferred way to create new objects is via
706      * {@link #createAndRegisterNewObject(Class)}method. It works exactly the same way,
707      * but makes the application type-safe. </i>
708      * </p>
709      *
710      * @see #createAndRegisterNewObject(Class)
711      */

712     public DataObject createAndRegisterNewObject(String JavaDoc objEntityName) {
713         ObjEntity entity = this.getEntityResolver().getObjEntity(objEntityName);
714
715         if (entity == null) {
716             throw new IllegalArgumentException JavaDoc("Invalid entity name: " + objEntityName);
717         }
718
719         String JavaDoc objClassName = entity.getClassName();
720         DataObject dataObject = null;
721         try {
722             dataObject = DataContext.newDataObject(objClassName);
723         }
724         catch (Exception JavaDoc ex) {
725             throw new CayenneRuntimeException("Error instantiating object.", ex);
726         }
727
728         registerNewObjectWithEntity(dataObject, entity);
729         return dataObject;
730     }
731
732     /**
733      * Instantiates new object and registers it with itself. Object class must have a
734      * default constructor.
735      *
736      * @since 1.1
737      */

738     public DataObject createAndRegisterNewObject(Class JavaDoc objectClass) {
739         if (objectClass == null) {
740             throw new NullPointerException JavaDoc("DataObject class can't be null.");
741         }
742
743         ObjEntity entity = getEntityResolver().lookupObjEntity(objectClass);
744         if (entity == null) {
745             throw new IllegalArgumentException JavaDoc("Class is not mapped with Cayenne: "
746                     + objectClass.getName());
747         }
748
749         DataObject dataObject = null;
750         try {
751             dataObject = (DataObject) objectClass.newInstance();
752         }
753         catch (Exception JavaDoc ex) {
754             throw new CayenneRuntimeException("Error instantiating object.", ex);
755         }
756
757         registerNewObjectWithEntity(dataObject, entity);
758         return dataObject;
759     }
760
761
762     /**
763      * Registers a new object (that is not yet persistent) with itself.
764      *
765      * @param dataObject new object that we want to make persistent.
766      */

767     public void registerNewObject(DataObject dataObject) {
768         if (dataObject == null) {
769             throw new NullPointerException JavaDoc("Can't register null object.");
770         }
771
772         // sanity check - maybe already registered
773
if (dataObject.getObjectId() != null) {
774             if (dataObject.getDataContext() == this) {
775                 // already registered, just ignore
776
return;
777             }
778             else if (dataObject.getDataContext() != null) {
779                 throw new IllegalStateException JavaDoc(
780                         "DataObject is already registered with another DataContext. Try using 'localObjects()' instead.");
781             }
782         }
783
784         ObjEntity entity = getEntityResolver().lookupObjEntity(dataObject);
785         if (entity == null) {
786             throw new IllegalArgumentException JavaDoc(
787                     "Can't find ObjEntity for DataObject class: "
788                             + dataObject.getClass().getName()
789                             + ", class is likely not mapped.");
790         }
791
792         registerNewObjectWithEntity(dataObject, entity);
793     }
794
795     private void registerNewObjectWithEntity(DataObject dataObject, ObjEntity objEntity) {
796         // method is private ... assuming all sanity checks on the DataObject have been
797
// performed by the caller depending on the inocation context
798

799         if (dataObject.getObjectId() == null) {
800             dataObject.setObjectId(new TempObjectId(dataObject.getClass()));
801         }
802
803         // initialize to-many relationships with a fault
804
Iterator JavaDoc it = objEntity.getRelationships().iterator();
805         while (it.hasNext()) {
806             ObjRelationship rel = (ObjRelationship) it.next();
807             if (rel.isToMany()) {
808                 dataObject.writePropertyDirectly(rel.getName(), Fault.getToManyFault());
809             }
810         }
811
812         getObjectStore().addObject(dataObject);
813         dataObject.setDataContext(this);
814         dataObject.setPersistenceState(PersistenceState.NEW);
815     }
816
817     /**
818      * Unregisters a Collection of DataObjects from the DataContext and the underlying
819      * ObjectStore. This operation also unsets DataContext and ObjectId for each object
820      * and changes its state to TRANSIENT.
821      */

822     public void unregisterObjects(Collection JavaDoc dataObjects) {
823         getObjectStore().objectsUnregistered(dataObjects);
824     }
825
826     /**
827      * "Invalidates" a Collection of DataObject. This operation would remove each object's
828      * snapshot from cache and change object's state to HOLLOW. On the next access to this
829      * object, it will be refetched.
830      */

831     public void invalidateObjects(Collection JavaDoc dataObjects) {
832         getObjectStore().objectsInvalidated(dataObjects);
833     }
834
835     /**
836      * Schedules all objects in the collection for deletion on the next commit of this
837      * DataContext. Object's persistence state is changed to PersistenceState.DELETED;
838