hibernate source code 阅读三

经过hibernate source code 阅读一我们可以看出,hibernate其实在builderSessionFactory就已经把SQL语句封装好了

经过代码跟踪我发现在类:org.hibernate.mapping.SimpleValue中定义了这样一个属性:private final List columns = new ArrayList();

一开始在下面这个方法中就吧XML中的节点元素解析之后添加到column集合里面来了,以供后面拼接SQL语句和其他操作使用

public void addColumn(Column column) {
		if ( !columns.contains(column) ) columns.add(column);
		column.setValue(this);
		column.setTypeIndex( columns.size()-1 );
	}


上面我们看到了hibernate是在何时拼接出SQL语句的,接下来我们看看hibernate是在何时执行SQL语句的:

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;

public class  SimpleTest{

	public static void main(String[] args) {

		SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
		Session session = sessionFactory.getCurrentSession();
		Transaction tx = session.beginTransaction();

		Tparam param = new Tparam();
		param.setFcode(655l);
		param.setFremark("hibernate test");

		session.save(param);
		tx.commit();
	}
}

难道是这句:session.save(param)?

我仔细看了一下代码,他这里面做了很多事情,我个人感觉就是持久化对象到缓存(map)中,并没有真正的将对象持久化到数据库

public Serializable save(Object obj) throws HibernateException {
		return save(null, obj);
	}

	public Serializable save(String entityName, Object object) throws HibernateException {
		return fireSave( new SaveOrUpdateEvent(entityName, object, this) );
	}

private Serializable fireSave(SaveOrUpdateEvent event) {
		errorIfClosed();
		checkTransactionSynchStatus();
		SaveOrUpdateEventListener[] saveEventListener = listeners.getSaveEventListeners();
		for ( int i = 0; i < saveEventListener.length; i++ ) {
			saveEventListener[i].onSaveOrUpdate(event);
		}
		return event.getResultId();
	}

/**
	 * Handle the given update event.
	 *
	 * @param event The update event to be handled.
	 */
	public void onSaveOrUpdate(SaveOrUpdateEvent event) {
		final SessionImplementor source = event.getSession();
		final Object object = event.getObject();
		final Serializable requestedId = event.getRequestedId();

		if ( requestedId != null ) {
			//assign the requested id to the proxy, *before* 
			//reassociating the proxy
			if ( object instanceof HibernateProxy ) {
				( ( HibernateProxy ) object ).getHibernateLazyInitializer().setIdentifier( requestedId );
			}
		}

		if ( reassociateIfUninitializedProxy( object, source ) ) {
			log.trace( "reassociated uninitialized proxy" );
			// an uninitialized proxy, noop, don't even need to 
			// return an id, since it is never a save()
		}
		else {
			//initialize properties of the event:
			final Object entity = source.getPersistenceContext().unproxyAndReassociate( object );
			event.setEntity( entity );
			event.setEntry( source.getPersistenceContext().getEntry( entity ) );
			//return the id in the event object
			event.setResultId( performSaveOrUpdate( event ) );
		}

	}

protected Serializable performSaveOrUpdate(SaveOrUpdateEvent event) {
		// this implementation is supposed to tolerate incorrect unsaved-value
		// mappings, for the purpose of backward-compatibility
		EntityEntry entry = event.getSession().getPersistenceContext().getEntry( event.getEntity() );
		if ( entry!=null && entry.getStatus() != Status.DELETED ) {
			return entityIsPersistent(event);
		}
		else {
			return entityIsTransient(event);
		}
	}

protected Serializable performSaveOrUpdate(SaveOrUpdateEvent event) {
		// this implementation is supposed to tolerate incorrect unsaved-value
		// mappings, for the purpose of backward-compatibility
		EntityEntry entry = event.getSession().getPersistenceContext().getEntry( event.getEntity() );
		if ( entry!=null && entry.getStatus() != Status.DELETED ) {
			return entityIsPersistent(event);
		}
		else {
			return entityIsTransient(event);
		}
	}


我们仔细看看下面的描述,save the transient instance:保存瞬态实例
/**
	 * Save the transient instance, assigning the right identifier
	 *
	 * @param event The initiating event.
	 *
	 * @return The entity's identifier value after saving.
	 */
	protected Serializable saveWithGeneratedOrRequestedId(SaveOrUpdateEvent event) {
		return saveWithGeneratedId(
				event.getEntity(),
				event.getEntityName(),
				null,
				event.getSession(),
				true
		);
	}

/**
	 * Prepares the save call using a newly generated id.
	 *
	 * @param entity The entity to be saved
	 * @param entityName The entity-name for the entity to be saved
	 * @param anything Generally cascade-specific information.
	 * @param source The session which is the source of this save event.
	 * @param requiresImmediateIdAccess does the event context require
	 * access to the identifier immediately after execution of this method (if
	 * not, post-insert style id generators may be postponed if we are outside
	 * a transaction).
	 *
	 * @return The id used to save the entity; may be null depending on the
	 *         type of id generator used and the requiresImmediateIdAccess value
	 */
	protected Serializable saveWithGeneratedId(
			Object entity,
			String entityName,
			Object anything,
			EventSource source,
			boolean requiresImmediateIdAccess) {
		EntityPersister persister = source.getEntityPersister( entityName, entity );
		Serializable generatedId = persister.getIdentifierGenerator().generate( source, entity );
		if ( generatedId == null ) {
			throw new IdentifierGenerationException( "null id generated for:" + entity.getClass() );
		}
		else if ( generatedId == IdentifierGeneratorFactory.SHORT_CIRCUIT_INDICATOR ) {
			return source.getIdentifier( entity );
		}
		else if ( generatedId == IdentifierGeneratorFactory.POST_INSERT_INDICATOR ) {
			return performSave( entity, null, persister, true, anything, source, requiresImmediateIdAccess );
		}
		else {

			if ( log.isDebugEnabled() ) {
				log.debug(
						"generated identifier: " +
								persister.getIdentifierType().toLoggableString( generatedId, source.getFactory() ) +
								", using strategy: " +
								persister.getIdentifierGenerator().getClass().getName()
						//TODO: define toString()s for generators
				);
			}

			return performSave( entity, generatedId, persister, false, anything, source, true );
		}
	}


我们看看下面是如何描述的: ppepares the save call by checking the session caches for a pre-existing entity and performing any lifecycle callbacks,调用save方法首先会从session中检查一个已经存在的实体和执行生命周期回调的实体
/**
	 * Ppepares the save call by checking the session caches for a pre-existing
	 * entity and performing any lifecycle callbacks.
	 *
	 * @param entity The entity to be saved.
	 * @param id The id by which to save the entity.
	 * @param persister The entity's persister instance.
	 * @param useIdentityColumn Is an identity column being used?
	 * @param anything Generally cascade-specific information.
	 * @param source The session from which the event originated.
	 * @param requiresImmediateIdAccess does the event context require
	 * access to the identifier immediately after execution of this method (if
	 * not, post-insert style id generators may be postponed if we are outside
	 * a transaction).
	 *
	 * @return The id used to save the entity; may be null depending on the
	 *         type of id generator used and the requiresImmediateIdAccess value
	 */
	protected Serializable performSave(
			Object entity,
			Serializable id,
			EntityPersister persister,
			boolean useIdentityColumn,
			Object anything,
			EventSource source,
			boolean requiresImmediateIdAccess) {

		if ( log.isTraceEnabled() ) {
			log.trace(
					"saving " +
							MessageHelper.infoString( persister, id, source.getFactory() )
			);
		}

		EntityKey key;
		if ( !useIdentityColumn ) {
			key = new EntityKey( id, persister, source.getEntityMode() );
			Object old = source.getPersistenceContext().getEntity( key );
			if ( old != null ) {
				if ( source.getPersistenceContext().getEntry( old ).getStatus() == Status.DELETED ) {
					source.forceFlush( source.getPersistenceContext().getEntry( old ) );
				}
				else {
					throw new NonUniqueObjectException( id, persister.getEntityName() );
				}
			}
			persister.setIdentifier( entity, id, source.getEntityMode() );
		}
		else {
			key = null;
		}

		if ( invokeSaveLifecycle( entity, persister, source ) ) {
			return id; //EARLY EXIT
		}

		return performSaveOrReplicate(
				entity,
				key,
				persister,
				useIdentityColumn,
				anything,
				source,
				requiresImmediateIdAccess
		);
	}

	/**
	 * Performs all the actual work needed to save an entity (well to get the save moved to
	 * the execution queue).
	 *
	 * @param entity The entity to be saved
	 * @param key The id to be used for saving the entity (or null, in the case of identity columns)
	 * @param persister The entity's persister instance.
	 * @param useIdentityColumn Should an identity column be used for id generation?
	 * @param anything Generally cascade-specific information.
	 * @param source The session which is the source of the current event.
	 * @param requiresImmediateIdAccess Is access to the identifier required immediately
	 * after the completion of the save?  persist(), for example, does not require this...
	 *
	 * @return The id used to save the entity; may be null depending on the
	 *         type of id generator used and the requiresImmediateIdAccess value
	 */
	protected Serializable performSaveOrReplicate(
			Object entity,
			EntityKey key,
			EntityPersister persister,
			boolean useIdentityColumn,
			Object anything,
			EventSource source,
			boolean requiresImmediateIdAccess) {

		validate( entity, persister, source );

		Serializable id = key == null ? null : key.getIdentifier();

		boolean inTxn = source.getJDBCContext().isTransactionInProgress();
		boolean shouldDelayIdentityInserts = !inTxn && !requiresImmediateIdAccess;

		if ( useIdentityColumn && !shouldDelayIdentityInserts ) {
			log.trace( "executing insertions" );
			source.getActionQueue().executeInserts();
		}

		// 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()?
		source.getPersistenceContext().addEntry(
				entity,
				Status.SAVING,
				null,
				null,
				id,
				null,
				LockMode.WRITE,
				useIdentityColumn,
				persister,
				false,
				false
		);

		cascadeBeforeSave( source, persister, entity, anything );

		Object[] values = persister.getPropertyValuesToInsert( entity, getMergeMap( anything ), source );
		Type[] types = persister.getPropertyTypes();

		boolean substitute = substituteValuesIfNecessary( entity, id, values, persister, source );

		if ( persister.hasCollections() ) {
			substitute = substitute || visitCollectionsBeforeSave( entity, id, values, types, source );
		}

		if ( substitute ) {
			persister.setPropertyValues( entity, values, source.getEntityMode() );
		}

		TypeFactory.deepCopy(
				values,
				types,
				persister.getPropertyUpdateability(),
				values,
				source
		);

		new ForeignKeys.Nullifier( entity, false, useIdentityColumn, source )
				.nullifyTransientReferences( values, types );
		new Nullability( source ).checkNullability( values, persister, false );

		if ( useIdentityColumn ) {
			EntityIdentityInsertAction insert = new EntityIdentityInsertAction(
					values, entity, persister, source, shouldDelayIdentityInserts
			);
			if ( !shouldDelayIdentityInserts ) {
				log.debug( "executing identity-insert immediately" );
				source.getActionQueue().execute( insert );
				id = insert.getGeneratedId();
				//now done in EntityIdentityInsertAction
				//persister.setIdentifier( entity, id, source.getEntityMode() );
				key = new EntityKey( id, persister, source.getEntityMode() );
				source.getPersistenceContext().checkUniqueness( key, entity );
				//source.getBatcher().executeBatch(); //found another way to ensure that all batched joined inserts have been executed
			}
			else {
				log.debug( "delaying identity-insert due to no transaction in progress" );
				source.getActionQueue().addAction( insert );
				key = insert.getDelayedEntityKey();
			}
		}

		Object version = Versioning.getVersion( values, persister );
		source.getPersistenceContext().addEntity(
				entity,
				Status.MANAGED,
				values,
				key,
				version,
				LockMode.WRITE,
				useIdentityColumn,
				persister,
				isVersionIncrementDisabled(),
				false
		);
		//source.getPersistenceContext().removeNonExist( new EntityKey( id, persister, source.getEntityMode() ) );

		if ( !useIdentityColumn ) {
			source.getActionQueue().addAction(
					new EntityInsertAction( id, values, entity, version, persister, source )
			);
		}

		cascadeAfterSave( source, persister, entity, anything );

		markInterceptorDirty( entity, persister, source );

		return id;
	}


/**
	 * Generates an appropriate EntityEntry instance and adds it 
	 * to the event source's internal caches.
	 */
	public EntityEntry addEntry(
			final Object entity,
			final Status status,
			final Object[] loadedState,
			final Object rowId,
			final Serializable id,
			final Object version,
			final LockMode lockMode,
			final boolean existsInDatabase,
			final EntityPersister persister,
			final boolean disableVersionIncrement, 
			boolean lazyPropertiesAreUnfetched) {
		
		EntityEntry e = new EntityEntry(
				status,
				loadedState,
				rowId,
				id,
				version,
				lockMode,
				existsInDatabase,
				persister,
				session.getEntityMode(),
				disableVersionIncrement,
				lazyPropertiesAreUnfetched
			);
		entityEntries.put(entity, e);
		
		setHasNonReadOnlyEnties(status);
		return e;
	}


通过以上代码我们可以看到最终返回了一个id,而这个id用来给实体持久化到数据库中的时候使用,除此之外我看到的就是持久化的操作
那既然save方法没有将对象持久化到数据库,那就剩下最后一步了tx.commit();我们看看他都做了些什么:

public void commit() throws HibernateException {
		if (!begun) {
			throw new TransactionException("Transaction not successfully started");
		}

		log.debug("commit");

		if ( !transactionContext.isFlushModeNever() && callback ) {
			transactionContext.managedFlush(); //if an exception occurs during flush, user must call rollback()
		}

		notifyLocalSynchsBeforeTransactionCompletion();
		if ( callback ) {
			jdbcContext.beforeTransactionCompletion( this );
		}

		try {
			commitAndResetAutoCommit();
			log.debug("committed JDBC Connection");
			committed = true;
			if ( callback ) {
				jdbcContext.afterTransactionCompletion( true, this );
			}
			notifyLocalSynchsAfterTransactionCompletion( Status.STATUS_COMMITTED );
		}
		catch (SQLException e) {
			log.error("JDBC commit failed", e);
			commitFailed = true;
			if ( callback ) {
				jdbcContext.afterTransactionCompletion( false, this );
			}
			notifyLocalSynchsAfterTransactionCompletion( Status.STATUS_UNKNOWN );
			throw new TransactionException("JDBC commit failed", e);
		}
		finally {
			closeIfRequired();
		}
	}

public void managedFlush() {
		if ( isClosed() ) {
			log.trace( "skipping auto-flush due to session closed" );
			return;
		}
		log.trace("automatically flushing session");
		flush();
		
		if ( childSessionsByEntityMode != null ) {
			Iterator iter = childSessionsByEntityMode.values().iterator();
			while ( iter.hasNext() ) {
				( (Session) iter.next() ).flush();
			}
		}
	}

public void flush() throws HibernateException {
		errorIfClosed();
		checkTransactionSynchStatus();
		if ( persistenceContext.getCascadeLevel() > 0 ) {
			throw new HibernateException("Flush during cascade is dangerous");
		}
		FlushEventListener[] flushEventListener = listeners.getFlushEventListeners();
		for ( int i = 0; i < flushEventListener.length; i++ ) {
			flushEventListener[i].onFlush( new FlushEvent(this) );
		}
	}

/** Handle the given flush event.
	 *
	 * @param event The flush event to be handled.
	 * @throws HibernateException
	 */
	public void onFlush(FlushEvent event) throws HibernateException {
		final EventSource source = event.getSession();
		if ( source.getPersistenceContext().hasNonReadOnlyEntities() ) {
			
			flushEverythingToExecutions(event);
			performExecutions(source);
			postFlush(source);
		
			if ( source.getFactory().getStatistics().isStatisticsEnabled() ) {
				source.getFactory().getStatisticsImplementor().flush();
			}
			
		}
	}


下面这里需仔细看看:execute all SQL and second-level cache updates,真正执行SQL语句的就在下面:
/**
	 * Execute all SQL and second-level cache updates, in a
	 * special order so that foreign-key constraints cannot
	 * be violated:
	 * <ol>
	 * <li> Inserts, in the order they were performed
	 * <li> Updates
	 * <li> Deletion of collection elements
	 * <li> Insertion of collection elements
	 * <li> Deletes, in the order they were performed
	 * </ol>
	 */
	protected void performExecutions(EventSource session) throws HibernateException {

		log.trace("executing flush");

		try {
			session.getJDBCContext().getConnectionManager().flushBeginning();
			// we need to lock the collection caches before
			// executing entity inserts/updates in order to
			// account for bidi associations
			session.getActionQueue().prepareActions();
			session.getActionQueue().executeActions();
		}
		catch (HibernateException he) {
			log.error("Could not synchronize database state with session", he);
			throw he;
		}
		finally {
			session.getJDBCContext().getConnectionManager().flushEnding();
		}
	}

/**
	 * Perform all currently queued actions.
	 *
	 * @throws HibernateException error executing queued actions.
	 */
	public void executeActions() throws HibernateException {
		executeActions( insertions );
		executeActions( updates );
		executeActions( collectionRemovals );
		executeActions( collectionUpdates );
		executeActions( collectionCreations );
		executeActions( deletions );
	}

private void executeActions(List list) throws HibernateException {
		int size = list.size();
		for ( int i = 0; i < size; i++ ) {
			execute( (Executable) list.get(i) );
		}
		list.clear();
		session.getBatcher().executeBatch();
	}

public void executeBatch() throws HibernateException {
		if (batchUpdate!=null) {
			try {
				try {
					doExecuteBatch(batchUpdate);
				}
				finally {
					closeStatement(batchUpdate);
				}
			}
			catch (SQLException sqle) {
				throw JDBCExceptionHelper.convert(
				        factory.getSQLExceptionConverter(),
				        sqle,
				        "Could not execute JDBC batch update",
				        batchUpdateSQL
					);
			}
			finally {
				batchUpdate=null;
				batchUpdateSQL=null;
			}
		}
	}


我跟踪代码发现,真正将SQL语句执行到数据库的操作一步是:checkRowCount(ps.executeBatch, ps );
protected void doExecuteBatch(PreparedStatement ps) throws SQLException, HibernateException {
		if ( batchSize == 0 ) {
			log.debug( "no batched statements to execute" );
		}
		else {
			if ( log.isDebugEnabled() ) {
				log.debug( "Executing batch size: " + batchSize );
			}

			try {
				checkRowCounts( ps.executeBatch(), ps );
			}
			catch (RuntimeException re) {
				log.error( "Exception executing batch: ", re );
				throw re;
			}
			finally {
				batchSize = 0;
			}

		}

	}


以上算是我对hibernate将对象持久化到数据库的一点源码解读,理解的比较肤浅,希望在以后的学习中经常的看这些代码,最终能融汇贯通。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值