All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hibernate.internal.FetchingScrollableResultsImpl Maven / Gradle / Ivy

There is a newer version: 6.6.2.Final
Show newest version
/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
 * See the lgpl.txt file in the root directory or .
 */
package org.hibernate.internal;

import org.hibernate.HibernateException;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.sql.results.internal.RowProcessingStateStandardImpl;
import org.hibernate.sql.results.jdbc.internal.JdbcValuesSourceProcessingStateStandardImpl;
import org.hibernate.sql.results.jdbc.spi.JdbcValues;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions;
import org.hibernate.sql.results.spi.LoadContexts;
import org.hibernate.sql.results.spi.RowReader;

/**
 * Implementation of ScrollableResults which can handle collection fetches.
 *
 * @author Steve Ebersole
 */
public class FetchingScrollableResultsImpl extends AbstractScrollableResults {
	private R currentRow;

	private int currentPosition;
	private Integer maxPosition;
	private boolean beforeFirst;
	private boolean afterLast;

	public FetchingScrollableResultsImpl(
			JdbcValues jdbcValues,
			JdbcValuesSourceProcessingOptions processingOptions,
			JdbcValuesSourceProcessingStateStandardImpl jdbcValuesSourceProcessingState,
			RowProcessingStateStandardImpl rowProcessingState,
			RowReader rowReader,
			SharedSessionContractImplementor persistenceContext) {
		super(
				jdbcValues,
				processingOptions,
				jdbcValuesSourceProcessingState,
				rowProcessingState,
				rowReader,
				persistenceContext
		);

		this.maxPosition = jdbcValuesSourceProcessingState.getQueryOptions().getEffectiveLimit().getMaxRows();
		beforeFirst = true;
	}

	@Override
	protected R getCurrentRow() {
		return currentRow;
	}

	@Override
	public boolean next() {
		if ( afterLast || isResultSetEmpty() ) {
			return false;
		}
		else if ( maxPosition != null && maxPosition <= currentPosition ) {
			currentPosition = maxPosition + 1;
			currentRow = null;
			afterLast = true;
			beforeFirst = false;
			return false;
		}
		else if ( beforeFirst ) {
			if ( !getRowProcessingState().next() ) {
				currentPosition = 0;
				beforeFirst = false;
				return false;
			}
		}

		boolean last = prepareCurrentRow();

		beforeFirst = false;
		currentPosition++;

		if ( last ) {
			if ( maxPosition == null ) {
				// we just hit the last position
				maxPosition = currentPosition;
			}
		}

		afterScrollOperation();

		return true;
	}


	@Override
	public boolean previous() {
		if ( beforeFirst || isResultSetEmpty() ) {
			return false;
		}
		else if ( currentPosition == 1 ) {
			beforeFirst();
			return false;
		}
		else {
			EntityKey keyToRead = null;
			// This check is needed since processing leaves the cursor
			// after the last physical row for the current logical row;
			// thus if we are after the last physical row, this might be
			// caused by either:
			//      1) scrolling to the last logical row
			//      2) scrolling past the last logical row
			// In the latter scenario, the previous logical row
			// really is the last logical row.
			//
			if ( afterLast ) {
				// position cursor to the last row
				getRowProcessingState().last();
				keyToRead = getEntityKey();
			}
			else {
				// Since the result set cursor is always left at the first
				// physical row after the "last processed", we need to jump
				// back one position to get the key value we are interested
				// in skipping

				getRowProcessingState().previous();

				// sequentially read the result set in reverse until we recognize
				// a change in the key value.  At that point, we are pointed at
				// the last physical sequential row for the logical row in which
				// we are interested in processing
				boolean firstPass = true;
				final EntityKey lastKey = getEntityKey();

				while ( getRowProcessingState().previous() ) {
					EntityKey checkKey = getEntityKey();

					if ( firstPass ) {
						firstPass = false;
						keyToRead = checkKey;
					}

					if ( !lastKey.equals( checkKey ) ) {
						break;
					}
				}
			}

			// Read backwards until we read past the first physical sequential
			// row with the key we are interested in loading
			while ( getRowProcessingState().previous() ) {
				EntityKey checkKey = getEntityKey();

				if ( !keyToRead.equals( checkKey ) ) {
					break;
				}
			}

			// Finally, read ahead one row to position result set cursor
			// at the first physical row we are interested in loading
			getRowProcessingState().next();
			prepareCurrentRow();
		}

		afterLast = false;
		currentPosition--;

		afterScrollOperation();

		return true;
	}

	@Override
	public boolean scroll(int positions) {
		boolean more = false;
		if ( positions > 0 ) {
			// scroll ahead
			for ( int i = 0; i < positions; i++ ) {
				more = next();
				if ( !more ) {
					break;
				}
			}
		}
		else if ( positions < 0 ) {
			// scroll backward
			for ( int i = 0; i < -positions; i++ ) {
				more = previous();
				if ( !more ) {
					break;
				}
			}
		}
		else {
			throw new HibernateException( "scroll(0) not valid" );
		}

		afterScrollOperation();

		return more;
	}

	@Override
	public boolean position(int position) {
		final boolean underlyingScrollSuccessful = getRowProcessingState().position( position );
		if ( !underlyingScrollSuccessful ) {
			currentRow = null;
			return false;

		}
		currentPosition = position - 1;
		return next();
	}

	@Override
	public boolean last() {
		boolean more = false;
		if ( maxPosition != null ) {
			if ( currentPosition > maxPosition ) {
				more = previous();
			}
			for ( int i = currentPosition; i < maxPosition; i++ ) {
				more = next();
			}
		}
		else {
			final RowProcessingStateStandardImpl rowProcessingState = getRowProcessingState();
			if ( isResultSetEmpty() || afterLast ) {
				// should not be able to reach last without maxPosition being set
				// unless there are no results
				return false;
			}

			while ( !afterLast ) {
				more = next();
			}
		}

		afterScrollOperation();

		return more;
	}

	@Override
	public boolean first() {
		beforeFirst();
		boolean more = next();

		afterScrollOperation();

		return more;
	}

	@Override
	public void beforeFirst() {
		getRowProcessingState().beforeFirst();
		beforeFirst = true;
		afterLast = false;
		currentRow = null;
		currentPosition = 0;
	}

	@Override
	public void afterLast() {
		// TODO : not sure the best way to handle this.
		// The non-performant way :
		last();
		next();
		afterScrollOperation();
	}

	@Override
	public boolean isFirst() {
		return currentPosition == 1;
	}

	@Override
	public boolean isLast() {
		return maxPosition != null && currentPosition == maxPosition;
	}

	@Override
	public int getRowNumber() {
		return currentPosition;
	}

	@Override
	public boolean setRowNumber(int rowNumber) {
		if ( rowNumber == 1 ) {
			return first();
		}
		else if ( rowNumber == -1 ) {
			return last();
		}
		else if ( maxPosition != null && rowNumber == maxPosition ) {
			return last();
		}
		return scroll( rowNumber - currentPosition );
	}

	private boolean prepareCurrentRow() {
		final RowProcessingStateStandardImpl rowProcessingState = getRowProcessingState();
		final RowReader rowReader = getRowReader();

		boolean last = false;
		boolean resultProcessed = false;

		final EntityKey entityKey = getEntityKey();
		final PersistenceContext persistenceContext = rowProcessingState.getSession().getPersistenceContext();
		final LoadContexts loadContexts = persistenceContext.getLoadContexts();

		loadContexts.register( getJdbcValuesSourceProcessingState() );
		persistenceContext.beforeLoad();
		try {
			currentRow = rowReader.readRow( rowProcessingState );

			rowProcessingState.finishRowProcessing( true );

			while ( !resultProcessed ) {
				if ( rowProcessingState.next() ) {
					final EntityKey entityKey2 = getEntityKey();
					if ( !entityKey.equals( entityKey2 ) ) {
						resultProcessed = true;
						last = false;
					}
					else {
						rowReader.readRow( rowProcessingState );
						rowProcessingState.finishRowProcessing( false );
					}
				}
				else {
					last = true;
					resultProcessed = true;
				}

			}
			getJdbcValuesSourceProcessingState().finishUp( false );
		}
		finally {
			persistenceContext.afterLoad();
			loadContexts.deregister( getJdbcValuesSourceProcessingState() );
		}
		persistenceContext.initializeNonLazyCollections();
		afterScrollOperation();
		return last;
	}


	private boolean isResultSetEmpty() {
		return currentPosition == 0 && !beforeFirst && !afterLast;
	}

	private EntityKey getEntityKey() {
		return getRowReader().resolveSingleResultEntityKey( getRowProcessingState() );
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy