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

org.hibernate.search.jpa.impl.FullTextQueryImpl Maven / Gradle / Ivy

/*
 * Hibernate Search, full-text search for your domain model
 *
 * 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.search.jpa.impl;

import java.io.Serializable;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.persistence.EntityExistsException;
import javax.persistence.EntityManager;
import javax.persistence.EntityNotFoundException;
import javax.persistence.FlushModeType;
import javax.persistence.LockModeType;
import javax.persistence.LockTimeoutException;
import javax.persistence.NoResultException;
import javax.persistence.NonUniqueResultException;
import javax.persistence.OptimisticLockException;
import javax.persistence.Parameter;
import javax.persistence.PersistenceException;
import javax.persistence.PessimisticLockException;
import javax.persistence.Query;
import javax.persistence.TemporalType;

import org.apache.lucene.search.Explanation;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.Sort;

import org.hibernate.Criteria;
import org.hibernate.FlushMode;
import org.hibernate.HibernateException;
import org.hibernate.LockOptions;
import org.hibernate.ObjectNotFoundException;
import org.hibernate.QueryException;
import org.hibernate.QueryTimeoutException;
import org.hibernate.Session;
import org.hibernate.StaleObjectStateException;
import org.hibernate.StaleStateException;
import org.hibernate.TransientObjectException;
import org.hibernate.TypeMismatchException;
import org.hibernate.UnresolvableObjectException;
import org.hibernate.exception.ConstraintViolationException;
import org.hibernate.hql.internal.QueryExecutionRequestException;
import org.hibernate.search.filter.FullTextFilter;
import org.hibernate.search.exception.SearchException;
import org.hibernate.search.jpa.FullTextQuery;
import org.hibernate.search.query.DatabaseRetrievalMethod;
import org.hibernate.search.query.ObjectLookupMethod;
import org.hibernate.search.query.engine.spi.FacetManager;
import org.hibernate.search.spatial.Coordinates;
import org.hibernate.transform.ResultTransformer;

/**
 * Implements JPA 2 query interface and delegate the call to
 * a Hibernate Core FullTextQuery.
 * This has the consequence of "duplicating" the JPA 2 query logic in some areas.
 *
 * @author Emmanuel Bernard
 */
final class FullTextQueryImpl implements FullTextQuery {

	private final org.hibernate.search.FullTextQuery query;
	private final Session session;
	private final EntityManager em;

	private Integer firstResult;
	private Integer maxResults;
	//initialized at 0 since we don't expect to use hints at this stage
	private final Map hints = new HashMap( 0 );
	private FlushModeType jpaFlushMode;

	public FullTextQueryImpl(org.hibernate.search.FullTextQuery query, Session session, EntityManager em) {
		this.query = query;
		this.session = session;
		this.em = em;
	}

	@Override
	public FullTextQuery setSort(Sort sort) {
		query.setSort( sort );
		return this;
	}

	@Override
	public FullTextQuery setFilter(Filter filter) {
		query.setFilter( filter );
		return this;
	}

	@Override
	public int getResultSize() {
		try {
			return query.getResultSize();
		}
		catch (QueryTimeoutException e) {
			throwQueryTimeoutException( e );
		}
		return 0;
	}

	private void throwQueryTimeoutException(QueryTimeoutException e) {
		throw new javax.persistence.QueryTimeoutException( e.getMessage(), e, this );
	}

	@Override
	public FullTextQuery setCriteriaQuery(Criteria criteria) {
		query.setCriteriaQuery( criteria );
		return this;
	}

	@Override
	public FullTextQuery setProjection(String... fields) {
		query.setProjection( fields );
		return this;
	}

	@Override
	public FullTextQuery setSpatialParameters(double latitude, double longitude, String fieldName) {
		query.setSpatialParameters( latitude, longitude, fieldName );
		return this;
	}

	@Override
	public FullTextQuery setSpatialParameters(Coordinates center, String fieldName) {
		query.setSpatialParameters( center, fieldName );
		return this;
	}

	@Override
	public FullTextFilter enableFullTextFilter(String name) {
		return query.enableFullTextFilter( name );
	}

	@Override
	public void disableFullTextFilter(String name) {
		query.disableFullTextFilter( name );
	}

	@Override
	public FullTextQuery setResultTransformer(ResultTransformer transformer) {
		query.setResultTransformer( transformer );
		return this;
	}

	@Override
	public List getResultList() {
		try {
			return query.list();
		}
		catch (QueryTimeoutException e) {
			throwQueryTimeoutException( e );
			return null; //never happens
		}
		catch (QueryExecutionRequestException he) {
			//TODO when an illegal state exception should be raised?
			throw new IllegalStateException( he );
		}
		catch (TypeMismatchException e) {
			//TODO when an illegal arg exception should be raised?
			throw new IllegalArgumentException( e );
		}
		catch (SearchException he) {
			throwPersistenceException( he );
			throw he;
		}
	}

	@Override
	public FacetManager getFacetManager() {
		return query.getFacetManager();
	}

	@Override
	public String toString() {
		return query.toString();
	}

	//TODO mutualize this code with the EM this will fix the rollback issues
	private void throwPersistenceException(Exception e) {
		if ( e instanceof StaleStateException ) {
			PersistenceException pe = wrapStaleStateException( (StaleStateException) e );
			throwPersistenceException( pe );
		}
		else if ( e instanceof org.hibernate.OptimisticLockException ) {
			PersistenceException converted = wrapLockException( (HibernateException) e, null );
			throwPersistenceException( converted );
		}
		else if ( e instanceof org.hibernate.PessimisticLockException ) {
			PersistenceException converted = wrapLockException( (HibernateException) e, null );
			throwPersistenceException( converted );
		}
		else if ( e instanceof ConstraintViolationException ) {
			//FIXME this is bad cause ConstraintViolationException happens in other circumstances
			throwPersistenceException( new EntityExistsException( e ) );
		}
		else if ( e instanceof org.hibernate.QueryTimeoutException ) {
			javax.persistence.QueryTimeoutException converted = new javax.persistence.QueryTimeoutException(
					e.getMessage(), e
			);
			throwPersistenceException( converted );
		}
		else if ( e instanceof ObjectNotFoundException ) {
			throwPersistenceException( new EntityNotFoundException( e.getMessage() ) );
		}
		else if ( e instanceof org.hibernate.NonUniqueResultException ) {
			throwPersistenceException( new NonUniqueResultException( e.getMessage() ) );
		}
		else if ( e instanceof UnresolvableObjectException ) {
			throwPersistenceException( new EntityNotFoundException( e.getMessage() ) );
		}
		else if ( e instanceof QueryException ) {
			throw new IllegalArgumentException( e );
		}
		else if ( e instanceof TransientObjectException ) {
			//FIXME rollback
			throw new IllegalStateException( e ); //Spec 3.2.3 Synchronization rules
		}
		else {
			throwPersistenceException( new PersistenceException( e ) );
		}
	}

	public PersistenceException wrapLockException(HibernateException e, LockOptions lockOptions) {
		if ( OptimisticLockingCompatibilityHelper.isOptimisticLockException( e ) ) {
			throw OptimisticLockingCompatibilityHelper.convertToLockException( e );
		}
		else if ( e instanceof org.hibernate.PessimisticLockException ) {
			org.hibernate.PessimisticLockException ple = (org.hibernate.PessimisticLockException) e;
			if ( lockOptions != null && lockOptions.getTimeOut() > -1 ) {
				// assume lock timeout occurred if a timeout or NO WAIT was specified
				return new LockTimeoutException( ple.getMessage(), ple );
			}
			else {
				return new PessimisticLockException( ple.getMessage(), ple );
			}
		}
		else {
			return new OptimisticLockException( e.getMessage(), e );
		}
	}

	void throwPersistenceException(PersistenceException e) {
		if ( !( e instanceof NoResultException || e instanceof NonUniqueResultException ) ) {
			//FIXME rollback
		}
		throw e;
	}

	PersistenceException wrapStaleStateException(StaleStateException e) {
		if ( e instanceof StaleObjectStateException ) {
			StaleObjectStateException sose = (StaleObjectStateException) e;
			Serializable identifier = sose.getIdentifier();
			if ( identifier != null ) {
				Object entity = session.load( sose.getEntityName(), identifier );
				if ( entity instanceof Serializable ) {
					//avoid some user errors regarding boundary crossing
					return new OptimisticLockException( null, e, entity );
				}
				else {
					return new OptimisticLockException( e );
				}
			}
			else {
				return new OptimisticLockException( e );
			}
		}
		else {
			return new OptimisticLockException( e );
		}
	}

	@Override
	public Object getSingleResult() {
		try {
			List result = query.list();
			if ( result.size() == 0 ) {
				throwPersistenceException( new NoResultException( "No entity found for query" ) );
			}
			else if ( result.size() > 1 ) {
				Set uniqueResult = new HashSet( result );
				if ( uniqueResult.size() > 1 ) {
					throwPersistenceException( new NonUniqueResultException( "result returns " + uniqueResult.size() + " elements" ) );
				}
				else {
					return uniqueResult.iterator().next();
				}

			}
			else {
				return result.get( 0 );
			}
			return null; //should never happen
		}
		catch (QueryTimeoutException e) {
			throwQueryTimeoutException( e );
			return null; //never happens
		}
		catch (QueryExecutionRequestException he) {
			throw new IllegalStateException( he );
		}
		catch (TypeMismatchException e) {
			throw new IllegalArgumentException( e );
		}
		catch (HibernateException he) {
			throwPersistenceException( he );
			return null;
		}
	}

	@Override
	public FullTextQuery setMaxResults(int maxResults) {
		if ( maxResults < 0 ) {
			throw new IllegalArgumentException(
					"Negative ("
							+ maxResults
							+ ") parameter passed in to setMaxResults"
			);
		}
		query.setMaxResults( maxResults );
		this.maxResults = maxResults;
		return this;
	}

	@Override
	public int getMaxResults() {
		return maxResults == null || maxResults == -1
				? Integer.MAX_VALUE
				: maxResults;
	}

	@Override
	public FullTextQuery setFirstResult(int firstResult) {
		if ( firstResult < 0 ) {
			throw new IllegalArgumentException(
					"Negative ("
							+ firstResult
							+ ") parameter passed in to setFirstResult"
			);
		}
		query.setFirstResult( firstResult );
		this.firstResult = firstResult;
		return this;
	}

	@Override
	public int getFirstResult() {
		return firstResult == null ? 0 : firstResult;
	}

	@Override
	public Explanation explain(int documentId) {
		return query.explain( documentId );
	}

	@Override
	public FullTextQuery limitExecutionTimeTo(long timeout, TimeUnit timeUnit) {
		query.limitExecutionTimeTo( timeout, timeUnit );
		return this;
	}

	@Override
	public boolean hasPartialResults() {
		return query.hasPartialResults();
	}

	@Override
	public FullTextQuery initializeObjectsWith(ObjectLookupMethod lookupMethod, DatabaseRetrievalMethod retrievalMethod) {
		query.initializeObjectsWith( lookupMethod, retrievalMethod );
		return this;
	}

	@Override
	public int executeUpdate() {
		throw new IllegalStateException( "Update not allowed in FullTextQueries" );
	}

	@Override
	public FullTextQuery setHint(String hintName, Object value) {
		hints.put( hintName, value );
		if ( "javax.persistence.query.timeout".equals( hintName ) ) {
			if ( value == null ) {
				//nothing
			}
			else if ( value instanceof String ) {
				query.setTimeout( Long.parseLong( (String) value ), TimeUnit.MILLISECONDS );
			}
			else if ( value instanceof Number ) {
				query.setTimeout( ( (Number) value ).longValue(), TimeUnit.MILLISECONDS );
			}
		}
		return this;
	}

	@Override
	public Map getHints() {
		return hints;
	}

	@Override
	public  Query setParameter(Parameter tParameter, T t) {
		throw new UnsupportedOperationException( "parameters not supported in fullText queries" );
	}

	@Override
	public Query setParameter(Parameter calendarParameter, Calendar calendar, TemporalType temporalType) {
		throw new UnsupportedOperationException( "parameters not supported in fullText queries" );
	}

	@Override
	public Query setParameter(Parameter dateParameter, Date date, TemporalType temporalType) {
		throw new UnsupportedOperationException( "parameters not supported in fullText queries" );
	}

	@Override
	public Query setParameter(String name, Object value) {
		throw new UnsupportedOperationException( "parameters not supported in fullText queries" );
	}

	@Override
	public Query setParameter(String name, Date value, TemporalType temporalType) {
		throw new UnsupportedOperationException( "parameters not supported in fullText queries" );
	}

	@Override
	public Query setParameter(String name, Calendar value, TemporalType temporalType) {
		throw new UnsupportedOperationException( "parameters not supported in fullText queries" );
	}

	@Override
	public Query setParameter(int position, Object value) {
		throw new UnsupportedOperationException( "parameters not supported in fullText queries" );
	}

	@Override
	public Query setParameter(int position, Date value, TemporalType temporalType) {
		throw new UnsupportedOperationException( "parameters not supported in fullText queries" );
	}

	@Override
	@SuppressWarnings("unchecked")
	public Set> getParameters() {
		return Collections.EMPTY_SET;
	}

	@Override
	public Query setParameter(int position, Calendar value, TemporalType temporalType) {
		throw new UnsupportedOperationException( "parameters not supported in fullText queries" );
	}

	@Override
	public Parameter getParameter(String name) {
		throw new UnsupportedOperationException( "parameters not supported in fullText queries" );
	}

	@Override
	public Parameter getParameter(int position) {
		throw new UnsupportedOperationException( "parameters not supported in fullText queries" );
	}

	@Override
	public  Parameter getParameter(String name, Class type) {
		throw new UnsupportedOperationException( "parameters not supported in fullText queries" );
	}

	@Override
	public  Parameter getParameter(int position, Class type) {
		throw new UnsupportedOperationException( "parameters not supported in fullText queries" );
	}

	@Override
	public boolean isBound(Parameter param) {
		throw new UnsupportedOperationException( "parameters not supported in fullText queries" );
	}

	@Override
	public  T getParameterValue(Parameter param) {
		throw new UnsupportedOperationException( "parameters not supported in fullText queries" );
	}

	@Override
	public Object getParameterValue(String name) {
		throw new UnsupportedOperationException( "parameters not supported in fullText queries" );
	}

	@Override
	public Object getParameterValue(int position) {
		throw new UnsupportedOperationException( "parameters not supported in fullText queries" );
	}

	@Override
	public FullTextQuery setFlushMode(FlushModeType flushMode) {
		this.jpaFlushMode = flushMode;
		if ( flushMode == FlushModeType.AUTO ) {
			query.setFlushMode( FlushMode.AUTO );
		}
		else if ( flushMode == FlushModeType.COMMIT ) {
			query.setFlushMode( FlushMode.COMMIT );
		}
		return this;
	}

	@Override
	public FlushModeType getFlushMode() {
		if ( jpaFlushMode != null ) {
			return jpaFlushMode;
		}
		return em.getFlushMode();
	}

	@Override
	public Query setLockMode(LockModeType lockModeType) {
		throw new UnsupportedOperationException( "lock modes not supported in fullText queries" );
	}

	@Override
	public LockModeType getLockMode() {
		throw new UnsupportedOperationException( "lock modes not supported in fullText queries" );
	}

	@Override
	public  T unwrap(Class type) {
		//I've purposely decided not to return the underlying Hibernate FullTextQuery
		//as I see this as an implementation detail that should not be exposed.
		return (T) query.unwrap( type );
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy