| /*************************************************************************** |
| * Copyright (c) 2004 - 2008 Eike Stepper, 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.id.CDOID; |
| import org.eclipse.emf.cdo.common.id.CDOIDTemp; |
| import org.eclipse.emf.cdo.common.id.CDOIDUtil; |
| import org.eclipse.emf.cdo.common.model.CDOClassifierRef; |
| import org.eclipse.emf.cdo.common.model.CDOFeature; |
| import org.eclipse.emf.cdo.common.model.CDOPackage; |
| import org.eclipse.emf.cdo.common.model.CDOPackageInfo; |
| import org.eclipse.emf.cdo.common.model.resource.CDOResourceNodeClass; |
| import org.eclipse.emf.cdo.common.query.CDOQueryInfo; |
| import org.eclipse.emf.cdo.common.revision.CDORevision; |
| import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta; |
| import org.eclipse.emf.cdo.internal.server.StoreAccessor; |
| import org.eclipse.emf.cdo.server.IQueryContext; |
| import org.eclipse.emf.cdo.server.ISession; |
| import org.eclipse.emf.cdo.server.IStoreAccessor; |
| import org.eclipse.emf.cdo.server.ITransaction; |
| import org.eclipse.emf.cdo.server.hibernate.IHibernateStoreAccessor; |
| import org.eclipse.emf.cdo.server.hibernate.id.CDOIDHibernate; |
| import org.eclipse.emf.cdo.server.hibernate.internal.id.CDOIDHibernateFactoryImpl; |
| 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.spi.common.revision.InternalCDORevision; |
| |
| import org.eclipse.net4j.util.ObjectUtil; |
| import org.eclipse.net4j.util.WrappedException; |
| import org.eclipse.net4j.util.collection.CloseableIterator; |
| import org.eclipse.net4j.util.om.monitor.OMMonitor; |
| import org.eclipse.net4j.util.om.trace.ContextTracer; |
| |
| import org.hibernate.Criteria; |
| import org.hibernate.FlushMode; |
| import org.hibernate.Query; |
| import org.hibernate.Session; |
| import org.hibernate.SessionFactory; |
| import org.hibernate.criterion.Expression; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.List; |
| |
| /** |
| * @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 Session hibernateSession; |
| |
| private boolean errorOccured = false; |
| |
| public HibernateStoreAccessor(HibernateStore store, ISession session) |
| { |
| super(store, session); |
| HibernateThreadContext.setCurrentHibernateStoreAccessor(this); |
| if (TRACER.isEnabled()) |
| { |
| TRACER.trace("Created " + this.getClass().getName() + " for repository " + store.getRepository().getName()); |
| } |
| } |
| |
| public HibernateStoreAccessor(HibernateStore store, ITransaction transaction) |
| { |
| super(store, transaction); |
| HibernateThreadContext.setCurrentHibernateStoreAccessor(this); |
| if (TRACER.isEnabled()) |
| { |
| TRACER.trace("Created " + this.getClass().getName() + " for repository " + store.getRepository().getName()); |
| } |
| } |
| |
| /** Clears the current hibernate session and sets a new one in the thread context */ |
| public void resetHibernateSession() |
| { |
| endHibernateSession(); |
| beginHibernateSession(); |
| } |
| |
| @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"); |
| } |
| |
| assert hibernateSession == null; |
| final SessionFactory sessionFactory = getStore().getHibernateSessionFactory(); |
| hibernateSession = sessionFactory.openSession(); |
| hibernateSession.beginTransaction(); |
| } |
| |
| /** |
| * Commits the session |
| * |
| * @since 2.0 |
| */ |
| public void commitRollbackHibernateSession() |
| { |
| if (TRACER.isEnabled()) |
| { |
| TRACER.trace("Commiting hibernate session"); |
| } |
| |
| if (isErrorOccured()) |
| { |
| if (TRACER.isEnabled()) |
| { |
| TRACER.trace("Rolling back hb transaction"); |
| } |
| |
| hibernateSession.getTransaction().rollback(); |
| } |
| else |
| { |
| if (TRACER.isEnabled()) |
| { |
| TRACER.trace("Committing hb transaction"); |
| } |
| |
| hibernateSession.getTransaction().commit(); |
| } |
| } |
| |
| /** |
| * commits/rollbacks and closes the session |
| * |
| * @since 2.0 |
| */ |
| public void endHibernateSession() |
| { |
| if (TRACER.isEnabled()) |
| { |
| TRACER.trace("Closing hibernate session"); |
| } |
| |
| if (hibernateSession != null && hibernateSession.isOpen()) |
| { |
| try |
| { |
| if (hibernateSession.getTransaction().isActive()) |
| { |
| commitRollbackHibernateSession(); |
| } |
| } |
| finally |
| { |
| hibernateSession.close(); |
| } |
| } |
| |
| hibernateSession = null; |
| } |
| |
| public Session getHibernateSession() |
| { |
| if (hibernateSession == null) |
| { |
| beginHibernateSession(); |
| } |
| |
| return hibernateSession; |
| } |
| |
| /** |
| * @since 2.0 |
| */ |
| public boolean isErrorOccured() |
| { |
| return errorOccured; |
| } |
| |
| /** |
| * @since 2.0 |
| */ |
| public void setErrorOccured(boolean errorOccured) |
| { |
| this.errorOccured = errorOccured; |
| } |
| |
| public HibernateStoreChunkReader createChunkReader(CDORevision revision, CDOFeature feature) |
| { |
| return new HibernateStoreChunkReader(this, revision, feature); |
| } |
| |
| public CloseableIterator<Object> createQueryIterator(CDOQueryInfo queryInfo) |
| { |
| // TODO: implement HibernateStoreAccessor.createQueryIterator(queryInfo) |
| throw new UnsupportedOperationException(); |
| } |
| |
| public CloseableIterator<CDOID> readObjectIDs() |
| { |
| throw new UnsupportedOperationException(); |
| } |
| |
| public CDOClassifierRef readObjectType(CDOID id) |
| { |
| CDORevision cdoRevision = readRevision(id, -1); |
| return cdoRevision.getCDOClass().createClassRef(); |
| } |
| |
| public void readPackage(CDOPackage cdoPackage) |
| { |
| getStore().getPackageHandler().readPackage(cdoPackage); |
| } |
| |
| public void readPackageEcore(CDOPackage cdoPackage) |
| { |
| throw new UnsupportedOperationException(); |
| } |
| |
| public Collection<CDOPackageInfo> readPackageInfos() |
| { |
| return getStore().getPackageHandler().getCDOPackageInfos(); |
| } |
| |
| public CDORevision readRevision(CDOID id, int referenceChunk) |
| { |
| return HibernateUtil.getInstance().getCDORevision(id); |
| } |
| |
| public CDORevision readRevisionByTime(CDOID id, int referenceChunk, long timeStamp) |
| { |
| throw new UnsupportedOperationException(); |
| } |
| |
| public CDORevision readRevisionByVersion(CDOID id, int referenceChunk, int version) |
| { |
| // TODO Could be necessary to implement |
| throw new UnsupportedOperationException(); |
| } |
| |
| /** |
| * TODO Clarify the meaning of {@link IStoreAccessor#refreshRevisions()} |
| * |
| * @since 2.0 |
| */ |
| public void refreshRevisions() |
| { |
| // Do nothing |
| } |
| |
| /** |
| * @since 2.0 |
| */ |
| public void queryResources(QueryResourcesContext context) |
| { |
| CDOIDHibernate folderID = getHibernateID(context.getFolderID()); |
| String name = context.getName(); |
| boolean exactMatch = context.exactMatch(); |
| |
| final Session session = getHibernateSession(); |
| final Criteria criteria = session.createCriteria(CDOResourceNodeClass.NAME); |
| if (folderID == null) |
| { |
| criteria.add(Expression.isNull("containerID")); |
| } |
| else |
| { |
| criteria.add(Expression.eq("containerID", folderID)); |
| } |
| |
| List<?> result = criteria.list(); |
| for (Object o : result) |
| { |
| final CDORevision revision = (CDORevision)o; |
| String revisionName = (String)revision.data().get(getResourceNameFeature(), 0); |
| boolean match = exactMatch || revisionName == null || name == null ? ObjectUtil.equals(revisionName, name) |
| : revisionName.startsWith(name); |
| |
| if (match && !context.addResource(revision.getID())) |
| { |
| // No more results allowed |
| break; |
| } |
| } |
| } |
| |
| private CDOIDHibernate getHibernateID(CDOID id) |
| { |
| if (!CDOIDUtil.isNull(id)) |
| { |
| if (id instanceof CDOIDHibernate) |
| { |
| return (CDOIDHibernate)id; |
| } |
| |
| // TODO Can this happen? When? |
| final long longID = CDOIDUtil.getLong(id); |
| return CDOIDHibernateFactoryImpl.getInstance().createCDOID(longID, CDOResourceNodeClass.NAME); |
| } |
| |
| return null; |
| } |
| |
| private CDOFeature getResourceNameFeature() |
| { |
| return getResourceNodeClass().getCDONameFeature(); |
| } |
| |
| private CDOResourceNodeClass getResourceNodeClass() |
| { |
| return getStore().getRepository().getPackageManager().getCDOResourcePackage().getCDOResourceNodeClass(); |
| } |
| |
| /** |
| * @since 2.0 |
| */ |
| public void executeQuery(CDOQueryInfo info, IQueryContext context) |
| { |
| // TODO: implement HibernateStoreAccessor.executeQuery(info, context) |
| throw new UnsupportedOperationException(); |
| } |
| |
| /** |
| * Is handled through {@link #endHibernateSession()}. |
| */ |
| public void commit(OMMonitor monitor) |
| { |
| commitRollbackHibernateSession(); |
| HibernateThreadContext.setCommitContext(null); |
| } |
| |
| @Override |
| public void write(CommitContext context, OMMonitor monitor) |
| { |
| List<InternalCDORevision> adjustRevisions = new ArrayList<InternalCDORevision>(); |
| HibernateThreadContext.setCommitContext(context); |
| if (context.getNewPackages().length > 0) |
| { |
| writePackages(context.getNewPackages(), monitor); |
| } |
| try |
| { |
| // start with fresh hibernate session |
| final Session session = getHibernateSession(); |
| session.setFlushMode(FlushMode.MANUAL); |
| |
| // first repair the version for all dirty objects |
| for (CDORevision cdoRevision : context.getDirtyObjects()) |
| { |
| if (cdoRevision instanceof InternalCDORevision) |
| { |
| InternalCDORevision internalCDORevision = (InternalCDORevision)cdoRevision; |
| internalCDORevision.setVersion(cdoRevision.getVersion() - 1); |
| adjustRevisions.add(internalCDORevision); |
| } |
| } |
| |
| // delete all objects |
| for (CDOID cdoID : context.getDetachedObjects()) |
| { |
| final CDORevision revision = HibernateUtil.getInstance().getCDORevision(cdoID); |
| session.delete(revision); |
| } |
| |
| final List<CDORevision> cdoRevisions = Arrays.asList(context.getNewObjects()); |
| |
| // keep track for which cdoRevisions the container id needs to be repaired afterwards |
| final List<InternalCDORevision> repairContainerIDs = new ArrayList<InternalCDORevision>(); |
| |
| // first save the non-cdoresources |
| for (CDORevision cdoRevision : cdoRevisions) |
| { |
| if (cdoRevision instanceof InternalCDORevision) |
| { |
| final CDOID containerID = (CDOID)((InternalCDORevision)cdoRevision).getContainerID(); |
| if (containerID instanceof CDOIDTemp && !containerID.isNull()) |
| { |
| repairContainerIDs.add((InternalCDORevision)cdoRevision); |
| } |
| } |
| |
| session.save(HibernateUtil.getInstance().getEntityName(cdoRevision), cdoRevision); |
| if (TRACER.isEnabled()) |
| { |
| TRACER.trace("Persisted new Object " + cdoRevision.getCDOClass().getName() + " id: " + cdoRevision.getID()); |
| } |
| } |
| |
| for (CDORevision cdoRevision : context.getDirtyObjects()) |
| { |
| session.merge(HibernateUtil.getInstance().getEntityName(cdoRevision), cdoRevision); |
| if (TRACER.isEnabled()) |
| { |
| TRACER.trace("Updated Object " + cdoRevision.getCDOClass().getName() + " id: " + cdoRevision.getID()); |
| } |
| } |
| |
| session.flush(); |
| |
| // now do an update of the container without incrementing the version |
| for (InternalCDORevision cdoRevision : repairContainerIDs) |
| { |
| final CDORevision container = HibernateUtil.getInstance().getCDORevision((CDOID)cdoRevision.getContainerID()); |
| final String entityName = HibernateUtil.getInstance().getEntityName(cdoRevision); |
| final CDOIDHibernate id = (CDOIDHibernate)cdoRevision.getID(); |
| final CDOIDHibernate containerID = (CDOIDHibernate)container.getID(); |
| final String hqlUpdate = "update " + entityName |
| + " set contID_Entity = :contEntity, contID_ID=:contID, contID_class=:contClass where e_id = :id"; |
| final Query qry = session.createQuery(hqlUpdate); |
| qry.setParameter("contEntity", containerID.getEntityName()); |
| qry.setParameter("contID", containerID.getId().toString()); |
| qry.setParameter("contClass", containerID.getId().getClass().getName()); |
| qry.setParameter("id", id.getId()); |
| if (qry.executeUpdate() != 1) |
| { |
| throw new IllegalStateException("Not able to update container columns of " + entityName + " with id " + id); |
| } |
| } |
| |
| session.flush(); |
| |
| } |
| catch (Exception e) |
| { |
| OM.LOG.error(e); |
| throw WrappedException.wrap(e); |
| } |
| finally |
| { |
| for (InternalCDORevision cdoRevision : adjustRevisions) |
| { |
| cdoRevision.setVersion(cdoRevision.getVersion() + 1); |
| } |
| } |
| |
| context.applyIDMappings(monitor); |
| } |
| |
| @Override |
| protected void detachObjects(CDOID[] detachedObjects, long revised, OMMonitor monitor) |
| { |
| // TODO: implement HibernateStoreAccessor.detachObjects(detachedObjects) |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| protected void rollback(CommitContext context) |
| { |
| setErrorOccured(true); |
| endHibernateSession(); |
| HibernateThreadContext.setCommitContext(null); |
| } |
| |
| @Override |
| protected void writePackages(CDOPackage[] cdoPackages, OMMonitor monitor) |
| { |
| if (cdoPackages != null && cdoPackages.length != 0) |
| { |
| getStore().getPackageHandler().writePackages(cdoPackages); |
| } |
| |
| // Set a new hibernatesession in the thread |
| resetHibernateSession(); |
| } |
| |
| @Override |
| protected void writeRevisions(CDORevision[] revisions, OMMonitor monitor) |
| { |
| // Don't do anything it is done at commit |
| } |
| |
| @Override |
| protected void writeRevisionDeltas(CDORevisionDelta[] revisionDeltas, long created, OMMonitor monitor) |
| { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| protected void doActivate() throws Exception |
| { |
| } |
| |
| @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"); |
| } |
| |
| try |
| { |
| endHibernateSession(); |
| PersistableListHolder.getInstance().clearListMapping(); |
| } |
| finally |
| { |
| HibernateThreadContext.setCurrentHibernateStoreAccessor(this); |
| } |
| } |
| |
| @Override |
| protected void doPassivate() throws Exception |
| { |
| // TODO This method is called right before this accessor is added to a pool |
| } |
| |
| @Override |
| protected void doUnpassivate() throws Exception |
| { |
| // TODO This method is called right after this accessor is removed from a pool |
| } |
| } |