/* * Copyright (c) 2008, 2009, 2011-2013, 2015, 2016 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: * Eike Stepper - initial API and implementation * Martin Taal - Implementation */ package org.eclipse.emf.cdo.server.internal.hibernate; import org.eclipse.emf.cdo.common.id.CDOID; import org.eclipse.emf.cdo.common.revision.CDOList; import org.eclipse.emf.cdo.common.revision.CDORevision; import org.eclipse.emf.cdo.server.hibernate.IHibernateStoreChunkReader; 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.server.StoreChunkReader; import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.EStructuralFeature; import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.engine.spi.CollectionEntry; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.persister.collection.QueryableCollection; import java.util.List; /** * @author Eike Stepper */ public class HibernateStoreChunkReader extends StoreChunkReader implements IHibernateStoreChunkReader { public HibernateStoreChunkReader(HibernateStoreAccessor accessor, CDORevision revision, EStructuralFeature feature) { super(accessor, revision, feature); } @Override public HibernateStoreAccessor getAccessor() { return (HibernateStoreAccessor)super.getAccessor(); } public List<Chunk> executeRead() { // get a transaction, the hibernateStoreAccessor is placed in a threadlocal // so all db access uses the same session. final Session session = getAccessor().getHibernateSession(); // reread the revision as it is probably unreferenced final InternalCDORevision latestRevision = getLatestRevision(session); Object value = latestRevision.getValue(getFeature()); if (value instanceof WrappedHibernateList) { value = ((WrappedHibernateList)value).getDelegate(); } // hibernate details... boolean useExtraLazyMode = false; boolean standardCDOList = false; QueryableCollection persister = null; CollectionEntry entry = null; if (value instanceof PersistentCollection) { final PersistentCollection persistentCollection = (PersistentCollection)value; persister = (QueryableCollection)((SessionFactoryImplementor)session.getSessionFactory()).getCollectionPersister(persistentCollection.getRole()); entry = ((SessionImplementor)session).getPersistenceContext().getCollectionEntry(persistentCollection); useExtraLazyMode = !persister.getElementType().isEntityType(); if (useExtraLazyMode && ((PersistentCollection)value).hasQueuedOperations()) { session.flush(); } } else { standardCDOList = true; } final List<Chunk> chunks = getChunks(); for (Chunk chunk : chunks) { final int startIndex = chunk.getStartIndex(); final int maxElements = chunk.size(); if (standardCDOList) { // for eattributes just read them all, no chunking there... final CDOList list = (CDOList)value; if (startIndex >= list.size()) { return chunks; } for (int i = startIndex; i < startIndex + maxElements; i++) { if (i >= list.size()) { break; } addToChunk(chunk, i - startIndex, list.get(i)); } } else if (useExtraLazyMode) { if (getFeature() instanceof EReference) { for (int i = startIndex; i < startIndex + maxElements; i++) { final Object object = persister.getElementByIndex(entry.getLoadedKey(), i, (SessionImplementor)session, latestRevision); // could happen if the index > size) if (object == null) { continue; } addToChunk(chunk, i - startIndex, object); } } else { // for eattributes just read them all, no chunking there... final List<?> list = (List<?>)value; if (startIndex >= list.size()) { return chunks; } for (int i = startIndex; i < startIndex + maxElements; i++) { if (i >= list.size()) { break; } addToChunk(chunk, i - startIndex, list.get(i)); } } } else { final Query filterQuery = session.createFilter(value, ""); filterQuery.setMaxResults(maxElements); filterQuery.setFirstResult(startIndex); int i = 0; for (Object object : filterQuery.list()) { addToChunk(chunk, i++, object); } } } return chunks; } private InternalCDORevision getLatestRevision(Session session) { final CDOID id = getRevision().getID(); final HibernateStore store = getAccessor().getStore(); if (store.isAuditing() && store.getHibernateAuditHandler().getCDOAuditHandler().isAudited(id)) { InternalCDORevision revision = store.getHibernateAuditHandler().readRevision(session, id, getRevision().getTimeStamp()); // found one, use it if (revision != null) { return revision; } } return HibernateUtil.getInstance().getCDORevision(id); } private void addToRevisionCache(Object revision) { final InternalCDORevision internalRevision = (InternalCDORevision)revision; for (EStructuralFeature feature : internalRevision.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(); } } } getAccessor().addToRevisionCache(revision); } private void addToChunk(Chunk chunk, int i, Object object) { if (object instanceof CDORevision) { addToRevisionCache(object); chunk.add(i, HibernateUtil.getInstance().getCDOID(object)); } else { chunk.add(i, object); } } 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; } } }