org.eclipse.emf.cdo.server.internal.hibernate.HibernateQueryHandler.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.emf.cdo.server.internal.hibernate.HibernateQueryHandler.java

Source

/*
 * Copyright (c) 2009-2013, 2015 Eike Stepper (Berlin, Germany) and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Martin Taal - initial API and implementation
 *    Eike Stepper - maintenance
 */
package org.eclipse.emf.cdo.server.internal.hibernate;

import org.eclipse.emf.cdo.common.branch.CDOBranchVersion;
import org.eclipse.emf.cdo.common.id.CDOID;
import org.eclipse.emf.cdo.common.revision.CDORevision;
import org.eclipse.emf.cdo.common.util.CDOQueryInfo;
import org.eclipse.emf.cdo.internal.common.branch.CDOBranchVersionImpl;
import org.eclipse.emf.cdo.server.IQueryContext;
import org.eclipse.emf.cdo.server.IQueryHandler;
import org.eclipse.emf.cdo.server.hibernate.IHibernateStore;
import org.eclipse.emf.cdo.server.internal.hibernate.tuplizer.WrappedHibernateList;
import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionManager;

import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.teneo.hibernate.auditing.model.teneoauditing.TeneoAuditEntry;

import org.hibernate.Query;
import org.hibernate.ScrollMode;
import org.hibernate.ScrollableResults;
import org.hibernate.Session;

import java.io.Serializable;
import java.lang.reflect.Array;

/**
 * Implements server side HQL query execution..
 *
 * @author Martin Taal
 */
public class HibernateQueryHandler implements IQueryHandler {

    private HibernateStoreAccessor hibernateStoreAccessor;

    private HibernateAuditHandler hibernateAuditHandler;

    /**
     * Executes hql queries. Gets the session from the {@link HibernateStoreAccessor} creates a hibernate query and sets
     * the parameters taken from the {@link CDOQueryInfo#getParameters()}. Takes into account the
     * {@link CDOQueryInfo#getMaxResults()} and the {@link IHibernateStore#FIRST_RESULT} values for paging.
     *
     * @param info
     *          the object containing the query and parameters
     * @param context
     *          the query results are placed in the context
     * @see IQueryHandler#executeQuery(CDOQueryInfo, IQueryContext)
     */
    public void executeQuery(CDOQueryInfo info, IQueryContext context) {
        // get a transaction, the hibernateStoreAccessor is placed in a threadlocal
        // so all db access uses the same session.
        final Session session = hibernateStoreAccessor.getHibernateSession();
        try {
            // create the query
            final Query query = session.createQuery(info.getQueryString());
            query.setReadOnly(true);

            // get the parameters with some parameter conversion
            int firstResult = -1;
            boolean cacheResults = true;
            for (String key : info.getParameters().keySet()) {
                if (key.compareToIgnoreCase(IHibernateStore.CACHE_RESULTS) == 0) {
                    try {
                        cacheResults = (Boolean) info.getParameters().get(key);
                    } catch (ClassCastException e) {
                        throw new IllegalArgumentException("Parameter " + IHibernateStore.CACHE_RESULTS //$NON-NLS-1$
                                + " must be a boolean. errorMessage " + e.getMessage());
                    }
                } else if (key.compareToIgnoreCase(IHibernateStore.FIRST_RESULT) == 0) {
                    final Object o = info.getParameters().get(key);
                    if (o != null) {
                        try {
                            firstResult = (Integer) o;
                        } catch (ClassCastException e) {
                            throw new IllegalArgumentException(
                                    "Parameter firstResult must be an integer but it is a " + o //$NON-NLS-1$
                                            + " class " + o.getClass().getName()); //$NON-NLS-1$
                        }
                    }
                } else {
                    // in case the parameter is a CDOID get the object from the db
                    final Object param = info.getParameters().get(key);
                    if (param instanceof CDOID && HibernateUtil.getInstance().isStoreCreatedID((CDOID) param)) {
                        final CDOID id = (CDOID) param;
                        final String entityName = HibernateUtil.getInstance().getEntityName(id);
                        final Serializable idValue = HibernateUtil.getInstance().getIdValue(id);
                        final CDORevision revision = (CDORevision) session.get(entityName, idValue);
                        query.setEntity(key, revision);
                        if (cacheResults) {
                            addToRevisionCache(revision);
                        }
                    } else {
                        query.setParameter(key, param);
                    }
                }
            }

            // set the first result
            if (firstResult > -1) {
                query.setFirstResult(firstResult);
            }

            // the max result
            if (info.getMaxResults() != CDOQueryInfo.UNLIMITED_RESULTS) {
                query.setMaxResults(info.getMaxResults());
            }

            final ScrollableResults scroller = query.scroll(ScrollMode.FORWARD_ONLY);

            // and go for the query
            // future extension: support iterate, scroll through a parameter
            int i = 0;
            try {
                while (scroller.next()) {
                    Object[] os = scroller.get();
                    Object o;
                    if (os.length == 1) {
                        o = handleAuditEntries(os[0]);
                    } else {
                        o = handleAuditEntries(os);
                    }

                    final boolean addOneMore = context.addResult(o);
                    if (cacheResults && o instanceof CDORevision) {
                        addToRevisionCache((CDORevision) o);
                    }
                    if (o instanceof InternalCDORevision) {
                        ((InternalCDORevision) o).freeze();
                    }

                    // clear the session every 1000 results or so
                    if (i++ % 1000 == 0) {
                        session.clear();
                    }

                    if (!addOneMore) {
                        return;
                    }
                }
            } finally {
                scroller.close();
            }
        } finally {
            session.close();
        }
    }

    private Object handleAuditEntries(Object o) {
        if (o.getClass().isArray()) {
            for (int i = 0; i < Array.getLength(o); i++) {
                Array.set(o, i, handleAuditEntry(Array.get(o, i)));
            }
            return o;
        }
        return handleAuditEntry(o);
    }

    private Object handleAuditEntry(Object o) {
        if (!(o instanceof TeneoAuditEntry)) {
            // repair revision numbers
            if (o instanceof InternalCDORevision && hibernateStoreAccessor.getStore().isAuditing()) {
                final InternalCDORevision internalCDORevision = (InternalCDORevision) o;
                // a later revision, get the previous revision
                if (internalCDORevision.getVersion() > 1) {
                    final CDORevision previousVersion = getPreviousRevision(internalCDORevision);
                    if (previousVersion != null) {
                        internalCDORevision.setBranchPoint(hibernateStoreAccessor.getStore().getMainBranchHead()
                                .getBranch().getPoint(1 + previousVersion.getRevised()));
                    }
                }
            }

            return o;
        }
        return hibernateAuditHandler.convertAuditEntryToCDORevision((TeneoAuditEntry) o);
    }

    private CDORevision getPreviousRevision(InternalCDORevision internalCDORevision) {
        final InternalCDORevisionManager cdoRevisionManager = hibernateStoreAccessor.getStore().getRepository()
                .getRevisionManager();

        final CDOBranchVersion cdoBranchVersion = new CDOBranchVersionImpl(
                hibernateStoreAccessor.getStore().getMainBranchHead().getBranch(),
                internalCDORevision.getVersion() - 1);
        if (cdoRevisionManager.containsRevisionByVersion(internalCDORevision.getID(), cdoBranchVersion)) {
            return cdoRevisionManager.getRevisionByVersion(internalCDORevision.getID(), cdoBranchVersion, -1, true);
        }
        return hibernateStoreAccessor.readRevisionByVersion(internalCDORevision.getID(), cdoBranchVersion, -1,
                cdoRevisionManager);
    }

    private void addToRevisionCache(CDORevision revision) {
        final InternalCDORevision internalRevision = (InternalCDORevision) revision;
        for (EStructuralFeature feature : revision.getEClass().getEAllStructuralFeatures()) {
            if (!isMappedFeature(internalRevision, feature)) {
                continue;
            }

            if (feature.isMany() || feature instanceof EReference) {
                final Object value = internalRevision.getValue(feature);
                if (value instanceof WrappedHibernateList) {
                    // force the size to be cached
                    ((WrappedHibernateList) value).size();
                }
            }
        }

        hibernateStoreAccessor.addToRevisionCache(revision);
    }

    private boolean isMappedFeature(InternalCDORevision revision, EStructuralFeature feature) {
        try {
            int featureID = revision.getClassInfo().getEClass().getFeatureID(feature);
            revision.getClassInfo().getPersistentFeatureIndex(featureID);
            return true;
        } catch (IllegalArgumentException ex) {
            return false;
        } catch (ArrayIndexOutOfBoundsException ex) {
            return false;
        }
    }

    public HibernateStoreAccessor getHibernateStoreAccessor() {
        return hibernateStoreAccessor;
    }

    public void setHibernateStoreAccessor(HibernateStoreAccessor hibernateStoreAccessor) {
        this.hibernateStoreAccessor = hibernateStoreAccessor;
        hibernateAuditHandler = hibernateStoreAccessor.getStore().getHibernateAuditHandler();
    }
}