| /* |
| * 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(); |
| } |
| } |