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

Java tutorial

Introduction

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

Source

/*
 * Copyright (c) 2008-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:
 *    Eike Stepper - initial API and implementation
 *    Martin Taal - specific hibernate functionality
 */
package org.eclipse.emf.cdo.server.internal.hibernate;

import org.eclipse.emf.cdo.common.branch.CDOBranch;
import org.eclipse.emf.cdo.common.branch.CDOBranchHandler;
import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
import org.eclipse.emf.cdo.common.branch.CDOBranchVersion;
import org.eclipse.emf.cdo.common.commit.CDOCommitInfo;
import org.eclipse.emf.cdo.common.commit.CDOCommitInfoHandler;
import org.eclipse.emf.cdo.common.id.CDOID;
import org.eclipse.emf.cdo.common.id.CDOIDTemp;
import org.eclipse.emf.cdo.common.id.CDOIDUtil;
import org.eclipse.emf.cdo.common.lob.CDOLobHandler;
import org.eclipse.emf.cdo.common.model.CDOClassifierRef;
import org.eclipse.emf.cdo.common.protocol.CDODataInput;
import org.eclipse.emf.cdo.common.protocol.CDODataOutput;
import org.eclipse.emf.cdo.common.revision.CDORevision;
import org.eclipse.emf.cdo.common.revision.CDORevisionCacheAdder;
import org.eclipse.emf.cdo.common.revision.CDORevisionData;
import org.eclipse.emf.cdo.common.revision.CDORevisionHandler;
import org.eclipse.emf.cdo.common.util.CDOQueryInfo;
import org.eclipse.emf.cdo.eresource.EresourcePackage;
import org.eclipse.emf.cdo.server.IQueryHandler;
import org.eclipse.emf.cdo.server.ISession;
import org.eclipse.emf.cdo.server.ITransaction;
import org.eclipse.emf.cdo.server.hibernate.IHibernateStore;
import org.eclipse.emf.cdo.server.hibernate.IHibernateStoreAccessor;
import org.eclipse.emf.cdo.server.internal.hibernate.bundle.OM;
import org.eclipse.emf.cdo.server.internal.hibernate.tuplizer.PersistableListHolder;
import org.eclipse.emf.cdo.server.internal.hibernate.tuplizer.WrappedHibernateList;
import org.eclipse.emf.cdo.spi.common.commit.CDOChangeSetSegment;
import org.eclipse.emf.cdo.spi.common.commit.CDOCommitInfoUtil;
import org.eclipse.emf.cdo.spi.common.commit.InternalCDOCommitInfoManager;
import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit;
import org.eclipse.emf.cdo.spi.common.revision.DetachedCDORevision;
import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta;
import org.eclipse.emf.cdo.spi.server.InternalCommitContext;
import org.eclipse.emf.cdo.spi.server.Store;
import org.eclipse.emf.cdo.spi.server.StoreAccessor;

import org.eclipse.net4j.util.HexUtil;
import org.eclipse.net4j.util.ObjectUtil;
import org.eclipse.net4j.util.StringUtil;
import org.eclipse.net4j.util.WrappedException;
import org.eclipse.net4j.util.collection.Pair;
import org.eclipse.net4j.util.io.ExtendedDataInputStream;
import org.eclipse.net4j.util.io.IOUtil;
import org.eclipse.net4j.util.io.LimitedInputStream;
import org.eclipse.net4j.util.om.monitor.OMMonitor;
import org.eclipse.net4j.util.om.monitor.OMMonitor.Async;
import org.eclipse.net4j.util.om.trace.ContextTracer;

import org.eclipse.emf.ecore.EAnnotation;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.EcoreFactory;
import org.eclipse.emf.teneo.Constants;
import org.eclipse.emf.teneo.PackageRegistryProvider;
import org.eclipse.emf.teneo.hibernate.auditing.model.teneoauditing.TeneoAuditCommitInfo;
import org.eclipse.emf.teneo.hibernate.auditing.model.teneoauditing.TeneoAuditEntry;
import org.eclipse.emf.teneo.hibernate.auditing.model.teneoauditing.TeneoauditingPackage;

import org.hibernate.Criteria;
import org.hibernate.FlushMode;
import org.hibernate.Query;
import org.hibernate.ScrollMode;
import org.hibernate.ScrollableResults;
import org.hibernate.Session;
import org.hibernate.SessionFactory;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Serializable;
import java.io.Writer;
import java.sql.Clob;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Implements the runtime behavior of accessing the hibernate store using queries and doing write and commit. The
 * HibernateStoreAccessor corresponds roughly to a Hibernate session. It offers methods to create and close them and
 * implements transaction handling. The main update/create/delete operations are done in the
 * {@link #write(InternalCommitContext, OMMonitor)} method.
 *
 * @see HibernateStore
 * @see HibernatePackageHandler
 * @author Eike Stepper
 * @author Martin Taal
 */
public class HibernateStoreAccessor extends StoreAccessor implements IHibernateStoreAccessor {
    private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, HibernateStoreAccessor.class);

    private static final String NAME_EFEATURE_NAME = "name";//$NON-NLS-1$

    // used to tag an ereference
    private static final String TENEO_MAPPED_SOURCE = "teneo.mapped";

    private static final String TENEO_UNMAPPED_SOURCE = "teneo.unmapped";

    private Session hibernateSession;

    private boolean errorOccured;

    private int currentListChunk = -1;

    private HibernateRawCommitContext rawCommitContext = new HibernateRawCommitContext();

    public void addToRevisionCache(Object object) {
        if (object instanceof CDORevision) {
            getStore().getRepository().getRevisionManager().addRevision((CDORevision) object);
        } else if (object instanceof Object[]) {
            // handle hibernate query result
            final Object[] objects = (Object[]) object;
            for (Object o : objects) {
                addToRevisionCache(o);
            }
        }

        // also primitive types can get here, ignore those
    }

    /**
     * Constructor
     *
     * @param store
     *          the {@link Store} used by the accessor.
     * @param session
     *          the client session (not a Hibernate Session)
     */
    public HibernateStoreAccessor(HibernateStore store, ISession session) {
        super(store, session);
        if (TRACER.isEnabled()) {
            TRACER.trace(
                    "Created " + this.getClass().getName() + " for repository " + store.getRepository().getName()); //$NON-NLS-1$ //$NON-NLS-2$
        }
    }

    /**
     * Constructor for a specific transaction
     *
     * @param store
     *          the HibernateStore backing this accessor
     * @param transaction
     *          the client transaction (not the a Hibernate transaction)
     */
    public HibernateStoreAccessor(HibernateStore store, ITransaction transaction) {
        super(store, transaction);
        if (TRACER.isEnabled()) {
            TRACER.trace(
                    "Created " + this.getClass().getName() + " for repository " + store.getRepository().getName()); //$NON-NLS-1$ //$NON-NLS-2$
        }
    }

    /**
     * @return the backing store
     */
    @Override
    public HibernateStore getStore() {
        return (HibernateStore) super.getStore();
    }

    /**
     * Starts a hibernate session and begins a transaction.
     *
     * @since 2.0
     */
    public void beginHibernateSession() {
        if (TRACER.isEnabled()) {
            TRACER.trace("Creating hibernate session and transaction"); //$NON-NLS-1$
        }

        assert hibernateSession == null;
        final SessionFactory sessionFactory = getStore().getHibernateSessionFactory();
        hibernateSession = sessionFactory.openSession();
        hibernateSession.setDefaultReadOnly(true);
        hibernateSession.setFlushMode(FlushMode.MANUAL);
        hibernateSession.beginTransaction();
    }

    /**
     * Calls {@link #endHibernateSession()}, commits the transaction and closes the session.
     *
     * @since 2.0
     */
    public void commitRollbackHibernateSession() {
        endHibernateSession();
    }

    /**
     * Commits/rollbacks and closes the session
     *
     * @since 2.0
     */
    public void endHibernateSession() {
        if (TRACER.isEnabled()) {
            TRACER.trace("Closing hibernate session"); //$NON-NLS-1$
        }

        if (hibernateSession != null && hibernateSession.isOpen()) {
            try {
                if (hibernateSession.getTransaction().isActive()) {
                    if (TRACER.isEnabled()) {
                        TRACER.trace("Commiting hibernate session"); //$NON-NLS-1$
                    }

                    if (isErrorOccured()) {
                        if (TRACER.isEnabled()) {
                            TRACER.trace("Rolling back hb transaction"); //$NON-NLS-1$
                        }

                        hibernateSession.getTransaction().rollback();
                    } else {
                        if (TRACER.isEnabled()) {
                            TRACER.trace("Committing hb transaction"); //$NON-NLS-1$
                        }

                        hibernateSession.getTransaction().commit();
                    }
                }
            } finally {
                hibernateSession.close();
            }
        }

        hibernateSession = null;
    }

    /**
     * @return the current hibernate session. If there is none then a new one is created and a transaction is started.
     *
     * Note the default is a readonly flushmode manual session.
     */
    public Session getHibernateSession() {
        if (hibernateSession == null) {
            beginHibernateSession();
        }
        return hibernateSession;
    }

    /**
     * Closes/commits the current hibernate session if there is one, and starts a new one and begins a transaction.
     *
     * @return a newly created Hibernate Session
     */
    public Session getNewHibernateSession(boolean readOnly) {
        if (hibernateSession != null) {
            endHibernateSession();
        }

        if (hibernateSession != null) {
            throw new IllegalStateException("Hibernate session should be null"); //$NON-NLS-1$
        }

        beginHibernateSession();
        return hibernateSession;
    }

    /**
     * @return true if an error occured during database actions. Normally means that the transaction will be rolled back
     *         and not committed.
     * @since 2.0
     */
    public boolean isErrorOccured() {
        return errorOccured;
    }

    /**
     * @since 2.0
     */
    public void setErrorOccured(boolean errorOccured) {
        this.errorOccured = errorOccured;
    }

    /**
     * @return a {@link HibernateStoreChunkReader}
     */
    public HibernateStoreChunkReader createChunkReader(InternalCDORevision revision, EStructuralFeature feature) {
        return new HibernateStoreChunkReader(this, revision, feature);
    }

    /**
     * @return the current collection of package units.
     * @see HibernateStore
     * @see HibernatePackageHandler
     */
    public Collection<InternalCDOPackageUnit> readPackageUnits() {
        return getStore().getPackageHandler().getPackageUnits();
    }

    /**
     * Loads the package units from the database and returns the EPackage instances.
     *
     * @return the loaded EPackage instances.
     * @see HibernatePackageHandler
     */
    public EPackage[] loadPackageUnit(InternalCDOPackageUnit packageUnit) {
        return getStore().getPackageHandler().loadPackageUnit(packageUnit);
    }

    /**
     * Reads the revision from the database. using the passed id.
     *
     * @param id
     *          identifies the CDORevision to read
     * @param branchPoint
     *          ignored until auditing is supported.
     * @param listChunk
     *          not used by Hibernate
     * @param cache
     *          the revision cache, the read revision is added to the cache
     * @return the read revision
     */
    public InternalCDORevision readRevision(CDOID id, CDOBranchPoint branchPoint, int listChunk,
            CDORevisionCacheAdder cache) {
        if (!HibernateUtil.getInstance().isStoreCreatedID(id)) {
            return null;
        }
        currentListChunk = listChunk;
        try {
            if (getStore().isAuditing()
                    && getStore().getHibernateAuditHandler().getCDOAuditHandler().isAudited(id)) {
                InternalCDORevision revision = getStore().getHibernateAuditHandler()
                        .readRevision(getHibernateSession(), id, branchPoint.getTimeStamp());
                // found one, use it
                if (revision != null) {

                    if (cache != null) {
                        cache.addRevision(revision);
                    }
                    revision.freeze();

                    return revision;
                }
            }

            final InternalCDORevision revision = HibernateUtil.getInstance().getCDORevision(id);
            if (revision == null) {
                final CDOClassifierRef classifierRef = CDOIDUtil.getClassifierRef(id);
                if (classifierRef == null) {
                    throw new IllegalArgumentException(
                            "This CDOID type of " + id + " is not supported by this store."); //$NON-NLS-1$ //$NON-NLS-2$
                }

                final EClass eClass = HibernateUtil.getInstance().getEClass(classifierRef);
                return new DetachedCDORevision(eClass, id, branchPoint.getBranch(), 0, 0);
            }

            revision.setBranchPoint(getStore().getMainBranchHead());
            revision.freeze();

            if (cache != null) {
                cache.addRevision(revision);
            }

            return revision;
        } finally {
            currentListChunk = -1;
        }
    }

    public Pair<Integer, Long> createBranch(int branchID, BranchInfo branchInfo) {
        // TODO: implement HibernateStoreAccessor.createBranch(branchID, branchInfo)
        throw new UnsupportedOperationException();
    }

    public BranchInfo loadBranch(int branchID) {
        // TODO: implement HibernateStoreAccessor.loadBranch(branchID)
        throw new UnsupportedOperationException();
    }

    public SubBranchInfo[] loadSubBranches(int branchID) {
        // TODO: implement HibernateStoreAccessor.loadSubBranches(branchID)
        throw new UnsupportedOperationException();
    }

    public int loadBranches(int startID, int endID, CDOBranchHandler branchHandler) {
        // TODO: implement HibernateStoreAccessor.loadBranches(startID, endID, branchHandler)
        throw new UnsupportedOperationException();
    }

    public void loadCommitInfos(CDOBranch branch, long startTime, long endTime, CDOCommitInfoHandler handler) {
        // no commit info support
        if (!getStore().isAuditing()) {
            return;
        }

        final Session session = getHibernateSession();
        final InternalCDOCommitInfoManager commitInfoManager = getStore().getRepository().getCommitInfoManager();

        // only get a specific range of objects
        String direction = " desc ";
        int count = 0;
        if (endTime < CDOBranchPoint.UNSPECIFIED_DATE) {
            count = CDOCommitInfoUtil.decodeCount(endTime);
            if (count < 0) {
                direction = " desc ";
                count = -1 * count;
            } else {
                direction = " asc ";
            }
        }

        final String qryStr = "select e from TeneoAuditCommitInfo e where e.commitTime>=:startTime and e.commitTime<=:endTime order by e.commitTime "
                + direction;
        final Query qry = session.createQuery(qryStr);
        if (count > 0) {
            qry.setMaxResults(count);
        }
        qry.setParameter("startTime", startTime);
        qry.setParameter("endTime",
                endTime == CDOBranchPoint.UNSPECIFIED_DATE || endTime < 0 ? Long.MAX_VALUE : endTime);
        for (Object o : qry.list()) {
            final TeneoAuditCommitInfo teneoCommitInfo = (TeneoAuditCommitInfo) o;
            final CDOCommitInfo cdoCommitInfo = commitInfoManager.createCommitInfo(
                    getStore().getRepository().getBranchManager().getMainBranch(), teneoCommitInfo.getCommitTime(),
                    teneoCommitInfo.getCommitTime() - 1, teneoCommitInfo.getUser(), teneoCommitInfo.getComment(),
                    null);
            handler.handleCommitInfo(cdoCommitInfo);
        }
    }

    public Set<CDOID> readChangeSet(OMMonitor monitor, CDOChangeSetSegment... segments) {
        // TODO: implement HibernateStoreAccessor.readChangeSet(segments)
        throw new UnsupportedOperationException();
    }

    // should only return revisions of the eclass itself and not of its subclasses.
    public void handleRevisions(EClass eClass, CDOBranch branch, long timeStamp, boolean exactTime,
            CDORevisionHandler handler) {

        if (eClass != null) {
            if (!getStore().isMapped(eClass)) {
                return;
            }

            handleRevisionsByEClass(eClass, handler, timeStamp);
        } else {
            for (EPackage ePackage : getStore().getPackageHandler().getEPackages()) {
                // an auditing epackage
                if (ePackage == TeneoauditingPackage.eINSTANCE
                        || ePackage.getEAnnotation(Constants.ANNOTATION_SOURCE_AUDITING) != null) {
                    continue;
                }

                for (EClassifier eClassifier : ePackage.getEClassifiers()) {
                    if (eClassifier instanceof EClass) {
                        final EClass eClazz = (EClass) eClassifier;
                        if (!getStore().isMapped(eClazz)) {
                            continue;
                        }
                        handleRevisionsByEClass(eClazz, handler, timeStamp);
                    }
                }
            }
        }
    }

    private void handleRevisionsByEClass(EClass eClass, CDORevisionHandler handler, long timeStamp) {
        // get a transaction, the hibernateStoreAccessor is placed in a threadlocal
        // so all db access uses the same session.
        final Session session = getHibernateSession();
        try {
            if (timeStamp > 0) {
                getStore().getHibernateAuditHandler().handleRevisionsByEClass(session, eClass, handler, timeStamp);
                return;
            }

            // create the query
            final Query query = session.createQuery("select e from " + getStore().getEntityName(eClass) + " e");
            for (Object o : query.list()) {
                CDORevision cdoRevision = (CDORevision) o;

                // if a subclass ignore
                if (cdoRevision.getEClass() != eClass) {
                    continue;
                }

                if (!handler.handleRevision(cdoRevision)) {
                    return;
                }
            }
        } finally {
            session.clear();
        }
    }

    /**
     * @see #readRevision(CDOID, CDOBranchPoint, int, CDORevisionCacheAdder)
     */
    public InternalCDORevision readRevisionByVersion(CDOID id, CDOBranchVersion branchVersion, int listChunk,
            CDORevisionCacheAdder cache) {
        InternalCDORevision revision = null;
        if (getStore().getHibernateAuditHandler().getCDOAuditHandler().isAudited(id)) {
            revision = getStore().getHibernateAuditHandler().readRevisionByVersion(getHibernateSession(), id,
                    branchVersion.getVersion());
        } else {
            revision = readRevision(id, branchVersion.getBranch().getPoint(System.currentTimeMillis()), listChunk,
                    cache);
            if (revision != null) {
                // otherwise CDO gets confused and we get wrong version numbers later
                revision.setVersion(branchVersion.getVersion());
            }
        }

        if (revision != null && !(revision instanceof DetachedCDORevision)) {
            revision.freeze();
        }

        if (cache != null) {
            cache.addRevision(revision);
        }

        return revision;
    }

    /**
     * Queries for resources in a certain folder and returns them in the context object
     *
     * @param context
     *          the context provides input parameters (the folder) and is used to store the results of the query.
     * @since 2.0
     */
    public void queryResources(QueryResourcesContext context) {
        final Session session = getHibernateSession();

        final CDOID folderID = getHibernateID(context.getFolderID());
        String name = context.getName();
        boolean exactMatch = context.exactMatch();
        final HibernateAuditHandler hibernateAuditHandler = getStore().getHibernateAuditHandler();

        List<?> result = null;
        if (context.getTimeStamp() == 0 || !getStore().isAuditing()) {

            final Criteria criteria = session
                    .createCriteria(EresourcePackage.eINSTANCE.getCDOResourceNode().getName());
            if (folderID == null) {
                criteria.add(org.hibernate.criterion.Restrictions.isNull("folder"));
            } else {
                criteria.add(org.hibernate.criterion.Restrictions.eq("folder.id", CDOIDUtil.getLong(folderID)));
            }

            result = criteria.list();
        } else {
            result = hibernateAuditHandler.getCDOResources(session, folderID, context.getTimeStamp());
        }

        for (Object o : result) {
            final CDORevision revision;
            if (o instanceof CDORevision) {
                revision = (CDORevision) o;
            } else {
                final TeneoAuditEntry teneoAuditEntry = (TeneoAuditEntry) o;
                revision = hibernateAuditHandler.getCDORevision(session, teneoAuditEntry);
            }

            ((InternalCDORevision) revision).freeze();

            final EStructuralFeature feature = revision.getEClass().getEStructuralFeature(NAME_EFEATURE_NAME);
            if (feature != null) {
                Object value = revision.data().get(feature, 0);
                if (value == CDORevisionData.NIL) {
                    value = null;
                }

                final String revisionName = (String) value;
                final boolean match = exactMatch || revisionName == null || name == null
                        ? ObjectUtil.equals(revisionName, name)
                        : revisionName.startsWith(name);

                if (match && !context.addResource(HibernateUtil.getInstance().getCDOID(revision))) {
                    // No more results allowed
                    break;
                }
            }
        }
    }

    public void queryXRefs(QueryXRefsContext context) {
        final Session session = getHibernateSession();
        for (CDOID targetCdoId : context.getTargetObjects().keySet()) {
            final CDORevision revision = HibernateUtil.getInstance().getCDORevision(targetCdoId);
            final EClass targetEClass = context.getTargetObjects().get(targetCdoId);

            if (!getStore().isMapped(targetEClass)) {
                continue;
            }

            final String targetEntityName = getStore().getEntityName(targetEClass);
            final Map<EClass, List<EReference>> sourceCandidates = context.getSourceCandidates();
            for (EClass sourceEClass : sourceCandidates.keySet()) {

                if (!getStore().isMapped(sourceEClass)) {
                    continue;
                }

                final String sourceEntityName = getStore().getEntityName(sourceEClass);
                for (EReference eref : sourceCandidates.get(sourceEClass)) {
                    // handle transient ereferences
                    if (!isEReferenceMapped(session, sourceEntityName, eref)) {
                        continue;
                    }

                    final String hql;
                    if (eref.isMany()) {
                        hql = "select ref from " + sourceEntityName + " as ref, " + targetEntityName
                                + " as refTo where refTo = :to and refTo in elements(ref." + eref.getName() + ")";
                    } else {
                        hql = "select ref from " + sourceEntityName + " as ref where :to = ref." + eref.getName();
                    }

                    final Query qry = session.createQuery(hql);
                    qry.setEntity("to", revision);
                    ScrollableResults result = qry.scroll(ScrollMode.FORWARD_ONLY);
                    while (result.next()) {
                        final InternalCDORevision sourceRevision = (InternalCDORevision) result.get()[0];

                        sourceRevision.freeze();

                        int sourceIndex = 0;
                        if (eref.isMany()) {
                            // note this takes performance for sure as the list is read,
                            // consider not supporting sourceIndex, or doing it differently
                            final WrappedHibernateList cdoList = (WrappedHibernateList) sourceRevision
                                    .getList(eref);
                            sourceIndex = cdoList.getDelegate().indexOf(revision);
                        }

                        boolean more = context.addXRef(targetCdoId, sourceRevision.getID(), eref, sourceIndex);
                        if (!more) {
                            return;
                        }
                    }
                }
            }
        }
    }

    private boolean isEReferenceMapped(Session session, String entityName, EReference eref) {
        // mapped
        if (null != eref.getEAnnotation(TENEO_MAPPED_SOURCE)) {
            return true;
        } else
        // not mapped
        if (null != eref.getEAnnotation(TENEO_UNMAPPED_SOURCE)) {
            return false;
        }

        // not computed yet
        for (String propName : session.getSessionFactory().getClassMetadata(entityName).getPropertyNames()) {
            if (propName.equals(eref.getName())) {
                final EAnnotation eAnnotation = EcoreFactory.eINSTANCE.createEAnnotation();
                eAnnotation.setSource(TENEO_MAPPED_SOURCE);
                eref.getEAnnotations().add(eAnnotation);
                return true;
            }
        }
        // not mapped
        final EAnnotation eAnnotation = EcoreFactory.eINSTANCE.createEAnnotation();
        eAnnotation.setSource(TENEO_UNMAPPED_SOURCE);
        eref.getEAnnotations().add(eAnnotation);
        return false;
    }

    private CDOID getHibernateID(CDOID id) {
        if (!CDOIDUtil.isNull(id)) {
            if (HibernateUtil.getInstance().isStoreCreatedID(id)) {
                return id;
            }

            // TODO Can this happen? When?
            // the folder id is always a long
            final Long idValue = CDOIDUtil.getLong(id);
            return CDOIDUtil.createLongWithClassifier(idValue,
                    new CDOClassifierRef(EresourcePackage.eINSTANCE.getCDOResourceNode()));
        }

        return null;
    }

    /**
     * @param info
     *          the query information, is not used actively in this method.
     * @return a new instance of {@link HibernateQueryHandler}
     */
    public IQueryHandler getQueryHandler(CDOQueryInfo info) {
        String queryLanguage = info.getQueryLanguage();
        if (StringUtil.equalsUpperOrLowerCase(queryLanguage, IHibernateStore.QUERY_LANGUAGE)) {
            final HibernateQueryHandler queryHandler = new HibernateQueryHandler();
            queryHandler.setHibernateStoreAccessor(this);
            return queryHandler;
        }

        return null;
    }

    /**
     * Commits the session, see {@link #commitRollbackHibernateSession()}.
     *
     * @param monitor
     *          not used
     */
    @Override
    protected void doCommit(OMMonitor monitor) {
        commitRollbackHibernateSession();
        HibernateThreadContext.setCommitContext(null);
    }

    /**
     * Performs the main write and update actions. Persists new EPackages, updates changed objects, creates new ones and
     * removes deleted objects. Updates both container as well as resource associations.
     *
     * @param context
     *          the context contains the changed, new and to-be-removed objects
     * @param monitor
     *          not used by this method
     */
    @Override
    public void doWrite(InternalCommitContext context, OMMonitor monitor) {
        // NOTE: the same flow is also present in the super class (StoreAccessor)
        // changes in flow can mean that the flow here also has to change

        monitor.begin(3);
        HibernateThreadContext.setCommitContext(context);
        if (context.getNewPackageUnits().length > 0) {
            writePackageUnits(context.getNewPackageUnits(), monitor.fork());
        }

        // Note: instead of an Async here, we could do much more fine-grained monitoring below. But this
        // simplistic solution is sufficient to prevent timeout errors.
        final Async async = monitor.forkAsync();
        HibernateThreadContext.getCommitContext().setInDoWrite(true);
        try {
            // start with fresh hibernate session to prevent side effects
            final Session session = context instanceof HibernateRawCommitContext ? getHibernateSession()
                    : getNewHibernateSession(false);
            session.setDefaultReadOnly(false);

            // decrement version, hibernate will increment it
            decrementVersions(context);

            // order is 1) insert, 2) update and then delete
            // this order is the most stable! Do not change it without testing

            // System.err.println(getStore().getMappingXml());

            final List<InternalCDORevision> repairContainerIDs = new ArrayList<InternalCDORevision>();
            final List<InternalCDORevision> repairResourceIDs = new ArrayList<InternalCDORevision>();
            for (InternalCDORevision revision : context.getNewObjects()) {
                revision.setListPreserving();

                // keep track for which cdoRevisions the container id needs to be repaired afterwards
                final CDOID containerID = (CDOID) revision.getContainerID();
                if (containerID instanceof CDOIDTemp && !containerID.isNull()) {
                    repairContainerIDs.add(revision);
                }

                final CDOID resourceID = revision.getResourceID();
                if (resourceID instanceof CDOIDTemp && !resourceID.isNull()) {
                    repairResourceIDs.add(revision);
                }

                final String entityName = getStore().getEntityName(revision.getEClass());
                session.saveOrUpdate(entityName, revision);
            }

            // now apply all the changes
            if (context.getDirtyObjectDeltas() != null) {
                for (InternalCDORevisionDelta delta : context.getDirtyObjectDeltas()) {
                    final String entityName = HibernateUtil.getInstance().getEntityName(delta.getID());
                    final Serializable idValue = HibernateUtil.getInstance().getIdValue(delta.getID());
                    final InternalCDORevision cdoRevision = (InternalCDORevision) session.get(entityName, idValue);
                    cdoRevision.setListPreserving();
                    delta.applyTo(cdoRevision);
                }
            }

            // preserve old behavior for the hibernate raw commit
            if (context instanceof HibernateRawCommitContext) {
                // now check the versions and store the hibernate revision to repair
                // versions later on. The versions can be updated when inserting new objects
                // this will result in a version difference when the object gets merged
                // this repair is done just before the merge
                final Map<CDOID, InternalCDORevision> existingRevisions = CDOIDUtil.createMap();
                for (InternalCDORevision revision : context.getDirtyObjects()) {
                    final String entityName = HibernateUtil.getInstance().getEntityName(revision.getID());
                    final Serializable idValue = HibernateUtil.getInstance().getIdValue(revision.getID());
                    final InternalCDORevision cdoRevision = (InternalCDORevision) session.get(entityName, idValue);
                    if (cdoRevision != null) {
                        if (cdoRevision.getVersion() != revision.getVersion()) {
                            throw new IllegalStateException(
                                    "Revision " + cdoRevision + " was already updated by another transaction");
                        }
                        existingRevisions.put(revision.getID(), cdoRevision);
                    }
                }

                for (InternalCDORevision revision : context.getDirtyObjects()) {
                    final String entityName = HibernateUtil.getInstance().getEntityName(revision.getID());
                    final InternalCDORevision existingRevision = existingRevisions.get(revision.getID());
                    if (existingRevision != null) {
                        revision.setVersion(existingRevision.getVersion());
                    }

                    final InternalCDORevision cdoRevision = (InternalCDORevision) session.merge(entityName,
                            revision);
                    if (getStore().isAuditing() && cdoRevision.getVersion() == revision.getVersion()) {
                        // do a direct update of the version in the db to get it in sync with
                        // hibernate, a special case, hibernate does not send the change back, do it ourselves
                        // only needs to be done in case of auditing
                        cdoRevision.setVersion(cdoRevision.getVersion() + 1);
                    }

                    if (TRACER.isEnabled()) {
                        TRACER.trace(
                                "Updated Object " + revision.getEClass().getName() + " id: " + revision.getID()); //$NON-NLS-1$ //$NON-NLS-2$
                    }
                }
            }

            // and increment the versions stored in the context
            // note that this is needed because above the cdorevision read from the db
            // is updated and its version gets incremented, and not the revision currently
            // in the cache
            incrementVersions(context);

            session.flush();

            // delete all objects
            for (CDOID id : context.getDetachedObjects()) {
                try {
                    final CDORevision revision = HibernateUtil.getInstance().getCDORevision(id);

                    // maybe deleted in parallell?
                    if (revision != null) {
                        session.delete(revision);
                    }
                } catch (org.hibernate.ObjectNotFoundException ex) {
                    // ignore these, an object can be removed through cascade deletes
                }
            }

            session.flush();

            // now do an update of the container without incrementing the version
            repairContainerIDs(repairContainerIDs, session);
            repairResourceIDs(repairResourceIDs, session);

            session.flush();

            // write the blobs
            ExtendedDataInputStream in = context.getLobs();
            if (in != null) {
                try {
                    int count = in.readInt();
                    for (int i = 0; i < count; i++) {
                        byte[] id = in.readByteArray();
                        long size = in.readLong();
                        if (size > 0) {
                            writeBlob(id, size, new LimitedInputStream(in, size));
                        } else {
                            writeClob(id, -size, new InputStreamReader(new LimitedInputStream(in, -size)));
                        }
                    }
                } catch (IOException ex) {
                    throw WrappedException.wrap(ex);
                }
            }

            session.flush();

        } catch (Exception e) {
            OM.LOG.error(e);
            throw WrappedException.wrap(e);
        } finally {
            HibernateThreadContext.getCommitContext().setInDoWrite(false);
            async.stop();
        }

        context.applyIDMappings(monitor.fork());
        monitor.done();
    }

    // set the version one back, hibernate will update it
    private void decrementVersions(CommitContext context) {
        for (InternalCDORevision revision : context.getNewObjects()) {
            revision.setVersion(revision.getVersion() - 1);
        }
        for (InternalCDORevision revision : context.getDirtyObjects()) {
            revision.setVersion(revision.getVersion() - 1);
        }
    }

    private void incrementVersions(CommitContext context) {
        for (InternalCDORevision revision : context.getNewObjects()) {
            revision.setVersion(1);
        }
        for (InternalCDORevision revision : context.getDirtyObjects()) {
            revision.setVersion(revision.getVersion() + 1);
        }
    }

    private void repairContainerIDs(List<InternalCDORevision> repairContainerIDs, Session session) {
        for (InternalCDORevision revision : repairContainerIDs) {
            final CDORevision container = HibernateUtil.getInstance()
                    .getCDORevision((CDOID) revision.getContainerID());
            final String entityName = getStore().getEntityName(revision.getEClass());
            final CDOID id = revision.getID();
            final String hqlUpdate = "update " + entityName + " set " + CDOHibernateConstants.CONTAINER_PROPERTY //$NON-NLS-1$ //$NON-NLS-2$
                    + " = :containerInfo where " + getStore().getIdentifierPropertyName(entityName) + " = :id"; //$NON-NLS-1$ //$NON-NLS-2$
            final Query qry = session.createQuery(hqlUpdate);
            qry.setParameter("containerInfo", //$NON-NLS-1$
                    ContainerInfoConverter.getInstance().convertContainerRelationToString(revision, container.getID()));
            qry.setParameter("id", HibernateUtil.getInstance().getIdValue(id)); //$NON-NLS-1$
            if (qry.executeUpdate() != 1) {
                // OM.LOG.error("Not able to update container columns of " + entityName + " with id " + id); //$NON-NLS-1$
                // //$NON-NLS-2$
                throw new IllegalStateException(
                        "Not able to update container columns of " + entityName + " with id " + id); //$NON-NLS-1$ //$NON-NLS-2$
            }
        }
    }

    private void repairResourceIDs(List<InternalCDORevision> repairResourceIDs, Session session) {
        for (InternalCDORevision revision : repairResourceIDs) {
            final CDORevision resource = HibernateUtil.getInstance().getCDORevision(revision.getResourceID());
            final String entityName = getStore().getEntityName(revision.getEClass());
            final CDOID id = revision.getID();
            final String hqlUpdate = "update " + entityName + " set " + CDOHibernateConstants.RESOURCE_PROPERTY //$NON-NLS-1$ //$NON-NLS-2$
                    + " = :resourceInfo where " + getStore().getIdentifierPropertyName(entityName) + " = :id"; //$NON-NLS-1$ //$NON-NLS-2$
            final Query qry = session.createQuery(hqlUpdate);
            qry.setParameter("resourceInfo", resource.getID()); //$NON-NLS-1$
            qry.setParameter("id", HibernateUtil.getInstance().getIdValue(id)); //$NON-NLS-1$
            if (qry.executeUpdate() != 1) {
                // OM.LOG.error("Not able to update resource ids of " + entityName + " with id " + id); //$NON-NLS-1$
                // //$NON-NLS-2$
                throw new IllegalStateException(
                        "Not able to update resource ids of " + entityName + " with id " + id); //$NON-NLS-1$ //$NON-NLS-2$
            }
        }
    }

    @Override
    protected void detachObjects(CDOID[] detachedObjects, CDOBranch branch, long timeStamp, OMMonitor monitor) {
        // handled by the write method
    }

    @Override
    protected void doRollback(CommitContext context) {
        setErrorOccured(true);
        endHibernateSession();
        HibernateThreadContext.setCommitContext(null);
    }

    /**
     * Writes package units to the datbaase.
     *
     * @param packageUnits
     *          the package units to write to the database
     * @param monitor
     *          not used by the store
     * @see HibernatePackageHandler
     */
    public void writePackageUnits(InternalCDOPackageUnit[] packageUnits, OMMonitor monitor) {
        if (packageUnits != null && packageUnits.length != 0) {
            getStore().getPackageHandler().writePackageUnits(packageUnits);
        }
    }

    @Override
    protected void writeCommitInfo(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID,
            String comment, OMMonitor monitor) {
        // is done in dowrite
    }

    @Override
    protected void writeRevisions(InternalCDORevision[] revisions, CDOBranch branch, OMMonitor monitor) {
        // Doesn't do anything. It is done in commit().
    }

    @Override
    public void addIDMappings(InternalCommitContext commitContext, OMMonitor monitor) {
        // Do nothing
    }

    @Override
    public CDOID getNextCDOID(CDORevision revision) {
        // Never called
        throw new UnsupportedOperationException();
    }

    @Override
    protected void writeRevisionDeltas(InternalCDORevisionDelta[] revisionDeltas, CDOBranch branch, long created,
            OMMonitor monitor) {
        // TODO: implement HibernateStoreAccessor.writeRevisionDeltas(revisionDeltas, branch, created, monitor)
        throw new UnsupportedOperationException();
    }

    public void queryLobs(List<byte[]> ids) {
        for (Iterator<byte[]> it = ids.iterator(); it.hasNext();) {
            byte[] id = it.next();
            final HibernateStoreLob lob = getCreateHibernateStoreLob(id);
            if (lob.isNew()) {
                it.remove();
            }
        }
    }

    public void handleLobs(long fromTime, long toTime, CDOLobHandler handler) throws IOException {
        final Session session = getHibernateSession();
        final Query qry = session.createQuery("select c from " + HibernateStoreLob.class.getName() + " as c");

        try {
            for (Object o : qry.list()) {
                final HibernateStoreLob lob = (HibernateStoreLob) o;
                if (lob.getBlob() != null) {
                    final OutputStream out = handler.handleBlob(HexUtil.hexToBytes(lob.getId()), lob.getSize());
                    if (out != null) {
                        final InputStream in = lob.getBlob().getBinaryStream();
                        try {
                            IOUtil.copyBinary(in, out, lob.getSize());
                        } finally {
                            IOUtil.close(out);
                        }
                    }
                } else {
                    final Clob clob = lob.getClob();
                    Reader in = clob.getCharacterStream();
                    Writer out = handler.handleClob(HexUtil.hexToBytes(lob.getId()), lob.getSize());
                    if (out != null) {
                        try {
                            IOUtil.copyCharacter(in, out, lob.getSize());
                        } finally {
                            IOUtil.close(out);
                        }
                    }
                }
            }
        } catch (SQLException ex) {
            throw new IllegalStateException(ex);
        }
    }

    public void loadLob(byte[] id, OutputStream out) throws IOException {
        final HibernateStoreLob lob = getCreateHibernateStoreLob(id);
        // can this ever occur?
        // TODO: how should non-existence be handled? Currently results in a timeout
        // on the client.
        if (lob.isNew()) {
            throw new IllegalStateException("Lob with id " + HexUtil.bytesToHex(id) + " does not exist");
        }

        final long size = lob.getSize();
        try {
            if (lob.getBlob() != null) {
                InputStream in = lob.getBlob().getBinaryStream();
                IOUtil.copyBinary(in, out, size);
            } else {
                Clob clob = lob.getClob();
                Reader in = clob.getCharacterStream();
                IOUtil.copyCharacter(in, new OutputStreamWriter(out), size);
            }
        } catch (Exception e) {
            throw new IllegalStateException(e);
        }
    }

    @Override
    protected void writeBlob(byte[] id, long size, InputStream inputStream) throws IOException {
        final HibernateStoreLob lob = getCreateHibernateStoreLob(id);
        if ((inputStream == null || size == 0) && !lob.isNew()) {
            getHibernateSession().delete(lob);
        } else {
            // deprecated usage, non-deprecated api uses a session
            // TODO: research which session to use
            lob.setBlob(getHibernateSession().getLobHelper().createBlob(inputStream, (int) size));
            lob.setSize((int) size);
            lob.setClob(null);
            getHibernateSession().saveOrUpdate(lob);
        }
    }

    @Override
    protected void writeClob(byte[] id, long size, Reader reader) throws IOException {
        final HibernateStoreLob lob = getCreateHibernateStoreLob(id);
        if ((reader == null || size == 0) && !lob.isNew()) {
            getHibernateSession().delete(lob);
        } else {
            // deprecated usage, non-deprecated api uses a session
            // TODO: research which session to use
            lob.setClob(getHibernateSession().getLobHelper().createClob(reader, (int) size));
            lob.setSize((int) size);
            lob.setBlob(null);
            getHibernateSession().saveOrUpdate(lob);
        }
    }

    private HibernateStoreLob getCreateHibernateStoreLob(byte[] idBytes) {
        final String id = HexUtil.bytesToHex(idBytes);
        final Session session = getHibernateSession();
        session.setDefaultReadOnly(false);
        HibernateStoreLob lob = (HibernateStoreLob) session.get(HibernateStoreLob.class, id);
        if (lob == null) {
            lob = new HibernateStoreLob();
            lob.setId(id);
        }

        return lob;
    }

    public void rawExport(CDODataOutput out, int fromBranchID, int toBranchID, long fromCommitTime,
            long toCommitTime) throws IOException {
        // we won't export any store specific stuff...
        // throw new UnsupportedOperationException();
    }

    public void rawImport(CDODataInput in, int fromBranchID, int toBranchID, long fromCommitTime, long toCommitTime,
            OMMonitor monitor) throws IOException {
        throw new UnsupportedOperationException();
    }

    public void rawStore(InternalCDOPackageUnit[] packageUnits, OMMonitor monitor) {
        if (packageUnits != null && packageUnits.length != 0) {
            getStore().getPackageHandler().writePackageUnits(packageUnits);
        }
        // forces a new hibernate session
        commit(monitor);
    }

    public void rawStore(InternalCDORevision revision, OMMonitor monitor) {
        final String entityName = HibernateUtil.getInstance().getEntityName(revision.getID());
        final Serializable idValue = HibernateUtil.getInstance().getIdValue(revision.getID());
        final CDORevision existingRevision = (CDORevision) getHibernateSession().get(entityName, idValue);
        if (existingRevision == null) {
            rawCommitContext.addNewObject(revision);
        } else {
            revision.setVersion(1 + existingRevision.getVersion());
            rawCommitContext.addDirtyObject(revision);
        }
    }

    public void rawStore(byte[] id, long size, InputStream inputStream) throws IOException {
        writeBlob(id, size, inputStream);
    }

    public void rawStore(byte[] id, long size, Reader reader) throws IOException {
        writeClob(id, size, reader);
    }

    public void rawStore(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID, String comment,
            OMMonitor monitor) {
        // TODO: support export and import and auditing
    }

    public void rawCommit(double commitWork, OMMonitor monitor) {
        doWrite(rawCommitContext, monitor);
        commit(monitor);
    }

    public void rawDelete(CDOID id, int version, CDOBranch branch, EClass eClass, OMMonitor monitor) {
        throw new UnsupportedOperationException();
    }

    @Override
    protected void doDeactivate() throws Exception {
        // TODO This method is called when this accessor is not needed anymore
        if (TRACER.isEnabled()) {
            TRACER.trace("Committing/rollback and closing hibernate session"); //$NON-NLS-1$
        }

        try {
            endHibernateSession();
        } finally {
            clearThreadState();
        }
    }

    @Override
    public void release() {
        super.release();
        getStore().ensureCorrectPackageRegistry();
    }

    @Override
    protected void doPassivate() throws Exception {
        clearThreadState();
    }

    private void clearThreadState() {
        PersistableListHolder.getInstance().clearListMapping();
        HibernateThreadContext.setCommitContext(null);
        PackageRegistryProvider.getInstance().setThreadPackageRegistry(null);
    }

    @Override
    protected void doActivate() throws Exception {
        PackageRegistryProvider.getInstance()
                .setThreadPackageRegistry(getStore().getRepository().getPackageRegistry());
    }

    @Override
    protected void doUnpassivate() throws Exception {
    }

    public int getCurrentListChunk() {
        return currentListChunk;
    }
}