Java tutorial
/* * Copyright 2009 Denys Pavlov, Igor Azarnyi * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.yes.cart.dao.impl; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.Term; import org.apache.lucene.search.*; import org.apache.lucene.util.FixedBitSet; import org.hibernate.*; import org.hibernate.Query; import org.hibernate.criterion.Criterion; import org.hibernate.criterion.Example; import org.hibernate.criterion.Order; import org.hibernate.criterion.RowCountProjection; import org.hibernate.proxy.HibernateProxy; import org.hibernate.search.FullTextQuery; import org.hibernate.search.FullTextSession; import org.hibernate.search.Search; import org.hibernate.search.annotations.Indexed; import org.hibernate.search.indexes.interceptor.EntityIndexingInterceptor; import org.hibernate.search.indexes.interceptor.IndexingOverride; import org.hibernate.search.query.dsl.DiscreteFacetContext; import org.hibernate.search.query.dsl.FacetRangeAboveBelowContext; import org.hibernate.search.query.dsl.QueryBuilder; import org.hibernate.search.query.engine.spi.FacetManager; import org.hibernate.search.query.facet.Facet; import org.hibernate.search.query.facet.FacetSortOrder; import org.hibernate.search.util.impl.ClassLoaderHelper; import org.hibernate.search.util.impl.HibernateHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.task.TaskExecutor; import org.yes.cart.dao.CriteriaTuner; import org.yes.cart.dao.EntityFactory; import org.yes.cart.dao.GenericFullTextSearchCapableDAO; import org.yes.cart.dao.ResultsIterator; import org.yes.cart.domain.entity.Identifiable; import org.yes.cart.domain.entity.Product; import org.yes.cart.domain.entityindexer.IndexFilter; import org.yes.cart.domain.misc.Pair; import org.yes.cart.domain.queryobject.FilteredNavigationRecordRequest; import java.io.IOException; import java.io.Serializable; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; /** * User: Igor Azarny iazarny@yahoo.com * Date: 08-May-2011 * Time: 11:12:54 */ public class GenericDAOHibernateImpl<T, PK extends Serializable> implements GenericFullTextSearchCapableDAO<T, PK> { private static final Logger LOG = LoggerFactory.getLogger(GenericDAOHibernateImpl.class); private final Logger LOGFTQ = LoggerFactory.getLogger("FTQ"); private final Class<T> persistentClass; private final boolean persistentClassIndexble; private final EntityFactory entityFactory; private final EntityIndexingInterceptor entityIndexingInterceptor; protected SessionFactory sessionFactory; private TaskExecutor indexExecutor; /** * Set the Hibernate SessionFactory to be used by this DAO. * Will automatically create a HibernateTemplate for the given SessionFactory. */ public final void setSessionFactory(final SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } /** * Executer that will perform indexing jobs asynchronously. * * @param indexExecutor index executor */ public void setIndexExecutor(final TaskExecutor indexExecutor) { this.indexExecutor = indexExecutor; } /** * Default constructor. * * @param type - entity type * @param entityFactory {@link EntityFactory} to create the entities */ @SuppressWarnings("unchecked") public GenericDAOHibernateImpl(final Class<T> type, final EntityFactory entityFactory) { this.persistentClass = type; this.persistentClassIndexble = null != type.getAnnotation(org.hibernate.search.annotations.Indexed.class); this.entityFactory = entityFactory; this.entityIndexingInterceptor = getInterceptor(); } private EntityIndexingInterceptor getInterceptor() { final Indexed indexed = getPersistentClass().getAnnotation(org.hibernate.search.annotations.Indexed.class); if (indexed != null) { final Class<? extends EntityIndexingInterceptor> interceptorClass = indexed.interceptor(); if (interceptorClass != null) { return ClassLoaderHelper.instanceFromClass(EntityIndexingInterceptor.class, interceptorClass, "IndexingActionInterceptor for " + getPersistentClass().getName()); } } return null; } /** * {@inheritDoc} */ public EntityFactory getEntityFactory() { return entityFactory; } /** * {@inheritDoc} */ public <I> I getEntityIdentifier(final Object entity) { if (entity == null) { // That's ok - it is null return null; } if (entity instanceof HibernateProxy && !Hibernate.isInitialized(entity)) { // Avoid Lazy select by getting identifier from session meta // If hibernate proxy is initialised then DO NOT use this approach as chances // are that this is detached entity from cache which is not associate with the // session and will result in exception return (I) sessionFactory.getCurrentSession().getIdentifier(entity); } else if (entity instanceof Identifiable) { // If it is not proxy or it is initialised then we can use identifiable return (I) Long.valueOf(((Identifiable) entity).getId()); } throw new IllegalArgumentException("Cannot get PK from object: " + entity); } /** * {@inheritDoc} */ public T findById(PK id) { return findById(id, false); } private static final LockOptions UPDATE = new LockOptions(LockMode.PESSIMISTIC_WRITE); /** * {@inheritDoc} */ @SuppressWarnings("unchecked") public T findById(final PK id, final boolean lock) { T entity; if (lock) { entity = (T) sessionFactory.getCurrentSession().get(getPersistentClass(), id, UPDATE); } else { entity = (T) sessionFactory.getCurrentSession().get(getPersistentClass(), id); } return entity; } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") public List<T> findByExample(final T exampleInstance, final String[] excludeProperty) { Criteria crit = sessionFactory.getCurrentSession().createCriteria(getPersistentClass()); Example example = Example.create(exampleInstance); for (String exclude : excludeProperty) { example.excludeProperty(exclude); } crit.add(example); return crit.list(); } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") public <T> T findSingleByNamedQuery(final String namedQueryName, final Object... parameters) { List<T> rez = (List<T>) this.findByNamedQuery(namedQueryName, parameters); if (!rez.isEmpty()) { return rez.get(0); } return null; } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") public <T> T findSingleByNamedQueryCached(final String namedQueryName, final Object... parameters) { List<T> rez = (List<T>) this.findByNamedQueryCached(namedQueryName, parameters); if (!rez.isEmpty()) { return rez.get(0); } return null; } /** * {@inheritDoc} */ public Object getScalarResultByNamedQuery(final String namedQueryName, final Object... parameters) { Query query = sessionFactory.getCurrentSession().getNamedQuery(namedQueryName); setQueryParameters(query, parameters); return query.uniqueResult(); } /** * {@inheritDoc} */ public Object getScalarResultByNamedQueryWithInit(final String namedQueryName, final Object... parameters) { Query query = sessionFactory.getCurrentSession().getNamedQuery(namedQueryName); setQueryParameters(query, parameters); final Object obj = query.uniqueResult(); if (obj instanceof Product) { Hibernate.initialize(((Product) obj).getAttributes()); } return obj; } /** * {@inheritDoc} */ public List<Object> findByQuery(final String hsqlQuery, final Object... parameters) { Query query = sessionFactory.getCurrentSession().createQuery(hsqlQuery); setQueryParameters(query, parameters); return query.list(); } /** * {@inheritDoc} */ public ResultsIterator<Object> findByQueryIterator(final String hsqlQuery, final Object... parameters) { Query query = sessionFactory.getCurrentSession().createQuery(hsqlQuery); if (parameters != null) { setQueryParameters(query, parameters); } return new ResultsIteratorImpl<Object>(query.scroll(ScrollMode.FORWARD_ONLY)); } /** * {@inheritDoc} */ public Object findSingleByQuery(final String hsqlQuery, final Object... parameters) { Query query = sessionFactory.getCurrentSession().createQuery(hsqlQuery); setQueryParameters(query, parameters); final List rez = query.list(); int size = rez.size(); switch (size) { case 0: { return null; } case 1: { return rez.get(0); } default: { LOG.error("#findSingleByQuery has more than one result for {}, [{}]", hsqlQuery, parameters); return rez.get(0); } } } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") public List<T> findByNamedQuery(final String namedQueryName, final Object... parameters) { Query query = sessionFactory.getCurrentSession().getNamedQuery(namedQueryName); if (parameters != null) { setQueryParameters(query, parameters); } return query.list(); } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") public ResultsIterator<T> findByNamedQueryIterator(final String namedQueryName, final Object... parameters) { Query query = sessionFactory.getCurrentSession().getNamedQuery(namedQueryName); if (parameters != null) { setQueryParameters(query, parameters); } return new ResultsIteratorImpl<T>(query.scroll(ScrollMode.FORWARD_ONLY)); } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") public List<T> findByNamedQueryForUpdate(final String namedQueryName, final int timeout, final Object... parameters) { Query query = sessionFactory.getCurrentSession().getNamedQuery(namedQueryName); LockOptions opts = new LockOptions(LockMode.PESSIMISTIC_WRITE); opts.setTimeOut(timeout); query.setLockOptions(opts); if (parameters != null) { setQueryParameters(query, parameters); } return query.list(); } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") public List<T> findByNamedQueryCached(final String namedQueryName, final Object... parameters) { Query query = sessionFactory.getCurrentSession().getNamedQuery(namedQueryName); query.setCacheable(true); query.setCacheMode(CacheMode.NORMAL); setQueryParameters(query, parameters); return query.list(); } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") public List<Object> findQueryObjectByNamedQuery(final String namedQueryName, final Object... parameters) { Query query = sessionFactory.getCurrentSession().getNamedQuery(namedQueryName); setQueryParameters(query, parameters); return query.list(); } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") public ResultsIterator<Object> findQueryObjectByNamedQueryIterator(final String namedQueryName, final Object... parameters) { Query query = sessionFactory.getCurrentSession().getNamedQuery(namedQueryName); setQueryParameters(query, parameters); final ScrollableResults results = query.scroll(ScrollMode.FORWARD_ONLY); return new ResultsIteratorImpl<Object>(results); } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") public List<Object> findQueryObjectRangeByNamedQuery(final String namedQueryName, final int firstResult, final int maxResults, final Object... parameters) { Query query = sessionFactory.getCurrentSession().getNamedQuery(namedQueryName); setQueryParameters(query, parameters); query.setFirstResult(firstResult); query.setMaxResults(maxResults); return query.list(); } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") public List<Object[]> findQueryObjectsByNamedQuery(final String namedQueryName, final Object... parameters) { Query query = sessionFactory.getCurrentSession().getNamedQuery(namedQueryName); setQueryParameters(query, parameters); return query.list(); } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") public List<Object[]> findQueryObjectsRangeByNamedQuery(final String namedQueryName, final int firstResult, final int maxResults, final Object... parameters) { Query query = sessionFactory.getCurrentSession().getNamedQuery(namedQueryName); setQueryParameters(query, parameters); query.setFirstResult(firstResult); query.setMaxResults(maxResults); return query.list(); } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") public List<T> findRangeByNamedQuery(final String namedQueryName, final int firstResult, final int maxResults, final Object... parameters) { Query query = sessionFactory.getCurrentSession().getNamedQuery(namedQueryName); query.setFirstResult(firstResult); query.setMaxResults(maxResults); setQueryParameters(query, parameters); return query.list(); } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") public List<T> findAll() { return findByCriteria(); } /** * {@inheritDoc} */ public ResultsIterator<T> findAllIterator() { final Criteria crit = sessionFactory.getCurrentSession().createCriteria(getPersistentClass()); final ScrollableResults results = crit.scroll(ScrollMode.FORWARD_ONLY); return new ResultsIteratorImpl<T>(results); } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") public T saveOrUpdate(T entity) { sessionFactory.getCurrentSession().saveOrUpdate(entity); return entity; } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") public T create(T entity) { sessionFactory.getCurrentSession().save(entity); return entity; } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") public T update(T entity) { sessionFactory.getCurrentSession().update(entity); return entity; } /** * {@inheritDoc} */ public void delete(final Object entity) { if (entity != null) { sessionFactory.getCurrentSession().delete(entity); } } /** * {@inheritDoc} */ public void refresh(final Object entity) { sessionFactory.getCurrentSession().refresh(entity); } /** * {@inheritDoc} */ public void evict(Object entity) { sessionFactory.getCurrentSession().evict(entity); } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") public List<T> findByCriteria(Criterion... criterion) { Criteria crit = sessionFactory.getCurrentSession().createCriteria(getPersistentClass()); for (Criterion c : criterion) { crit.add(c); } return crit.list(); } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") public List<T> findByCriteria(final int firstResult, final int maxResults, final Criterion... criterion) { Criteria crit = sessionFactory.getCurrentSession().createCriteria(getPersistentClass()); for (Criterion c : criterion) { crit.add(c); } crit.setFirstResult(firstResult); crit.setMaxResults(maxResults); return crit.list(); } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") public List<T> findByCriteria(final int firstResult, final int maxResults, final Criterion[] criterion, final Order[] order) { Criteria crit = sessionFactory.getCurrentSession().createCriteria(getPersistentClass()); for (Criterion c : criterion) { crit.add(c); } for (Order o : order) { crit.addOrder(o); } crit.setFirstResult(firstResult); crit.setMaxResults(maxResults); return crit.list(); } @Override public int findCountByCriteria(final Criterion... criterion) { Criteria crit = sessionFactory.getCurrentSession().createCriteria(getPersistentClass()); for (Criterion c : criterion) { crit.add(c); } crit.setProjection(new RowCountProjection()); return ((Number) crit.uniqueResult()).intValue(); } /** * {@inheritDoc} */ public T findSingleByCriteria(final Criterion... criterion) { return findSingleByCriteria(null, criterion); } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") public List<T> findByCriteria(final CriteriaTuner criteriaTuner, final Criterion... criterion) { Criteria crit = sessionFactory.getCurrentSession().createCriteria(getPersistentClass()); for (Criterion c : criterion) { crit.add(c); } if (criteriaTuner != null) { criteriaTuner.tune(crit); } return crit.list(); } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") public List<T> findByCriteria(final CriteriaTuner criteriaTuner, final int firstResult, final int maxResults, final Criterion... criterion) { Criteria crit = sessionFactory.getCurrentSession().createCriteria(getPersistentClass()); for (Criterion c : criterion) { crit.add(c); } if (criteriaTuner != null) { criteriaTuner.tune(crit); } crit.setFirstResult(firstResult); crit.setMaxResults(maxResults); return crit.list(); } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") public List<T> findByCriteria(final CriteriaTuner criteriaTuner, final int firstResult, final int maxResults, final Criterion[] criterion, final Order[] order) { Criteria crit = sessionFactory.getCurrentSession().createCriteria(getPersistentClass()); for (Criterion c : criterion) { crit.add(c); } for (Order o : order) { crit.addOrder(o); } if (criteriaTuner != null) { criteriaTuner.tune(crit); } crit.setFirstResult(firstResult); crit.setMaxResults(maxResults); return crit.list(); } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") public int findCountByCriteria(final CriteriaTuner criteriaTuner, final Criterion... criterion) { Criteria crit = sessionFactory.getCurrentSession().createCriteria(getPersistentClass()); for (Criterion c : criterion) { crit.add(c); } if (criteriaTuner != null) { criteriaTuner.tune(crit); } crit.setProjection(new RowCountProjection()); return ((Number) crit.uniqueResult()).intValue(); } /** * Find entities by criteria. * @param firstResult scroll to first result. * @param criterion given criteria * @return list of found entities. */ @SuppressWarnings("unchecked") public T findUniqueByCriteria(final int firstResult, final Criterion... criterion) { Criteria crit = sessionFactory.getCurrentSession().createCriteria(getPersistentClass()); for (Criterion c : criterion) { crit.add(c); } return (T) crit.setFirstResult(firstResult).setMaxResults(1).uniqueResult(); } /** * {@inheritDoc} */ public T findSingleByCriteria(final CriteriaTuner criteriaTuner, final Criterion... criterion) { Criteria crit = sessionFactory.getCurrentSession().createCriteria(getPersistentClass()); for (Criterion c : criterion) { crit.add(c); } if (criteriaTuner != null) { criteriaTuner.tune(crit); } return (T) crit.uniqueResult(); } private Class<T> getPersistentClass() { return persistentClass; } public void fullTextSearchReindex(PK primaryKey, boolean purgeOnly) { if (persistentClassIndexble) { sessionFactory.getCache().evictEntity(getPersistentClass(), primaryKey); FullTextSession fullTextSession = Search.getFullTextSession(sessionFactory.getCurrentSession()); fullTextSession.setFlushMode(FlushMode.MANUAL); fullTextSession.setCacheMode(CacheMode.IGNORE); fullTextSession.purge(getPersistentClass(), primaryKey); if (!purgeOnly) { T entity = findById(primaryKey); if (entity != null) { final T unproxied = (T) HibernateHelper.unproxy(entity); if (entityIndexingInterceptor != null) { if (IndexingOverride.APPLY_DEFAULT == entityIndexingInterceptor.onUpdate(unproxied)) { fullTextSession.index(unproxied); } } else { fullTextSession.index(unproxied); } } } fullTextSession.flushToIndexes(); //apply changes to indexes fullTextSession.clear(); //clear since the queue is processed } } /** * {@inheritDoc} */ public void fullTextSearchReindex(final PK primaryKey) { fullTextSearchReindex(primaryKey, false); } private final int IDLE = -3; private final int COMPLETED = -1; private final int LASTUPDATE = -2; private final int RUNNING = 0; private final AtomicInteger asyncRunningState = new AtomicInteger(IDLE); private final AtomicInteger currentIndexingCount = new AtomicInteger(0); /** * {@inheritDoc} */ public FTIndexState getFullTextIndexState() { return new FTIndexStateImpl(asyncRunningState.get() == RUNNING, asyncRunningState.get() == COMPLETED, currentIndexingCount.get()); } /** * {@inheritDoc} */ public void fullTextSearchReindex(final boolean async, final int batchSize) { fullTextSearchReindex(async, batchSize, null); } /** * {@inheritDoc} */ public void fullTextSearchReindex(final boolean async, final int batchSize, final IndexFilter<T> indexFilter) { final boolean runAsync = async && this.indexExecutor != null; asyncRunningState.compareAndSet(COMPLETED, IDLE); // Completed tasks must restart if (asyncRunningState.compareAndSet(IDLE, RUNNING)) { // If we are idle we can start if (runAsync) { this.indexExecutor.execute(createIndexingRunnable(true, batchSize, indexFilter)); // async } else { createIndexingRunnable(false, batchSize, indexFilter).run(); // sync } } else if (!runAsync) { // If we are not async but we have a running task, need to wait for it to imitate sync while (asyncRunningState.get() == RUNNING) { try { Thread.sleep(1000L); } catch (InterruptedException e) { } } } } private Runnable createIndexingRunnable(final boolean async, final int batchSize, final IndexFilter<T> filter) { return new Runnable() { @Override public void run() { int index = 0; final Logger log = LOGFTQ; try { if (persistentClassIndexble) { currentIndexingCount.set(0); if (log.isInfoEnabled()) { log.info("Full reindex for {} class", persistentClass); } FullTextSession fullTextSession = Search.getFullTextSession( async ? sessionFactory.openSession() : sessionFactory.getCurrentSession()); fullTextSession.setFlushMode(FlushMode.MANUAL); fullTextSession.setCacheMode(CacheMode.IGNORE); if (filter == null) { // only purge global full reindex because this clears all entries fullTextSession.purgeAll(getPersistentClass()); } ScrollableResults results = fullTextSession.createCriteria(persistentClass) .setFetchSize(batchSize).scroll(ScrollMode.FORWARD_ONLY); try { while (results.next()) { final T entity = (T) HibernateHelper.unproxy(results.get(0)); if (filter != null && filter.skipIndexing(entity)) { continue; // skip this object } if (entityIndexingInterceptor != null) { if (IndexingOverride.APPLY_DEFAULT == entityIndexingInterceptor .onUpdate(entity)) { fullTextSession.index(entity); } } else { fullTextSession.index(entity); } index++; if (index % batchSize == 0) { fullTextSession.flushToIndexes(); //apply changes to indexes fullTextSession.clear(); //clear since the queue is processed if (log.isInfoEnabled()) { log.info("Indexed {} items of {} class", index, persistentClass); } } currentIndexingCount.compareAndSet(index - 1, index); } } finally { results.close(); } fullTextSession.flushToIndexes(); //apply changes to indexes fullTextSession.clear(); //clear since the queue is processed if (log.isInfoEnabled()) { log.info("Indexed {} items of {} class", index, persistentClass); } fullTextSession.getSearchFactory().optimize(getPersistentClass()); } } catch (Exception exp) { LOGFTQ.error("Error during indexing", exp); } finally { asyncRunningState.set(COMPLETED); if (async) { try { if (persistentClassIndexble) { sessionFactory.getCurrentSession().close(); } } catch (Exception exp) { } } if (log.isInfoEnabled()) { log.info("Full reindex for {} class ... COMPLETED", persistentClass); } } } }; } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") public List<T> fullTextSearch(final org.apache.lucene.search.Query query) { if (persistentClassIndexble) { FullTextSession fullTextSession = Search.getFullTextSession(sessionFactory.getCurrentSession()); Query fullTextQuery = fullTextSession.createFullTextQuery(query, getPersistentClass()); List<T> list = fullTextQuery.list(); if (list != null) { return list; } } return Collections.EMPTY_LIST; } private static final Pair<List<Object[]>, Integer> EMPTY = new Pair<List<Object[]>, Integer>( Collections.EMPTY_LIST, 0); /** * {@inheritDoc} */ @SuppressWarnings("unchecked") public Pair<List<Object[]>, Integer> fullTextSearch(final org.apache.lucene.search.Query query, final int firstResult, final int maxResults, final String sortFieldName, final boolean reverse, final String... fields) { if (persistentClassIndexble) { if (LOGFTQ.isDebugEnabled()) { LOGFTQ.debug("Run {}x{} {}@{} {}", new Object[] { firstResult, maxResults, sortFieldName, reverse, query }); } final boolean explain = LOGFTQ.isTraceEnabled(); final FullTextQuery fullTextQuery = createFullTextQuery(query, firstResult, maxResults, sortFieldName, reverse); if (explain) { final List<String> allFields = new ArrayList<String>(Arrays.asList(fields)); allFields.add(FullTextQuery.EXPLANATION); fullTextQuery.setProjection(allFields.toArray(new String[allFields.size()])); } else { fullTextQuery.setProjection(fields); } final List<Object[]> list = fullTextQuery.list(); if (list != null) { if (explain) { final StringBuilder explanation = new StringBuilder("\n"); explanation.append("Query: ").append(query).append("\n"); explanation.append("First/Max/Sort/Reverse: ").append(firstResult).append("/") .append(maxResults).append("/").append(sortFieldName).append("/").append(reverse) .append("\n"); explanation.append(list.size()).append(" result(s): \n\n"); for (int i = 0; i < list.size(); i++) { explanation.append(i).append(" =======================================================\n"); final Object[] item = list.get(i); for (int ii = 0; ii < item.length - 1; ii++) { explanation.append(item[ii]).append(","); } explanation.append("\n\nreason: ").append(item[item.length - 1]).append("\n"); } LOGFTQ.trace(explanation.toString()); } return new Pair<List<Object[]>, Integer>(list, fullTextQuery.getResultSize()); } } return EMPTY; } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") public List<T> fullTextSearch(final org.apache.lucene.search.Query query, final int firstResult, final int maxResults, final String sortFieldName, final boolean reverse) { if (persistentClassIndexble) { if (LOGFTQ.isDebugEnabled()) { LOGFTQ.debug("Run {}x{} {}@{} {}", new Object[] { firstResult, maxResults, sortFieldName, reverse, query }); } final FullTextQuery fullTextQuery = createFullTextQuery(query, firstResult, maxResults, sortFieldName, reverse); final List<T> list = fullTextQuery.list(); if (list != null) { return list; } } return Collections.EMPTY_LIST; } private FullTextQuery createFullTextQuery(final org.apache.lucene.search.Query query, final int firstResult, final int maxResults, final String sortFieldName, final boolean reverse) { FullTextSession fullTextSession = Search.getFullTextSession(sessionFactory.getCurrentSession()); FullTextQuery fullTextQuery = fullTextSession.createFullTextQuery(query, getPersistentClass()); if (sortFieldName != null) { Sort sort = new Sort(new SortField(sortFieldName, SortField.STRING, reverse)); fullTextQuery.setSort(sort); } fullTextQuery.setFirstResult(firstResult); if (maxResults > 0) { fullTextQuery.setMaxResults(maxResults); } return fullTextQuery; } /** * {@inheritDoc} */ public Map<String, List<Pair<String, Integer>>> fullTextSearchNavigation( final org.apache.lucene.search.Query query, final List<FilteredNavigationRecordRequest> facetingRequest) { if (persistentClassIndexble) { if (LOGFTQ.isDebugEnabled()) { LOGFTQ.debug("Run facet request with base query {}", query); } if (facetingRequest == null || facetingRequest.isEmpty()) { return Collections.emptyMap(); } FullTextSession fullTextSession = Search.getFullTextSession(sessionFactory.getCurrentSession()); QueryBuilder qb = fullTextSession.getSearchFactory().buildQueryBuilder().forEntity(getPersistentClass()) .get(); FullTextQuery fullTextQuery = fullTextSession.createFullTextQuery(query, getPersistentClass()); fullTextQuery.setMaxResults(1); final FacetManager facetManager = fullTextQuery.getFacetManager(); boolean hasMultivalue = false; for (final FilteredNavigationRecordRequest facetingRequestItem : facetingRequest) { if (facetingRequestItem.isRangeValue()) { final FacetRangeAboveBelowContext facetCtx = qb.facet().name(facetingRequestItem.getFacetName()) .onField(facetingRequestItem.getField()).range(); final Iterator<Pair<String, String>> rageIt = facetingRequestItem.getRangeValues().iterator(); while (rageIt.hasNext()) { final Pair<String, String> range = rageIt.next(); if (rageIt.hasNext()) { facetCtx.from(range.getFirst()).to(range.getSecond()).excludeLimit(); } else { facetManager.enableFaceting(facetCtx.from(range.getFirst()).to(range.getSecond()) .orderedBy(FacetSortOrder.RANGE_DEFINITION_ODER).createFacetingRequest()); } } } else { final DiscreteFacetContext facetCtx = qb.facet().name(facetingRequestItem.getFacetName()) .onField(facetingRequestItem.getField()).discrete(); facetManager.enableFaceting( facetCtx.includeZeroCounts(facetingRequestItem.isMultiValue()).createFacetingRequest()); if (facetingRequestItem.isMultiValue()) { hasMultivalue = true; } } } final Map<String, List<Pair<String, Integer>>> out = new HashMap<String, List<Pair<String, Integer>>>(); IndexReader indexReader = null; FixedBitSet baseBitSet = null; try { if (hasMultivalue) { indexReader = fullTextSession.getSearchFactory().getIndexReaderAccessor() .open(getPersistentClass()); CachingWrapperFilter baseQueryFilter = new CachingWrapperFilter(new QueryWrapperFilter(query)); try { DocIdSet docIdSet = baseQueryFilter.getDocIdSet(indexReader); if (docIdSet instanceof FixedBitSet) { baseBitSet = (FixedBitSet) docIdSet; } else { baseBitSet = new FixedBitSet(1); } } catch (IOException e) { LOGFTQ.error("Unable to create base query bit set for query {} and faceting request {}", query, facetingRequest); LOGFTQ.error("Stacktrace:", e); baseBitSet = new FixedBitSet(1); } } for (final FilteredNavigationRecordRequest facetingRequestItem : facetingRequest) { final List<Pair<String, Integer>> facetsPairs = new ArrayList<Pair<String, Integer>>(); final List<Facet> facets = facetManager.getFacets(facetingRequestItem.getFacetName()); LOGFTQ.debug("Faceting request request: {}", facetingRequestItem); if (facetingRequestItem.isMultiValue() && !facetingRequestItem.isRangeValue()) { // Multivalue black magic for (final Facet facet : facets) { final org.apache.lucene.search.Query facetQuery = new TermQuery( new Term(facet.getFieldName(), facet.getValue())); try { CachingWrapperFilter filter = new CachingWrapperFilter( new QueryWrapperFilter(facetQuery)); DocIdSet docIdSet = filter.getDocIdSet(indexReader); if (docIdSet instanceof FixedBitSet) { FixedBitSet filterBitSet = (FixedBitSet) docIdSet; filterBitSet.and(baseBitSet); long count = filterBitSet.cardinality(); if (count > 0L) { LOGFTQ.debug("Has facet: {}", facet); facetsPairs.add(new Pair<String, Integer>(facet.getValue(), (int) count)); } } } catch (IOException e) { LOGFTQ.error( "Unable to create filter query bit set for query {} and faceting query {}", query, facetQuery); LOGFTQ.error("Stacktrace:", e); } } } else { // Standard discrete values and ranges for (final Facet facet : facets) { LOGFTQ.debug("Has facet: {}", facet); facetsPairs.add(new Pair<String, Integer>(facet.getValue(), facet.getCount())); } } out.put(facetingRequestItem.getFacetName(), facetsPairs); } } finally { if (hasMultivalue) { fullTextSession.getSearchFactory().getIndexReaderAccessor().close(indexReader); } } return out; } return Collections.emptyMap(); } /** * {@inheritDoc} */ public int fullTextSearchCount(final org.apache.lucene.search.Query query) { if (persistentClassIndexble) { if (LOGFTQ.isDebugEnabled()) { LOGFTQ.debug("Count {}", query); } FullTextSession fullTextSession = Search.getFullTextSession(sessionFactory.getCurrentSession()); FullTextQuery fullTextQuery = fullTextSession.createFullTextQuery(query, getPersistentClass()); fullTextQuery.setMaxResults(1); return fullTextQuery.getResultSize(); } return 0; } /** * {@inheritDoc} */ public int executeNativeUpdate(final String nativeQuery) { SQLQuery sqlQuery = sessionFactory.getCurrentSession().createSQLQuery(nativeQuery); return sqlQuery.executeUpdate(); } /** * {@inheritDoc} */ public List executeNativeQuery(final String nativeQuery) { SQLQuery sqlQuery = sessionFactory.getCurrentSession().createSQLQuery(nativeQuery); return sqlQuery.list(); } /** * {@inheritDoc} */ public List executeHsqlQuery(final String hsql) { Query query = sessionFactory.getCurrentSession().createQuery(hsql); return query.list(); } /** * {@inheritDoc} */ public int executeHsqlUpdate(final String hsql, final Object... parameters) { Query query = sessionFactory.getCurrentSession().createQuery(hsql); setQueryParameters(query, parameters); return query.executeUpdate(); } /** * {@inheritDoc} */ public int executeNativeUpdate(final String nativeQuery, final Object... parameters) { SQLQuery sqlQuery = sessionFactory.getCurrentSession().createSQLQuery(nativeQuery); setQueryParameters(sqlQuery, parameters); return sqlQuery.executeUpdate(); } /** * {@inheritDoc} */ public int executeUpdate(final String namedQueryName, final Object... parameters) { final Query query = sessionFactory.getCurrentSession().getNamedQuery(namedQueryName); setQueryParameters(query, parameters); return query.executeUpdate(); } private void setQueryParameters(final Query query, final Object[] parameters) { if (parameters != null) { int idx = 1; for (Object param : parameters) { if (param instanceof Collection) { query.setParameterList(String.valueOf(idx), (Collection) param); } else { query.setParameter(String.valueOf(idx), param); } idx++; } } } /** * {@inheritDoc} */ public void flushClear() { sessionFactory.getCurrentSession().flush(); sessionFactory.getCurrentSession().clear(); } /** * {@inheritDoc} */ public void flush() { sessionFactory.getCurrentSession().flush(); } /** * {@inheritDoc} */ public void clear() { sessionFactory.getCurrentSession().clear(); } static class FTIndexStateImpl implements FTIndexState { private boolean fullTextSearchReindexInProgress = false; private boolean fullTextSearchReindexCompleted = false; private int lastIndexCount = 0; public FTIndexStateImpl(final boolean fullTextSearchReindexInProgress, final boolean fullTextSearchReindexCompleted, final int lastIndexCount) { this.fullTextSearchReindexInProgress = fullTextSearchReindexInProgress; this.fullTextSearchReindexCompleted = fullTextSearchReindexCompleted; this.lastIndexCount = lastIndexCount; } @Override public boolean isFullTextSearchReindexInProgress() { return fullTextSearchReindexInProgress; } @Override public boolean isFullTextSearchReindexCompleted() { return fullTextSearchReindexCompleted; } @Override public int getLastIndexCount() { return lastIndexCount; } } }