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