📄 sessionimpl.java
字号:
// Sub-insertions should occur before containing insertion so // Try to do the callback now if ( persister.implementsLifecycle() ) { log.debug("calling onSave()"); if ( ( (Lifecycle) object ).onSave(this) ) { log.debug("insertion vetoed by onSave()"); return id; } } return doSave(object, key, persister, false, useIdentityColumn, cascadeAction, anything); } private Serializable doSave( final Object object, Key key, final ClassPersister persister, final boolean replicate, final boolean useIdentityColumn, final Cascades.CascadingAction cascadeAction, final Object anything) throws HibernateException { if ( persister.implementsValidatable() ) ( (Validatable) object ).validate(); Serializable id = useIdentityColumn ? null : key.getIdentifier(); // Put a placeholder in entries, so we don't recurse back and try to save() the // same object again. QUESTION: should this be done before onSave() is called? // likewise, should it be done before onUpdate()? addEntry(object, SAVING, null, id, null, LockMode.WRITE, useIdentityColumn, persister, false); //okay if id is null here // cascade-save to many-to-one BEFORE the parent is saved cascading++; try { Cascades.cascade(this, persister, object, cascadeAction, Cascades.CASCADE_BEFORE_INSERT_AFTER_DELETE, anything); } finally { cascading--; } Object[] values = persister.getPropertyValues(object); Type[] types = persister.getPropertyTypes(); boolean substitute = false; if (!replicate) { substitute = interceptor.onSave( object, id, values, persister.getPropertyNames(), types ); //keep the existing version number in the case of replicate! if ( persister.isVersioned() ) { substitute = Versioning.seedVersion( values, persister.getVersionProperty(), persister.getVersionType() ) || substitute; } } else if ( persister.hasCollections() ) { OnReplicateVisitor visitor = new OnReplicateVisitor(this, id); visitor.processValues(values, types); } if (substitute) persister.setPropertyValues(object, values); TypeFactory.deepCopy(values, types, persister.getPropertyUpdateability(), values); nullifyTransientReferences(values, types, useIdentityColumn, object); checkNullability(values, persister, false); if (useIdentityColumn) { id = persister.insert(values, object, this); //TODO: does not add newly inserted object to the second-level cache key = new Key(id, persister); checkUniqueness(key, object); persister.setIdentifier(object, id); } Object version = Versioning.getVersion(values, persister); addEntity(key, object); addEntry(object, LOADED, values, id, version, LockMode.WRITE, useIdentityColumn, persister, replicate); if (!useIdentityColumn) insertions.add( new ScheduledInsertion( id, values, object, persister, this ) ); // cascade-save to collections AFTER the collection owner was saved cascading++; try { Cascades.cascade(this, persister, object, cascadeAction, Cascades.CASCADE_AFTER_INSERT_BEFORE_DELETE, anything); } finally { cascading--; } return id; } boolean reassociateIfUninitializedProxy(Object value) throws MappingException { if ( !Hibernate.isInitialized(value) ) { HibernateProxy proxy = (HibernateProxy) value; LazyInitializer li = HibernateProxyHelper.getLazyInitializer(proxy); reassociateProxy(li, proxy); return true; } else { return false; } } private void reassociateProxy(Object value, Serializable id) throws MappingException { if (value instanceof HibernateProxy) { HibernateProxy proxy = (HibernateProxy) value; LazyInitializer li = HibernateProxyHelper.getLazyInitializer(proxy); li.setIdentifier(id); reassociateProxy(li, proxy); } } private Object unproxy(Object maybeProxy) throws HibernateException { if ( maybeProxy instanceof HibernateProxy ) { HibernateProxy proxy = (HibernateProxy) maybeProxy; LazyInitializer li = HibernateProxyHelper.getLazyInitializer(proxy); if ( li.isUninitialized() ) throw new PersistentObjectException( "object was an uninitialized proxy for: " + li.getPersistentClass().getName() ); return li.getImplementation(); //unwrap the object } else { return maybeProxy; } } private Object unproxyAndReassociate(Object maybeProxy) throws HibernateException { if ( maybeProxy instanceof HibernateProxy ) { HibernateProxy proxy = (HibernateProxy) maybeProxy; LazyInitializer li = HibernateProxyHelper.getLazyInitializer(proxy); reassociateProxy(li, proxy); return li.getImplementation(); //initialize + unwrap the object } else { return maybeProxy; } } /** * associate a proxy that was instantiated by another session with this session */ private void reassociateProxy(LazyInitializer li, HibernateProxy proxy) throws MappingException { if ( li.getSession()!=this ) { ClassPersister persister = getClassPersister( li.getPersistentClass() ); Key key = new Key( li.getIdentifier(), persister ); if ( !proxiesByKey.containsKey(key) ) proxiesByKey.put(key, proxy); // any earlier proxy takes precedence HibernateProxyHelper.getLazyInitializer( proxy ).setSession(this); } } private void nullifyTransientReferences(Object[] values, Type[] types, boolean earlyInsert, Object self) throws HibernateException { for ( int i=0; i<types.length; i++ ) { values[i] = nullifyTransientReferences( values[i], types[i], earlyInsert, self ); } } /** * Return null if the argument is an "unsaved" entity (ie. one with no existing database row), * or the input argument otherwise. This is how Hibernate avoids foreign key constraint * violations. */ private Object nullifyTransientReferences(Object value, Type type, boolean earlyInsert, Object self) throws HibernateException { if ( value==null ) { return null; } else if ( type.isEntityType() || type.isObjectType() ) { return ( isUnsaved(value, earlyInsert, self) ) ? null : value; } else if ( type.isComponentType() ) { AbstractComponentType actype = (AbstractComponentType) type; Object[] subvalues = actype.getPropertyValues(value, this); Type[] subtypes = actype.getSubtypes(); boolean substitute=false; for ( int i=0; i<subvalues.length; i++ ) { Object replacement = nullifyTransientReferences( subvalues[i], subtypes[i], earlyInsert, self ); if ( replacement!=subvalues[i] ) { substitute=true; subvalues[i]=replacement; } } if (substitute) actype.setPropertyValues(value, subvalues); return value; } else { return value; } } /** * determine if the object already exists in the database, using a * "best guess" */ private boolean isUnsaved(Object object, boolean earlyInsert, Object self) throws HibernateException { if ( object instanceof HibernateProxy ) { // if its an uninitialized proxy it can't be transient LazyInitializer li = HibernateProxyHelper.getLazyInitializer( (HibernateProxy) object ); if ( li.getImplementation(this)==null ) { return false; // ie. we never have to null out a reference to // an uninitialized proxy } else { //unwrap it object = li.getImplementation(); } } // if it was a reference to self, don't need to nullify // unless we are using native id generation, in which // case we definitely need to nullify if (object==self) return earlyInsert; // See if the entity is already bound to this session, if not look at the // entity identifier and assume that the entity is persistent if the // id is not "unsaved" (that is, we rely on foreign keys to keep // database integrity) EntityEntry e = getEntry(object); if (e==null) { return getPersister(object).isUnsaved(object); /*if( persister.hasIdentifierPropertyOrEmbeddedCompositeIdentifier() ) { Serializable id = persister.getIdentifier(object); if (id!=null) { // see if theres another object that *is* associated with the session for that id e = getEntry( getEntity( new Key(id, persister) ) ); if (e==null) { // look at the id value return persister.isUnsaved(object); } // else use the other object's entry.... } else { // null id, so have to assume transient (because thats safer) return true; } } else { // can't determine the id, so assume transient (because thats safer) return true; }*/ } return e.status==SAVING || ( earlyInsert ? !e.existsInDatabase : nullifiables.contains( new Key (e.id, e.persister) ) ); } /** * Delete a persistent object */ public void delete(Object object) throws HibernateException { if (object==null) throw new NullPointerException("attempted to delete null"); object = unproxyAndReassociate(object); EntityEntry entry = getEntry(object); final ClassPersister persister; if (entry==null) { log.trace("deleting a transient instance"); persister = getPersister(object); Serializable id = persister.getIdentifier(object); if (id==null) throw new TransientObjectException("the transient instance passed to delete() had a null identifier"); Object old = getEntity( new Key(id, persister) ); if (old!=null) throw new NonUniqueObjectException( id, persister.getMappedClass() ); new OnUpdateVisitor(this, id).process(object, persister); addEntity( new Key(id, persister), object ); entry = addEntry( object, LOADED, persister.getPropertyValues(object), id, persister.getVersion(object), LockMode.NONE, true, persister, false ); // not worth worrying about the proxy } else { log.trace("deleting a persistent instance"); if ( entry.status==DELETED || entry.status==GONE ) { log.trace("object was already deleted"); return; } persister = entry.persister; } if ( !persister.isMutable() ) throw new HibernateException( "attempted to delete an object of immutable class: " + MessageHelper.infoString(persister) ); doDelete(object, entry, persister); } private void doDelete(Object object, EntityEntry entry, ClassPersister persister) throws HibernateException { if ( log.isTraceEnabled() ) log.trace( "deleting " + MessageHelper.infoString(persister, entry.id) ); Type[] propTypes = persister.getPropertyTypes(); Object version = entry.version;//getCurrentVersion(); if ( entry.loadedState==null ) { //ie. the object came in from update() entry.deletedState = persister.getPropertyValues(object); } else { entry.deletedState = new Object[entry.loadedState.length]; TypeFactory.deepCopy(entry.loadedState, propTypes, persister.getPropertyUpdateability(), entry.deletedState); } interceptor.onDelete(object, entry.id, entry.deletedState, persister.getPropertyNames(), propTypes); //TODO: if an exception occurs, doesn't clean up entry.deletedState entry.status = DELETED; // before any callbacks, etc, so subdeletions see that this deletion happened first List deletionsByOnSave = null; HashSet nullifiablesByOnSave = null; try { // after nullify, because we don't want to nullify references to subdeletions // try to do callback + cascade, and before cascades, since this can veto if ( persister.implementsLifecycle() ) { HashSet oldNullifiables = (HashSet) nullifiables.clone(); ArrayList oldDeletions = (ArrayList) deletions.clone(); log.debug("calling onDelete()"); if ( ( (Lifecycle) object ).onDelete(this) ) { //rollback deletion entry.status = LOADED; entry.deletedState = null; log.debug("deletion vetoed by onDelete()"); return; //don't let it cascade } deletionsByOnSave = deletions.subList( oldDeletions.size(), deletions.size() ); deletions = oldDeletions; nullifiablesByOnSave = nullifiables; nullifiables = oldNullifiables; } } catch (CallbackException ce) { //rollback deletion entry.status = LOADED; entry.deletedState = null; throw ce; } cascading++; try { // cascade-delete to collections BEFORE the collection owner is deleted Cascades.cascade(this, persister, object, Cascades.ACTION_DELETE, Cascades.CASCADE_AFTER_INSERT_BEFORE_DELETE); } finally { cascading--; } nullifyTransientReferences(entry.deletedState, propTypes, false, object); checkNullability(entry.deletedState, persister, true); nullifiables.add( new Key(entry.id, persister) ); ScheduledDeletion delete = new ScheduledDeletion(entry.id, version, object, persister, this); deletions.add(delete); // Ensures that containing deletions happen before sub-deletions if ( persister.implementsLifecycle() ) { nullifiables.addAll(nullifiablesByOnSave); deletions.addAll(deletionsByOnSave); } cascading++; try { // cascade-delete to many-to-one AFTER the parent was deleted Cascades.cascade(this, persister, object, Cascades.ACTION_DELETE, Cascades.CASCADE_BEFORE_INSERT_AFTER_DELETE); } finally { cascading--; } } private static void checkNullability(Object[] values, ClassPersister persister, boolean isUpdate) throws PropertyValueException { boolean[] nullability = persister.getPropertyNullability(); boolean[] checkability = isUpdate ? persister.getPropertyUpdateability() : persister.getPropertyInsertability(); for ( int i=0; i<values.length; i++ ) { if ( !nullability[i] && checkability[i] && values[i]==null ) { throw new PropertyValueException(
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -