KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cayenne > CayenneDataObject


1 /*****************************************************************
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License. You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied. See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  ****************************************************************/

19
20 package org.apache.cayenne;
21
22 import java.io.IOException JavaDoc;
23 import java.io.ObjectInputStream JavaDoc;
24 import java.io.ObjectOutputStream JavaDoc;
25 import java.lang.reflect.Array JavaDoc;
26 import java.util.Collection JavaDoc;
27 import java.util.HashMap JavaDoc;
28 import java.util.Iterator JavaDoc;
29 import java.util.List JavaDoc;
30 import java.util.Map JavaDoc;
31 import java.util.StringTokenizer JavaDoc;
32
33 import org.apache.cayenne.access.DataContext;
34 import org.apache.cayenne.conf.Configuration;
35 import org.apache.cayenne.map.DbAttribute;
36 import org.apache.cayenne.map.DbJoin;
37 import org.apache.cayenne.map.DbRelationship;
38 import org.apache.cayenne.map.EntityResolver;
39 import org.apache.cayenne.map.ObjAttribute;
40 import org.apache.cayenne.map.ObjEntity;
41 import org.apache.cayenne.map.ObjRelationship;
42 import org.apache.cayenne.reflect.PropertyUtils;
43 import org.apache.cayenne.validation.BeanValidationFailure;
44 import org.apache.cayenne.validation.ValidationFailure;
45 import org.apache.cayenne.validation.ValidationResult;
46 import org.apache.cayenne.xml.XMLDecoder;
47 import org.apache.cayenne.xml.XMLEncoder;
48 import org.apache.cayenne.xml.XMLSerializable;
49
50 /**
51  * A default implementation of DataObject interface. It is normally used as a superclass
52  * of Cayenne persistent objects.
53  *
54  * @author Andrus Adamchik
55  */

56 public class CayenneDataObject implements DataObject, Validating, XMLSerializable {
57
58     protected long snapshotVersion = DEFAULT_VERSION;
59
60     protected ObjectId objectId;
61     protected transient int persistenceState = PersistenceState.TRANSIENT;
62     protected transient ObjectContext objectContext;
63     protected Map JavaDoc values = new HashMap JavaDoc();
64
65     /**
66      * Returns a DataContext that holds this object. Object becomes assocaiated with a
67      * DataContext either when the object is fetched using a query, or when a new object
68      * is registered explicitly with a DataContext.
69      *
70      * @deprecated since 3.0 use {@link #getObjectContext()}.
71      */

72     public DataContext getDataContext() {
73         if (objectContext == null || objectContext instanceof DataContext) {
74             return (DataContext) objectContext;
75         }
76
77         throw new CayenneRuntimeException("ObjectContext is not a DataContext: "
78                 + objectContext);
79     }
80
81     /**
82      * Sets object DataContext.
83      *
84      * @deprecated since 3.0 use {@link #setObjectContext(ObjectContext)}.
85      */

86     public void setDataContext(DataContext dataContext) {
87         this.objectContext = dataContext;
88
89         if (dataContext == null) {
90             this.persistenceState = PersistenceState.TRANSIENT;
91         }
92     }
93
94     /**
95      * Returns mapped ObjEntity for this object. If an object is transient or is not
96      * mapped returns null.
97      *
98      * @since 1.2
99      */

100     public ObjEntity getObjEntity() {
101         return (getObjectContext() != null) ? getObjectContext()
102                 .getEntityResolver()
103                 .lookupObjEntity(this) : null;
104     }
105
106     public ObjectId getObjectId() {
107         return objectId;
108     }
109
110     public void setObjectId(ObjectId objectId) {
111         this.objectId = objectId;
112     }
113
114     public int getPersistenceState() {
115         return persistenceState;
116     }
117
118     public void setPersistenceState(int persistenceState) {
119         this.persistenceState = persistenceState;
120
121         if (persistenceState == PersistenceState.HOLLOW) {
122             values.clear();
123         }
124     }
125
126     public Object JavaDoc readNestedProperty(String JavaDoc path) {
127         Object JavaDoc object = null;
128         CayenneDataObject dataObject = this;
129         String JavaDoc[] tokenized = tokenizePath(path);
130         int length = tokenized.length;
131
132         int pathIndex = 0;
133
134         for (int i = 0; i < length; i++) {
135             pathIndex += tokenized[i].length() + 1;
136
137             object = dataObject.readSimpleProperty(tokenized[i]);
138
139             if (object == null) {
140                 return null;
141             }
142             else if (object instanceof CayenneDataObject) {
143                 dataObject = (CayenneDataObject) object;
144             }
145             else if (i + 1 < length) {
146                 // read the rest of the path via introspection
147
return PropertyUtils.getProperty(object, path.substring(pathIndex));
148             }
149         }
150
151         return object;
152     }
153
154     private static final String JavaDoc[] tokenizePath(String JavaDoc path) {
155         if (path == null) {
156             throw new NullPointerException JavaDoc("Null property path.");
157         }
158
159         if (path.length() == 0) {
160             throw new IllegalArgumentException JavaDoc("Empty property path.");
161         }
162
163         // take a shortcut for simple properties
164
if (path.indexOf(".") < 0) {
165             return new String JavaDoc[] {
166                 path
167             };
168         }
169
170         StringTokenizer JavaDoc tokens = new StringTokenizer JavaDoc(path, ".");
171         int length = tokens.countTokens();
172         String JavaDoc[] tokenized = new String JavaDoc[length];
173         for (int i = 0; i < length; i++) {
174             tokenized[i] = tokens.nextToken();
175         }
176
177         return tokenized;
178     }
179
180     private final Object JavaDoc readSimpleProperty(String JavaDoc property) {
181         // side effect - resolves HOLLOW object
182
Object JavaDoc object = readProperty(property);
183
184         // if a null value is returned, there is still a chance to
185
// find a non-persistent property via reflection
186
if (object == null && !values.containsKey(property)) {
187             object = PropertyUtils.getProperty(this, property);
188         }
189
190         return object;
191     }
192
193     public Object JavaDoc readProperty(String JavaDoc propertyName) {
194         if (objectContext != null) {
195             // will resolve faults ourselves below as checking class descriptors for the
196
// "lazyFaulting" flag is inefficient. Passing "false" here to suppress fault
197
// processing
198
objectContext.prepareForAccess(this, propertyName, false);
199         }
200
201         Object JavaDoc object = readPropertyDirectly(propertyName);
202
203         if (object instanceof Fault) {
204             object = ((Fault) object).resolveFault(this, propertyName);
205             writePropertyDirectly(propertyName, object);
206         }
207
208         return object;
209     }
210
211     public Object JavaDoc readPropertyDirectly(String JavaDoc propName) {
212         return values.get(propName);
213     }
214
215     public void writeProperty(String JavaDoc propName, Object JavaDoc val) {
216         if (objectContext != null) {
217             // pass "false" to avoid unneeded fault processing
218
objectContext.prepareForAccess(this, propName, false);
219
220             // note how we notify ObjectContext of change BEFORE the object is actually
221
// changed... this is needed to take a valid current snapshot
222
Object JavaDoc oldValue = readPropertyDirectly(propName);
223             objectContext.propertyChanged(this, propName, oldValue, val);
224         }
225
226         writePropertyDirectly(propName, val);
227     }
228
229     public void writePropertyDirectly(String JavaDoc propName, Object JavaDoc val) {
230         values.put(propName, val);
231     }
232
233     public void removeToManyTarget(String JavaDoc relName, DataObject value, boolean setReverse) {
234
235         // Now do the rest of the normal handling (regardless of whether it was
236
// flattened or not)
237
List JavaDoc relList = (List JavaDoc) readProperty(relName);
238
239         // call 'propertyChanged' AFTER readProperty as readProperty ensures that this
240
// object fault is resolved
241
getObjectContext().propertyChanged(this, relName, value, null);
242
243         relList.remove(value);
244
245         if (value != null && setReverse) {
246             unsetReverseRelationship(relName, value);
247         }
248     }
249
250     public void addToManyTarget(String JavaDoc relName, DataObject value, boolean setReverse) {
251         if (value == null) {
252             throw new NullPointerException JavaDoc("Attempt to add null target DataObject.");
253         }
254
255         willConnect(relName, value);
256
257         // Now do the rest of the normal handling (regardless of whether it was
258
// flattened or not)
259
List JavaDoc list = (List JavaDoc) readProperty(relName);
260
261         // call 'propertyChanged' AFTER readProperty as readProperty ensures that this
262
// object fault is resolved
263
getObjectContext().propertyChanged(this, relName, null, value);
264
265         list.add(value);
266
267         if (value != null && setReverse) {
268             setReverseRelationship(relName, value);
269         }
270     }
271
272     public void setToOneTarget(
273             String JavaDoc relationshipName,
274             DataObject value,
275             boolean setReverse) {
276
277         willConnect(relationshipName, value);
278
279         Object JavaDoc oldTarget = readProperty(relationshipName);
280         if (oldTarget == value) {
281             return;
282         }
283
284         getObjectContext().propertyChanged(this, relationshipName, oldTarget, value);
285
286         if (setReverse) {
287             // unset old reverse relationship
288
if (oldTarget instanceof DataObject) {
289                 unsetReverseRelationship(relationshipName, (DataObject) oldTarget);
290             }
291
292             // set new reverse relationship
293
if (value != null) {
294                 setReverseRelationship(relationshipName, value);
295             }
296         }
297
298         objectContext.prepareForAccess(this, relationshipName, false);
299         writePropertyDirectly(relationshipName, value);
300     }
301
302     /**
303      * Called before establishing a relationship with another object. Applies "persistence
304      * by reachability" logic, pulling one of the two objects to a DataConext of another
305      * object in case one of the objects is transient. If both objects are persistent, and
306      * they don't have the same DataContext, CayenneRuntimeException is thrown.
307      *
308      * @since 1.2
309      */

310     protected void willConnect(String JavaDoc relationshipName, Persistent object) {
311         // first handle most common case - both objects are in the same
312
// ObjectContext or target is null
313
if (object == null || this.getObjectContext() == object.getObjectContext()) {
314             return;
315         }
316         else if (this.getObjectContext() == null && object.getObjectContext() != null) {
317             object.getObjectContext().registerNewObject(this);
318         }
319         else if (this.getObjectContext() != null && object.getObjectContext() == null) {
320             this.getObjectContext().registerNewObject(object);
321         }
322         else {
323             throw new CayenneRuntimeException(
324                     "Cannot set object as destination of relationship "
325                             + relationshipName
326                             + " because it is in a different ObjectContext");
327         }
328     }
329
330     /**
331      * Initializes reverse relationship from object <code>val</code> to this object.
332      *
333      * @param relName name of relationship from this object to <code>val</code>.
334      */

335     protected void setReverseRelationship(String JavaDoc relName, DataObject val) {
336         ObjRelationship rel = (ObjRelationship) objectContext
337                 .getEntityResolver()
338                 .getObjEntity(objectId.getEntityName())
339                 .getRelationship(relName);
340         ObjRelationship revRel = rel.getReverseRelationship();
341         if (revRel != null) {
342             if (revRel.isToMany())
343                 val.addToManyTarget(revRel.getName(), this, false);
344             else
345                 val.setToOneTarget(revRel.getName(), this, false);
346         }
347     }
348
349     /**
350      * Removes current object from reverse relationship of object <code>val</code> to
351      * this object.
352      */

353     protected void unsetReverseRelationship(String JavaDoc relName, DataObject val) {
354
355         EntityResolver resolver = objectContext.getEntityResolver();
356         ObjEntity entity = resolver.getObjEntity(objectId.getEntityName());
357
358         if (entity == null) {
359             throw new IllegalStateException JavaDoc("DataObject's entity is unmapped, objectId: "
360                     + objectId);
361         }
362
363         ObjRelationship rel = (ObjRelationship) entity.getRelationship(relName);
364         ObjRelationship revRel = rel.getReverseRelationship();
365         if (revRel != null) {
366             if (revRel.isToMany())
367                 val.removeToManyTarget(revRel.getName(), this, false);
368             else
369                 val.setToOneTarget(revRel.getName(), null, false);
370         }
371     }
372
373     /**
374      * A variation of "toString" method, that may be more efficient in some cases. For
375      * example when printing a list of objects into the same String.
376      */

377     public StringBuffer JavaDoc toStringBuffer(StringBuffer JavaDoc buffer, boolean fullDesc) {
378         String JavaDoc id = (objectId != null) ? objectId.toString() : "<no id>";
379         String JavaDoc state = PersistenceState.persistenceStateName(persistenceState);
380
381         buffer.append('{').append(id).append("; ").append(state).append("; ");
382
383         if (fullDesc) {
384             appendProperties(buffer);
385         }
386
387         buffer.append("}");
388         return buffer;
389     }
390
391     protected void appendProperties(StringBuffer JavaDoc buffer) {
392         buffer.append("[");
393         Iterator JavaDoc it = values.entrySet().iterator();
394
395         while (it.hasNext()) {
396             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) it.next();
397
398             buffer.append(entry.getKey()).append("=>");
399             Object JavaDoc value = entry.getValue();
400
401             if (value instanceof Persistent) {
402                 buffer.append('{').append(((Persistent) value).getObjectId()).append('}');
403             }
404             else if (value instanceof Collection JavaDoc) {
405                 buffer.append("(..)");
406             }
407             else if (value instanceof Fault) {
408                 buffer.append('?');
409             }
410             else {
411                 buffer.append(value);
412             }
413
414             if (it.hasNext()) {
415                 buffer.append("; ");
416             }
417         }
418
419         buffer.append("]");
420     }
421
422     public String JavaDoc toString() {
423         return toStringBuffer(new StringBuffer JavaDoc(), true).toString();
424     }
425
426     /**
427      * Default implementation does nothing.
428      *
429      * @deprecated since 3.0 use callbacks.
430      * @see LifecycleListener
431      */

432     public void fetchFinished() {
433     }
434
435     private void writeObject(ObjectOutputStream JavaDoc out) throws IOException JavaDoc {
436         out.writeInt(persistenceState);
437
438         switch (persistenceState) {
439             // New, modified or transient or deleted - write the whole shebang
440
// The other states (committed, hollow) all need just ObjectId
441
case PersistenceState.TRANSIENT:
442             case PersistenceState.NEW:
443             case PersistenceState.MODIFIED:
444             case PersistenceState.DELETED:
445                 out.writeObject(values);
446                 break;
447         }
448
449         out.writeObject(objectId);
450     }
451
452     private void readObject(ObjectInputStream JavaDoc in) throws IOException JavaDoc,
453             ClassNotFoundException JavaDoc {
454         this.persistenceState = in.readInt();
455
456         switch (persistenceState) {
457             case PersistenceState.TRANSIENT:
458             case PersistenceState.NEW:
459             case PersistenceState.MODIFIED:
460             case PersistenceState.DELETED:
461                 values = (Map JavaDoc) in.readObject();
462                 break;
463             case PersistenceState.COMMITTED:
464             case PersistenceState.HOLLOW:
465                 this.persistenceState = PersistenceState.HOLLOW;
466                 // props will be populated when required (readProperty called)
467
values = new HashMap JavaDoc();
468                 break;
469         }
470
471         this.objectId = (ObjectId) in.readObject();
472
473         // DataContext will be set *IF* the DataContext it came from is also
474
// deserialized. Setting of DataContext is handled by the DataContext
475
// itself
476
}
477
478     /**
479      * Returns a version of a DataRow snapshot that was used to create this object.
480      *
481      * @since 1.1
482      */

483     public long getSnapshotVersion() {
484         return snapshotVersion;
485     }
486
487     /**
488      * @since 1.1
489      */

490     public void setSnapshotVersion(long snapshotVersion) {
491         this.snapshotVersion = snapshotVersion;
492     }
493
494     /**
495      * Performs property validation of the object, appending any validation failures to
496      * the provided validationResult object. This method is invoked from "validateFor.."
497      * before committing a NEW or MODIFIED object to the database. Validation includes
498      * checking for null values and value sizes. CayenneDataObject subclasses may override
499      * this method, calling super.
500      *
501      * @since 1.1
502      */

503     protected void validateForSave(ValidationResult validationResult) {
504
505         ObjEntity objEntity = getObjectContext()
506                 .getEntityResolver()
507                 .lookupObjEntity(this);
508         if (objEntity == null) {
509             throw new CayenneRuntimeException(
510                     "No ObjEntity mapping found for DataObject " + getClass().getName());
511         }
512
513         // validate mandatory attributes
514

515         // handling a special case - meaningful mandatory FK... defer failures until
516
// relationship validation is done... This is just a temporary solution, as
517
// handling meaningful keys within the object lifecycle requires something more,
518
// namely read/write methods for relationships and direct values should be
519
// synchronous with each other..
520
Map JavaDoc failedDbAttributes = null;
521
522         Iterator JavaDoc attributes = objEntity.getAttributes().iterator();
523         while (attributes.hasNext()) {
524             Object JavaDoc next = attributes.next();
525
526             // TODO: andrus, 2/20/2007 - handle embedded attribute
527
if (!(next instanceof ObjAttribute)) {
528                 continue;
529             }
530
531             ObjAttribute objAttribute = (ObjAttribute) next;
532             DbAttribute dbAttribute = objAttribute.getDbAttribute();
533
534             // pk may still be generated
535
if (dbAttribute.isPrimaryKey()) {
536                 continue;
537             }
538
539             Object JavaDoc value = this.readPropertyDirectly(objAttribute.getName());
540             if (dbAttribute.isMandatory()) {
541                 ValidationFailure failure = BeanValidationFailure.validateNotNull(
542                         this,
543                         objAttribute.getName(),
544                         value);
545
546                 if (failure != null) {
547
548                     if (failedDbAttributes == null) {
549                         failedDbAttributes = new HashMap JavaDoc();
550                     }
551
552                     failedDbAttributes.put(dbAttribute.getName(), failure);
553                     continue;
554                 }
555             }
556
557             // validate length
558
if (value != null && dbAttribute.getMaxLength() > 0) {
559
560                 if (value.getClass().isArray()) {
561                     int len = Array.getLength(value);
562                     if (len > dbAttribute.getMaxLength()) {
563                         String JavaDoc message = "\""
564                                 + objAttribute.getName()
565                                 + "\" exceeds maximum allowed length ("
566                                 + dbAttribute.getMaxLength()
567                                 + " bytes): "
568                                 + len;
569                         validationResult.addFailure(new BeanValidationFailure(
570                                 this,
571                                 objAttribute.getName(),
572                                 message));
573                     }
574                 }
575                 else if (value instanceof CharSequence JavaDoc) {
576                     int len = ((CharSequence JavaDoc) value).length();
577                     if (len > dbAttribute.getMaxLength()) {
578                         String JavaDoc message = "\""
579                                 + objAttribute.getName()
580                                 + "\" exceeds maximum allowed length ("
581                                 + dbAttribute.getMaxLength()
582                                 + " chars): "
583                                 + len;
584                         validationResult.addFailure(new BeanValidationFailure(
585                                 this,
586                                 objAttribute.getName(),
587                                 message));
588                     }
589                 }
590             }
591         }
592
593         // validate mandatory relationships
594
Iterator JavaDoc relationships = objEntity.getRelationships().iterator();
595         while (relationships.hasNext()) {
596             ObjRelationship relationship = (ObjRelationship) relationships.next();
597
598             if (relationship.isSourceIndependentFromTargetChange()) {
599                 continue;
600             }
601
602             List JavaDoc dbRels = relationship.getDbRelationships();
603             if (dbRels.isEmpty()) {
604                 // Wha?
605
continue;
606             }
607
608             // if db relationship is not based on a PK and is based on mandatory
609
// attributes, see if we have a target object set
610
boolean validate = true;
611             DbRelationship dbRelationship = (DbRelationship) dbRels.get(0);
612             Iterator JavaDoc joins = dbRelationship.getJoins().iterator();
613             while (joins.hasNext()) {
614                 DbJoin join = (DbJoin) joins.next();
615                 DbAttribute source = join.getSource();
616
617                 if (source.isMandatory()) {
618                     // clear attribute failures...
619
if (failedDbAttributes != null && !failedDbAttributes.isEmpty()) {
620                         failedDbAttributes.remove(source.getName());
621
622                         // loop through all joins if there were previous mandatory
623

624                         // attribute failures....
625
if (!failedDbAttributes.isEmpty()) {
626                             continue;
627                         }
628                     }
629                 }
630                 else {
631                     // do not validate if the relation is based on
632
// multiple keys with some that can be nullable.
633
validate = false;
634                 }
635             }
636
637             if (validate) {
638                 Object JavaDoc value = this.readPropertyDirectly(relationship.getName());
639                 ValidationFailure failure = BeanValidationFailure.validateNotNull(
640                         this,
641                         relationship.getName(),
642                         value);
643
644                 if (failure != null) {
645                     validationResult.addFailure(failure);
646                     continue;
647                 }
648             }
649
650         }
651
652         // deal with previously found attribute failures...
653
if (failedDbAttributes != null && !failedDbAttributes.isEmpty()) {
654             Iterator JavaDoc failedAttributes = failedDbAttributes.values().iterator();
655             while (failedAttributes.hasNext()) {
656                 validationResult.addFailure((ValidationFailure) failedAttributes.next());
657             }
658         }
659     }
660
661     /**
662      * Calls {@link #validateForSave(ValidationResult)}. CayenneDataObject subclasses may
663      * override it providing validation logic that should be executed for the newly
664      * created objects before saving them.
665      *
666      * @since 1.1
667      */

668     public void validateForInsert(ValidationResult validationResult) {
669         validateForSave(validationResult);
670     }
671
672     /**
673      * Calls {@link #validateForSave(ValidationResult)}. CayenneDataObject subclasses may
674      * override it providing validation logic that should be executed for the modified
675      * objects before saving them.
676      *
677      * @since 1.1
678      */

679     public void validateForUpdate(ValidationResult validationResult) {
680         validateForSave(validationResult);
681     }
682
683     /**
684      * This implementation does nothing. CayenneDataObject subclasses may override it
685      * providing validation logic that should be executed for the deleted objects before
686      * committing them.
687      *
688      * @since 1.1
689      */

690     public void validateForDelete(ValidationResult validationResult) {
691         // does nothing
692
}
693
694     /**
695      * Encodes object to XML using provided encoder.
696      *
697      * @since 1.2
698      */

699     public void encodeAsXML(XMLEncoder encoder) {
700         EntityResolver er = getObjectContext().getEntityResolver();
701         ObjEntity object = er.lookupObjEntity(getClass());
702
703         String JavaDoc[] fields = this.getClass().getName().split("\\.");
704         encoder.setRoot(fields[fields.length - 1], this.getClass().getName());
705
706         for (Iterator JavaDoc it = object.getDeclaredAttributes().iterator(); it.hasNext();) {
707             ObjAttribute att = (ObjAttribute) it.next();
708             String JavaDoc name = att.getName();
709             encoder.encodeProperty(name, readNestedProperty(name));
710         }
711     }
712
713     public void decodeFromXML(XMLDecoder decoder) {
714
715         // TODO: (andrus, long time ago) relying on singleton Configuration and a single
716
// DataDomain is a very bad idea... Decoder itself can optionally store a
717
// DataContext or an EntityResolver to provide the context appropriate for a given
718
// environment
719
EntityResolver resolver = Configuration
720                 .getSharedConfiguration()
721                 .getDomain()
722                 .getEntityResolver();
723         ObjEntity objectEntity = resolver.lookupObjEntity(getClass());
724
725         for (Iterator JavaDoc it = objectEntity.getDeclaredAttributes().iterator(); it.hasNext();) {
726             ObjAttribute att = (ObjAttribute) it.next();
727             String JavaDoc name = att.getName();
728             writeProperty(name, decoder.decodeObject(name));
729         }
730     }
731
732     /**
733      * Returns this object's DataContext.
734      *
735      * @since 1.2
736      */

737     public ObjectContext getObjectContext() {
738         return objectContext;
739     }
740
741     /**
742      * @since 1.2
743      */

744     public void setObjectContext(ObjectContext objectContext) {
745         this.objectContext = objectContext;
746
747         if (objectContext == null) {
748             this.persistenceState = PersistenceState.TRANSIENT;
749         }
750     }
751 }
752
Popular Tags