blob: ff5c9f24f827920c2de2d5961e9dcd44e21e293b [file] [log] [blame]
/*
* 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 + " must be a boolean. errorMessage " + e.getMessage()); //$NON-NLS-1$
}
}
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();
}
}