KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > hibernate > engine > ForeignKeys


1 //$Id: ForeignKeys.java,v 1.16 2005/05/27 03:53:58 oneovthafew Exp $
2
package org.hibernate.engine;
3
4 import java.io.Serializable JavaDoc;
5
6 import org.hibernate.HibernateException;
7 import org.hibernate.TransientObjectException;
8 import org.hibernate.intercept.LazyPropertyInitializer;
9 import org.hibernate.persister.entity.EntityPersister;
10 import org.hibernate.proxy.HibernateProxy;
11 import org.hibernate.proxy.LazyInitializer;
12 import org.hibernate.type.AbstractComponentType;
13 import org.hibernate.type.EntityType;
14 import org.hibernate.type.Type;
15
16 /**
17  * Algorithms related to foreign key constraint transparency
18  *
19  * @author Gavin King
20  */

21 public final class ForeignKeys {
22     
23     private ForeignKeys() {}
24     
25     public static class Nullifier {
26     
27         private final boolean isDelete;
28         private final boolean isEarlyInsert;
29         private final SessionImplementor session;
30         private final Object JavaDoc self;
31         
32         public Nullifier(Object JavaDoc self, boolean isDelete, boolean isEarlyInsert, SessionImplementor session) {
33             this.isDelete = isDelete;
34             this.isEarlyInsert = isEarlyInsert;
35             this.session = session;
36             this.self = self;
37         }
38         
39         /**
40          * Nullify all references to entities that have not yet
41          * been inserted in the database, where the foreign key
42          * points toward that entity
43          */

44         public void nullifyTransientReferences(final Object JavaDoc[] values, final Type[] types)
45         throws HibernateException {
46             for ( int i = 0; i < types.length; i++ ) {
47                 values[i] = nullifyTransientReferences( values[i], types[i] );
48             }
49         }
50     
51         /**
52          * Return null if the argument is an "unsaved" entity (ie.
53          * one with no existing database row), or the input argument
54          * otherwise. This is how Hibernate avoids foreign key constraint
55          * violations.
56          */

57         private Object JavaDoc nullifyTransientReferences(final Object JavaDoc value, final Type type)
58         throws HibernateException {
59             if ( value == null ) {
60                 return null;
61             }
62             else if ( type.isEntityType() ) {
63                 EntityType entityType = (EntityType) type;
64                 if ( entityType.isOneToOne() ) {
65                     return value;
66                 }
67                 else {
68                     String JavaDoc entityName = entityType.getAssociatedEntityName();
69                     return isNullifiable(entityName, value) ? null : value;
70                 }
71             }
72             else if ( type.isAnyType() ) {
73                 return isNullifiable(null, value) ? null : value;
74             }
75             else if ( type.isComponentType() ) {
76                 AbstractComponentType actype = (AbstractComponentType) type;
77                 Object JavaDoc[] subvalues = actype.getPropertyValues(value, session);
78                 Type[] subtypes = actype.getSubtypes();
79                 boolean substitute = false;
80                 for ( int i = 0; i < subvalues.length; i++ ) {
81                     Object JavaDoc replacement = nullifyTransientReferences( subvalues[i], subtypes[i] );
82                     if ( replacement != subvalues[i] ) {
83                         substitute = true;
84                         subvalues[i] = replacement;
85                     }
86                 }
87                 if (substitute) actype.setPropertyValues( value, subvalues, session.getEntityMode() );
88                 return value;
89             }
90             else {
91                 return value;
92             }
93         }
94     
95         /**
96          * Determine if the object already exists in the database,
97          * using a "best guess"
98          */

99         private boolean isNullifiable(final String JavaDoc entityName, Object JavaDoc object)
100         throws HibernateException {
101             
102             if (object==LazyPropertyInitializer.UNFETCHED_PROPERTY) return false; //this is kinda the best we can do...
103

104             if ( object instanceof HibernateProxy ) {
105                 // if its an uninitialized proxy it can't be transient
106
LazyInitializer li = ( (HibernateProxy) object ).getHibernateLazyInitializer();
107                 if ( li.getImplementation(session) == null ) {
108                     return false;
109                     // ie. we never have to null out a reference to
110
// an uninitialized proxy
111
}
112                 else {
113                     //unwrap it
114
object = li.getImplementation();
115                 }
116             }
117     
118             // if it was a reference to self, don't need to nullify
119
// unless we are using native id generation, in which
120
// case we definitely need to nullify
121
if ( object == self ) {
122                 return isEarlyInsert || (
123                     isDelete &&
124                     session.getFactory()
125                         .getDialect()
126                         .hasSelfReferentialForeignKeyBug()
127                 );
128             }
129     
130             // See if the entity is already bound to this session, if not look at the
131
// entity identifier and assume that the entity is persistent if the
132
// id is not "unsaved" (that is, we rely on foreign keys to keep
133
// database integrity)
134

135             EntityEntry entityEntry = session.getPersistenceContext().getEntry(object);
136             if ( entityEntry==null ) {
137                 return isTransient(entityName, object, null, session);
138             }
139             else {
140                 return entityEntry.isNullifiable(isEarlyInsert, session);
141             }
142     
143         }
144         
145     }
146     
147     /**
148      * Is this instance persistent or detached?
149      * If <tt>assumed</tt> is non-null, don't hit the database to make the
150      * determination, instead assume that value; the client code must be
151      * prepared to "recover" in the case that this assumed result is incorrect.
152      */

153     public static boolean isNotTransient(String JavaDoc entityName, Object JavaDoc entity, Boolean JavaDoc assumed, SessionImplementor session)
154     throws HibernateException {
155         if (entity instanceof HibernateProxy) return true;
156         if ( session.getPersistenceContext().isEntryFor(entity) ) return true;
157         return !isTransient(entityName, entity, assumed, session);
158     }
159     
160     /**
161      * Is this instance, which we know is not persistent, actually transient?
162      * If <tt>assumed</tt> is non-null, don't hit the database to make the
163      * determination, instead assume that value; the client code must be
164      * prepared to "recover" in the case that this assumed result is incorrect.
165      */

166     public static boolean isTransient(String JavaDoc entityName, Object JavaDoc entity, Boolean JavaDoc assumed, SessionImplementor session)
167     throws HibernateException {
168         
169         if (entity==LazyPropertyInitializer.UNFETCHED_PROPERTY) {
170             // an unfetched association can only point to
171
// an entity that already exists in the db
172
return false;
173         }
174         
175         // let the interceptor inspect the instance to decide
176
Boolean JavaDoc isUnsaved = session.getInterceptor().isTransient(entity);
177         if (isUnsaved!=null) return isUnsaved.booleanValue();
178         
179         // let the persister inspect the instance to decide
180
EntityPersister persister = session.getEntityPersister(entityName, entity);
181         isUnsaved = persister.isTransient(entity, session);
182         if (isUnsaved!=null) return isUnsaved.booleanValue();
183
184         // we use the assumed value, if there is one, to avoid hitting
185
// the database
186
if (assumed!=null) return assumed.booleanValue();
187         
188         // hit the database, after checking the session cache for a snapshot
189
Object JavaDoc[] snapshot = session.getPersistenceContext()
190                 .getDatabaseSnapshot( persister.getIdentifier( entity, session.getEntityMode() ), persister );
191         return snapshot==null;
192
193     }
194
195     /**
196      * Return the identifier of the persistent or transient object, or throw
197      * an exception if the instance is "unsaved"
198      *
199      * Used by OneToOneType and ManyToOneType to determine what id value should
200      * be used for an object that may or may not be associated with the session.
201      * This does a "best guess" using any/all info available to use (not just the
202      * EntityEntry).
203      */

204     public static Serializable JavaDoc getEntityIdentifierIfNotUnsaved(
205             final String JavaDoc entityName,
206             final Object JavaDoc object,
207             final SessionImplementor session)
208     throws HibernateException {
209         if ( object == null ) {
210             return null;
211         }
212         else {
213             Serializable JavaDoc id = session.getContextEntityIdentifier(object);
214             if ( id==null ) {
215                 if ( isTransient(entityName, object, Boolean.FALSE, session) ) {
216                     throw new TransientObjectException(
217                             "object references an unsaved transient instance - save the transient instance before flushing: " +
218                             session.guessEntityName(object)
219                     );
220                 }
221                 id = session.getEntityPersister(entityName, object)
222                         .getIdentifier( object, session.getEntityMode() );
223             }
224             return id;
225         }
226     }
227
228 }
229
Popular Tags