| /* |
| * 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: |
| * Eike Stepper - initial API and implementation |
| * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444 |
| * Stefan Winkler - 249610: [DB] Support external references (Implementation) |
| * Stefan Winkler - Bug 329025: [DB] Support branching for range-based mapping strategy |
| */ |
| package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal; |
| |
| import org.eclipse.emf.cdo.common.branch.CDOBranch; |
| import org.eclipse.emf.cdo.common.branch.CDOBranchManager; |
| import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; |
| import org.eclipse.emf.cdo.common.branch.CDOBranchVersion; |
| import org.eclipse.emf.cdo.common.id.CDOID; |
| import org.eclipse.emf.cdo.common.model.CDOModelUtil; |
| import org.eclipse.emf.cdo.common.revision.CDOList; |
| import org.eclipse.emf.cdo.common.revision.CDORevision; |
| import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; |
| import org.eclipse.emf.cdo.common.revision.CDORevisionManager; |
| import org.eclipse.emf.cdo.eresource.EresourcePackage; |
| import org.eclipse.emf.cdo.server.IRepository; |
| import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext; |
| import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; |
| import org.eclipse.emf.cdo.server.db.IIDHandler; |
| import org.eclipse.emf.cdo.server.db.mapping.IClassMapping; |
| import org.eclipse.emf.cdo.server.db.mapping.IListMapping; |
| import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; |
| import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping; |
| import org.eclipse.emf.cdo.server.internal.db.bundle.OM; |
| import org.eclipse.emf.cdo.spi.common.commit.CDOChangeSetSegment; |
| import org.eclipse.emf.cdo.spi.common.revision.InternalCDOList; |
| import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; |
| |
| import org.eclipse.net4j.db.DBException; |
| import org.eclipse.net4j.db.DBType; |
| import org.eclipse.net4j.db.DBUtil; |
| import org.eclipse.net4j.db.IDBDatabase; |
| import org.eclipse.net4j.db.IDBPreparedStatement; |
| import org.eclipse.net4j.db.IDBPreparedStatement.ReuseProbability; |
| import org.eclipse.net4j.db.IDBSchemaTransaction; |
| import org.eclipse.net4j.db.ddl.IDBField; |
| import org.eclipse.net4j.db.ddl.IDBIndex; |
| import org.eclipse.net4j.db.ddl.IDBSchema; |
| import org.eclipse.net4j.db.ddl.IDBTable; |
| 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.EClass; |
| import org.eclipse.emf.ecore.EReference; |
| import org.eclipse.emf.ecore.EStructuralFeature; |
| import org.eclipse.emf.ecore.util.FeatureMapUtil; |
| |
| import org.eclipse.core.runtime.Assert; |
| |
| import java.sql.PreparedStatement; |
| import java.sql.ResultSet; |
| import java.sql.SQLException; |
| import java.text.MessageFormat; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.LinkedHashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| /** |
| * @author Eike Stepper |
| * @since 2.0 |
| */ |
| public abstract class AbstractHorizontalClassMapping implements IClassMapping, IMappingConstants |
| { |
| private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, AbstractHorizontalClassMapping.class); |
| |
| private EClass eClass; |
| |
| private IDBTable table; |
| |
| private AbstractHorizontalMappingStrategy mappingStrategy; |
| |
| private List<ITypeMapping> valueMappings; |
| |
| private List<IListMapping> listMappings; |
| |
| private Map<EStructuralFeature, IDBField> listSizeFields; |
| |
| private Map<EStructuralFeature, IDBField> unsettableFields; |
| |
| private String sqlSelectForHandle; |
| |
| private String sqlSelectForChangeSet; |
| |
| public AbstractHorizontalClassMapping(AbstractHorizontalMappingStrategy mappingStrategy, EClass eClass) |
| { |
| this.mappingStrategy = mappingStrategy; |
| this.eClass = eClass; |
| |
| initTable(); |
| initFields(); |
| initSQLStrings(); |
| } |
| |
| private void initTable() |
| { |
| final String tableName = mappingStrategy.getTableName(eClass); |
| final DBType idType = mappingStrategy.getStore().getIDHandler().getDBType(); |
| final int idLength = mappingStrategy.getStore().getIDColumnLength(); |
| |
| IDBDatabase database = mappingStrategy.getStore().getDatabase(); |
| table = database.getSchema().getTable(tableName); |
| if (table == null) |
| { |
| IDBSchemaTransaction schemaTransaction = database.getSchemaTransaction(); |
| IDBSchema workingCopy = schemaTransaction.getWorkingCopy(); |
| |
| table = workingCopy.addTable(tableName); |
| table.addField(ATTRIBUTES_ID, idType, idLength, true); |
| table.addField(ATTRIBUTES_VERSION, DBType.INTEGER, true); |
| |
| IDBField branchField = addBranchField(table); |
| |
| table.addField(ATTRIBUTES_CREATED, DBType.BIGINT, true); |
| table.addField(ATTRIBUTES_REVISED, DBType.BIGINT, true); |
| table.addField(ATTRIBUTES_RESOURCE, idType, idLength, true); |
| table.addField(ATTRIBUTES_CONTAINER, idType, idLength, true); |
| table.addField(ATTRIBUTES_FEATURE, DBType.INTEGER, true); |
| |
| IDBIndex primaryKey = table.addIndex(IDBIndex.Type.PRIMARY_KEY, ATTRIBUTES_ID, ATTRIBUTES_VERSION); |
| if (branchField != null) |
| { |
| primaryKey.addIndexField(branchField); |
| } |
| |
| table.addIndex(IDBIndex.Type.NON_UNIQUE, ATTRIBUTES_ID, ATTRIBUTES_REVISED); |
| } |
| } |
| |
| private void initFields() |
| { |
| final EStructuralFeature[] allPersistentFeatures = CDOModelUtil.getClassInfo(eClass).getAllPersistentFeatures(); |
| if (allPersistentFeatures == null) |
| { |
| valueMappings = Collections.emptyList(); |
| listMappings = Collections.emptyList(); |
| } |
| else |
| { |
| valueMappings = new ArrayList<ITypeMapping>(); |
| listMappings = new ArrayList<IListMapping>(); |
| |
| boolean hasUnsettableFeatures = false; |
| for (EStructuralFeature feature : allPersistentFeatures) |
| { |
| String fieldName = mappingStrategy.getFieldName(feature); |
| if (feature.isMany()) |
| { |
| IListMapping mapping = null; |
| if (FeatureMapUtil.isFeatureMap(feature)) |
| { |
| mapping = mappingStrategy.createFeatureMapMapping(eClass, feature); |
| } |
| else |
| { |
| mapping = mappingStrategy.createListMapping(eClass, feature); |
| } |
| |
| listMappings.add(mapping); |
| |
| // Add field for list sizes |
| if (listSizeFields == null) |
| { |
| listSizeFields = new LinkedHashMap<EStructuralFeature, IDBField>(); |
| } |
| |
| IDBField field = table.getField(fieldName); |
| if (field == null) |
| { |
| field = table.addField(fieldName, DBType.INTEGER); |
| } |
| |
| listSizeFields.put(feature, field); |
| } |
| else |
| { |
| ITypeMapping mapping = mappingStrategy.createValueMapping(feature); |
| IDBField field = table.getField(fieldName); |
| if (field == null) |
| { |
| mapping.createDBField(table, fieldName); |
| } |
| else |
| { |
| mapping.setDBField(table, fieldName); |
| } |
| |
| valueMappings.add(mapping); |
| |
| if (feature.isUnsettable()) |
| { |
| hasUnsettableFeatures = true; |
| } |
| } |
| } |
| |
| // Add unsettable fields to end of table |
| if (hasUnsettableFeatures) |
| { |
| unsettableFields = new LinkedHashMap<EStructuralFeature, IDBField>(); |
| for (EStructuralFeature feature : allPersistentFeatures) |
| { |
| if (!feature.isMany() && feature.isUnsettable()) |
| { |
| String fieldName = mappingStrategy.getUnsettableFieldName(feature); |
| IDBField field = table.getField(fieldName); |
| if (field == null) |
| { |
| field = table.addField(fieldName, DBType.BOOLEAN); |
| } |
| |
| unsettableFields.put(feature, field); |
| } |
| } |
| } |
| } |
| } |
| |
| private void initSQLStrings() |
| { |
| // ----------- Select all revisions (for handleRevisions) --- |
| StringBuilder builder = new StringBuilder("SELECT "); //$NON-NLS-1$ |
| builder.append(ATTRIBUTES_ID); |
| builder.append(", "); //$NON-NLS-1$ |
| builder.append(ATTRIBUTES_VERSION); |
| builder.append(" FROM "); //$NON-NLS-1$ |
| builder.append(table); |
| sqlSelectForHandle = builder.toString(); |
| |
| // ----------- Select all revisions (for readChangeSet) --- |
| builder = new StringBuilder("SELECT DISTINCT "); //$NON-NLS-1$ |
| builder.append(ATTRIBUTES_ID); |
| builder.append(" FROM "); //$NON-NLS-1$ |
| builder.append(table); |
| builder.append(" WHERE "); //$NON-NLS-1$ |
| sqlSelectForChangeSet = builder.toString(); |
| } |
| |
| protected IDBField addBranchField(IDBTable table) |
| { |
| return null; |
| } |
| |
| /** |
| * Read the revision's values from the DB. |
| * |
| * @return <code>true</code> if the revision has been read successfully.<br> |
| * <code>false</code> if the revision does not exist in the DB. |
| */ |
| protected final boolean readValuesFromStatement(PreparedStatement stmt, InternalCDORevision revision, |
| IDBStoreAccessor accessor) |
| { |
| ResultSet resultSet = null; |
| |
| try |
| { |
| if (TRACER.isEnabled()) |
| { |
| TRACER.format("Executing Query: {0}", stmt.toString()); //$NON-NLS-1$ |
| } |
| |
| stmt.setMaxRows(1); // Optimization: only 1 row |
| |
| resultSet = stmt.executeQuery(); |
| if (!resultSet.next()) |
| { |
| if (TRACER.isEnabled()) |
| { |
| TRACER.format("Resultset was empty"); //$NON-NLS-1$ |
| } |
| |
| return false; |
| } |
| |
| revision.setVersion(resultSet.getInt(ATTRIBUTES_VERSION)); |
| |
| long timeStamp = resultSet.getLong(ATTRIBUTES_CREATED); |
| |
| IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); |
| CDOBranchPoint branchPoint = revision.getBranch().getPoint(timeStamp); |
| |
| revision.setBranchPoint(branchPoint); |
| revision.setRevised(resultSet.getLong(ATTRIBUTES_REVISED)); |
| revision.setResourceID(idHandler.getCDOID(resultSet, ATTRIBUTES_RESOURCE)); |
| revision.setContainerID(idHandler.getCDOID(resultSet, ATTRIBUTES_CONTAINER)); |
| revision.setContainingFeatureID(resultSet.getInt(ATTRIBUTES_FEATURE)); |
| |
| for (ITypeMapping mapping : valueMappings) |
| { |
| EStructuralFeature feature = mapping.getFeature(); |
| if (feature.isUnsettable()) |
| { |
| IDBField field = unsettableFields.get(feature); |
| if (!resultSet.getBoolean(field.getName())) |
| { |
| // isSet==false -- setValue: null |
| revision.setValue(feature, null); |
| continue; |
| } |
| } |
| |
| mapping.readValueToRevision(resultSet, revision); |
| } |
| |
| if (listSizeFields != null) |
| { |
| for (Map.Entry<EStructuralFeature, IDBField> listSizeEntry : listSizeFields.entrySet()) |
| { |
| EStructuralFeature feature = listSizeEntry.getKey(); |
| IDBField field = listSizeEntry.getValue(); |
| int size = resultSet.getInt(field.getName()); |
| |
| // ensure the listSize (TODO: remove assertion) |
| CDOList list = revision.getList(feature, size); |
| |
| for (int i = 0; i < size; i++) |
| { |
| list.add(InternalCDOList.UNINITIALIZED); |
| } |
| |
| if (list.size() != size) |
| { |
| Assert.isTrue(false); |
| } |
| } |
| } |
| |
| return true; |
| } |
| catch (SQLException ex) |
| { |
| throw new DBException(ex); |
| } |
| finally |
| { |
| DBUtil.close(resultSet); |
| } |
| } |
| |
| protected final void readLists(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk) |
| { |
| for (IListMapping listMapping : listMappings) |
| { |
| listMapping.readValues(accessor, revision, listChunk); |
| } |
| } |
| |
| protected final IMappingStrategy getMappingStrategy() |
| { |
| return mappingStrategy; |
| } |
| |
| public final EClass getEClass() |
| { |
| return eClass; |
| } |
| |
| protected final Map<EStructuralFeature, IDBField> getUnsettableFields() |
| { |
| return unsettableFields; |
| } |
| |
| protected final Map<EStructuralFeature, IDBField> getListSizeFields() |
| { |
| return listSizeFields; |
| } |
| |
| public final List<ITypeMapping> getValueMappings() |
| { |
| return valueMappings; |
| } |
| |
| public final ITypeMapping getValueMapping(EStructuralFeature feature) |
| { |
| for (ITypeMapping mapping : valueMappings) |
| { |
| if (mapping.getFeature() == feature) |
| { |
| return mapping; |
| } |
| } |
| |
| return null; |
| } |
| |
| public final List<IListMapping> getListMappings() |
| { |
| return listMappings; |
| } |
| |
| public final IListMapping getListMapping(EStructuralFeature feature) |
| { |
| for (IListMapping mapping : listMappings) |
| { |
| if (mapping.getFeature() == feature) |
| { |
| return mapping; |
| } |
| } |
| |
| throw new IllegalArgumentException("List mapping for feature " + feature + " does not exist"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| |
| protected final IDBTable getTable() |
| { |
| return table; |
| } |
| |
| public List<IDBTable> getDBTables() |
| { |
| List<IDBTable> tables = new ArrayList<IDBTable>(); |
| tables.add(table); |
| |
| for (IListMapping listMapping : listMappings) |
| { |
| tables.addAll(listMapping.getDBTables()); |
| } |
| |
| return tables; |
| } |
| |
| @Override |
| public String toString() |
| { |
| return MessageFormat.format("{0}[{1} -> {2}]", getClass().getSimpleName(), eClass, table); |
| } |
| |
| protected void checkDuplicateResources(IDBStoreAccessor accessor, CDORevision revision) throws IllegalStateException |
| { |
| CDOID folderID = (CDOID)revision.data().getContainerID(); |
| String name = (String)revision.data().get(EresourcePackage.eINSTANCE.getCDOResourceNode_Name(), 0); |
| |
| CDOID existingID = accessor.readResourceID(folderID, name, revision.getBranch().getHead()); |
| if (existingID != null && !existingID.equals(revision.getID())) |
| { |
| throw new IllegalStateException("Duplicate resource node in folder " + folderID + ": " + name); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| } |
| |
| protected void writeLists(IDBStoreAccessor accessor, InternalCDORevision revision) |
| { |
| for (IListMapping listMapping : listMappings) |
| { |
| listMapping.writeValues(accessor, revision); |
| } |
| } |
| |
| public void writeRevision(IDBStoreAccessor accessor, InternalCDORevision revision, boolean mapType, boolean revise, |
| OMMonitor monitor) |
| { |
| Async async = null; |
| monitor.begin(10); |
| |
| try |
| { |
| try |
| { |
| async = monitor.forkAsync(); |
| CDOID id = revision.getID(); |
| if (mapType) |
| { |
| long timeStamp = revision.getTimeStamp(); |
| mappingStrategy.putObjectType(accessor, timeStamp, id, eClass); |
| } |
| else if (revise) |
| { |
| long revised = revision.getTimeStamp() - 1; |
| reviseOldRevision(accessor, id, revision.getBranch(), revised); |
| for (IListMapping mapping : getListMappings()) |
| { |
| mapping.objectDetached(accessor, id, revised); |
| } |
| } |
| } |
| finally |
| { |
| if (async != null) |
| { |
| async.stop(); |
| } |
| } |
| |
| try |
| { |
| async = monitor.forkAsync(); |
| if (revision.isResourceNode()) |
| { |
| checkDuplicateResources(accessor, revision); |
| } |
| } |
| finally |
| { |
| if (async != null) |
| { |
| async.stop(); |
| } |
| } |
| |
| try |
| { |
| // Write attribute table always (even without modeled attributes!) |
| async = monitor.forkAsync(); |
| writeValues(accessor, revision); |
| } |
| finally |
| { |
| if (async != null) |
| { |
| async.stop(); |
| } |
| } |
| |
| try |
| { |
| // Write list tables only if they exist |
| if (listMappings != null) |
| { |
| async = monitor.forkAsync(7); |
| writeLists(accessor, revision); |
| } |
| else |
| { |
| monitor.worked(7); |
| } |
| } |
| finally |
| { |
| if (async != null) |
| { |
| async.stop(); |
| } |
| } |
| } |
| finally |
| { |
| monitor.done(); |
| } |
| } |
| |
| public void handleRevisions(IDBStoreAccessor accessor, CDOBranch branch, long timeStamp, boolean exactTime, |
| CDORevisionHandler handler) |
| { |
| // branch parameter is ignored, because either it is null or main branch. |
| // this does not make any difference for non-branching store. |
| // see #handleRevisions() implementation in HorizontalBranchingClassMapping |
| // for branch handling. |
| |
| IRepository repository = accessor.getStore().getRepository(); |
| CDORevisionManager revisionManager = repository.getRevisionManager(); |
| CDOBranchManager branchManager = repository.getBranchManager(); |
| |
| // TODO: test for timeStamp == INVALID_TIME and encode revision.isValid() as WHERE instead of fetching all revisions |
| // in order to increase performance |
| |
| StringBuilder builder = new StringBuilder(sqlSelectForHandle); |
| |
| int timeParameters = 0; |
| if (timeStamp != CDOBranchPoint.INVALID_DATE) |
| { |
| if (exactTime) |
| { |
| if (timeStamp != CDOBranchPoint.UNSPECIFIED_DATE) |
| { |
| builder.append(" WHERE "); //$NON-NLS-1$ |
| builder.append(ATTRIBUTES_CREATED); |
| builder.append("=?"); //$NON-NLS-1$ |
| timeParameters = 1; |
| } |
| } |
| else |
| { |
| builder.append(" WHERE "); //$NON-NLS-1$ |
| if (timeStamp != CDOBranchPoint.UNSPECIFIED_DATE) |
| { |
| builder.append(ATTRIBUTES_CREATED); |
| builder.append(">=?"); //$NON-NLS-1$ |
| builder.append(" AND ("); //$NON-NLS-1$ |
| builder.append(ATTRIBUTES_REVISED); |
| builder.append("<=? OR "); //$NON-NLS-1$ |
| builder.append(ATTRIBUTES_REVISED); |
| builder.append("="); //$NON-NLS-1$ |
| builder.append(CDOBranchPoint.UNSPECIFIED_DATE); |
| builder.append(")"); //$NON-NLS-1$ |
| timeParameters = 2; |
| } |
| else |
| { |
| builder.append(ATTRIBUTES_REVISED); |
| builder.append("="); //$NON-NLS-1$ |
| builder.append(CDOBranchPoint.UNSPECIFIED_DATE); |
| } |
| } |
| } |
| |
| IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); |
| IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(builder.toString(), ReuseProbability.LOW); |
| ResultSet resultSet = null; |
| |
| try |
| { |
| for (int i = 0; i < timeParameters; i++) |
| { |
| stmt.setLong(i + 1, timeStamp); |
| } |
| |
| resultSet = stmt.executeQuery(); |
| while (resultSet.next()) |
| { |
| CDOID id = idHandler.getCDOID(resultSet, 1); |
| int version = resultSet.getInt(2); |
| |
| if (version >= CDOBranchVersion.FIRST_VERSION) |
| { |
| InternalCDORevision revision = (InternalCDORevision)revisionManager.getRevisionByVersion(id, |
| branchManager.getMainBranch().getVersion(version), CDORevision.UNCHUNKED, true); |
| |
| if (!handler.handleRevision(revision)) |
| { |
| break; |
| } |
| } |
| } |
| } |
| catch (SQLException e) |
| { |
| throw new DBException(e); |
| } |
| finally |
| { |
| DBUtil.close(resultSet); |
| DBUtil.close(stmt); |
| } |
| } |
| |
| public Set<CDOID> readChangeSet(IDBStoreAccessor accessor, CDOChangeSetSegment[] segments) |
| { |
| StringBuilder builder = new StringBuilder(sqlSelectForChangeSet); |
| boolean isFirst = true; |
| |
| for (int i = 0; i < segments.length; i++) |
| { |
| if (isFirst) |
| { |
| isFirst = false; |
| } |
| else |
| { |
| builder.append(" OR "); //$NON-NLS-1$ |
| } |
| |
| builder.append(ATTRIBUTES_CREATED); |
| builder.append(">=?"); //$NON-NLS-1$ |
| builder.append(" AND ("); //$NON-NLS-1$ |
| builder.append(ATTRIBUTES_REVISED); |
| builder.append("<=? OR "); //$NON-NLS-1$ |
| builder.append(ATTRIBUTES_REVISED); |
| builder.append("="); //$NON-NLS-1$ |
| builder.append(CDOBranchPoint.UNSPECIFIED_DATE); |
| builder.append(")"); //$NON-NLS-1$ |
| } |
| |
| IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); |
| IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(builder.toString(), ReuseProbability.LOW); |
| ResultSet resultSet = null; |
| |
| Set<CDOID> result = new HashSet<CDOID>(); |
| |
| try |
| { |
| int column = 1; |
| for (CDOChangeSetSegment segment : segments) |
| { |
| stmt.setLong(column++, segment.getTimeStamp()); |
| stmt.setLong(column++, segment.getEndTime()); |
| } |
| |
| resultSet = stmt.executeQuery(); |
| while (resultSet.next()) |
| { |
| CDOID id = idHandler.getCDOID(resultSet, 1); |
| result.add(id); |
| } |
| |
| return result; |
| } |
| catch (SQLException e) |
| { |
| throw new DBException(e); |
| } |
| finally |
| { |
| DBUtil.close(resultSet); |
| DBUtil.close(stmt); |
| } |
| } |
| |
| public void detachObject(IDBStoreAccessor accessor, CDOID id, int version, CDOBranch branch, long timeStamp, |
| OMMonitor monitor) |
| { |
| Async async = null; |
| monitor.begin(1 + listMappings.size()); |
| |
| try |
| { |
| if (version >= CDOBranchVersion.FIRST_VERSION) |
| { |
| reviseOldRevision(accessor, id, branch, timeStamp - 1); |
| } |
| |
| detachAttributes(accessor, id, version, branch, timeStamp, monitor.fork()); |
| |
| // notify list mappings so they can clean up |
| for (IListMapping mapping : getListMappings()) |
| { |
| try |
| { |
| async = monitor.forkAsync(); |
| mapping.objectDetached(accessor, id, timeStamp); |
| } |
| finally |
| { |
| if (async != null) |
| { |
| async.stop(); |
| } |
| } |
| } |
| } |
| finally |
| { |
| monitor.done(); |
| } |
| } |
| |
| public void rawDelete(IDBStoreAccessor accessor, CDOID id, int version, CDOBranch branch, OMMonitor monitor) |
| { |
| Async async = null; |
| monitor.begin(1 + listMappings.size()); |
| |
| try |
| { |
| rawDeleteAttributes(accessor, id, branch, version, monitor.fork()); |
| |
| // notify list mappings so they can clean up |
| for (IListMapping mapping : getListMappings()) |
| { |
| if (mapping instanceof AbstractBasicListTableMapping) |
| { |
| try |
| { |
| async = monitor.forkAsync(); |
| |
| AbstractBasicListTableMapping m = (AbstractBasicListTableMapping)mapping; |
| m.rawDeleted(accessor, id, branch, version); |
| } |
| finally |
| { |
| if (async != null) |
| { |
| async.stop(); |
| } |
| } |
| } |
| else |
| { |
| throw new UnsupportedOperationException("rawDeleted() is not supported by " + mapping.getClass().getName()); |
| } |
| } |
| } |
| finally |
| { |
| monitor.done(); |
| } |
| } |
| |
| protected abstract void rawDeleteAttributes(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, int version, |
| OMMonitor fork); |
| |
| public final boolean queryXRefs(IDBStoreAccessor accessor, QueryXRefsContext context, String idString) |
| { |
| String tableName = table.getName(); |
| List<EReference> refs = context.getSourceCandidates().get(eClass); |
| List<EReference> scalarRefs = new ArrayList<EReference>(); |
| |
| for (EReference ref : refs) |
| { |
| if (ref.isMany()) |
| { |
| IListMapping listMapping = getListMapping(ref); |
| String where = getListXRefsWhere(context); |
| |
| boolean more = listMapping.queryXRefs(accessor, tableName, where, context, idString); |
| if (!more) |
| { |
| return false; |
| } |
| } |
| else |
| { |
| scalarRefs.add(ref); |
| } |
| } |
| |
| if (!scalarRefs.isEmpty()) |
| { |
| boolean more = queryScalarXRefs(accessor, scalarRefs, context, idString); |
| if (!more) |
| { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| protected final boolean queryScalarXRefs(IDBStoreAccessor accessor, List<EReference> scalarRefs, |
| QueryXRefsContext context, String idString) |
| { |
| String tableName = table.getName(); |
| String where = getListXRefsWhere(context); |
| |
| for (EReference ref : scalarRefs) |
| { |
| ITypeMapping valueMapping = getValueMapping(ref); |
| String valueField = valueMapping.getField().getName(); |
| |
| StringBuilder builder = new StringBuilder(); |
| builder.append("SELECT "); |
| builder.append(ATTRIBUTES_ID); |
| builder.append(", "); |
| builder.append(valueField); |
| builder.append(" FROM "); |
| builder.append(tableName); |
| builder.append(" WHERE "); |
| builder.append(ATTRIBUTES_VERSION); |
| builder.append(">0 AND "); |
| builder.append(where); |
| builder.append(" AND "); |
| builder.append(valueField); |
| builder.append(" IN "); |
| builder.append(idString); |
| String sql = builder.toString(); |
| if (TRACER.isEnabled()) |
| { |
| TRACER.format("Query XRefs (attributes): {0}", sql); |
| } |
| |
| IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); |
| IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sql, ReuseProbability.MEDIUM); |
| ResultSet resultSet = null; |
| |
| try |
| { |
| resultSet = stmt.executeQuery(); |
| while (resultSet.next()) |
| { |
| CDOID sourceID = idHandler.getCDOID(resultSet, 1); |
| CDOID targetID = idHandler.getCDOID(resultSet, 2); |
| |
| boolean more = context.addXRef(targetID, sourceID, ref, 0); |
| if (TRACER.isEnabled()) |
| { |
| TRACER.format(" add XRef to context: src={0}, tgt={1}, idx=0", sourceID, targetID); |
| } |
| |
| if (!more) |
| { |
| if (TRACER.isEnabled()) |
| { |
| TRACER.format(" result limit reached. Ignoring further results."); |
| } |
| |
| return false; |
| } |
| } |
| } |
| catch (SQLException ex) |
| { |
| throw new DBException(ex); |
| } |
| finally |
| { |
| DBUtil.close(resultSet); |
| DBUtil.close(stmt); |
| } |
| } |
| |
| return true; |
| } |
| |
| protected abstract String getListXRefsWhere(QueryXRefsContext context); |
| |
| protected abstract void detachAttributes(IDBStoreAccessor accessor, CDOID id, int version, CDOBranch branch, |
| long timeStamp, OMMonitor fork); |
| |
| protected abstract void reviseOldRevision(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, long timeStamp); |
| |
| protected abstract void writeValues(IDBStoreAccessor accessor, InternalCDORevision revision); |
| |
| protected static void appendTypeMappingNames(StringBuilder builder, Collection<ITypeMapping> typeMappings) |
| { |
| if (typeMappings != null) |
| { |
| for (ITypeMapping typeMapping : typeMappings) |
| { |
| builder.append(", "); //$NON-NLS-1$ |
| builder.append(typeMapping.getField()); |
| } |
| } |
| } |
| |
| protected static void appendFieldNames(StringBuilder builder, Map<EStructuralFeature, IDBField> fields) |
| { |
| if (fields != null) |
| { |
| for (IDBField field : fields.values()) |
| { |
| builder.append(", "); //$NON-NLS-1$ |
| builder.append(field); |
| } |
| } |
| } |
| |
| protected static void appendTypeMappingParameters(StringBuilder builder, Collection<ITypeMapping> typeMappings) |
| { |
| if (typeMappings != null) |
| { |
| for (int i = 0; i < typeMappings.size(); i++) |
| { |
| builder.append(", ?"); //$NON-NLS-1$ |
| } |
| } |
| } |
| |
| protected static void appendFieldParameters(StringBuilder builder, Map<EStructuralFeature, IDBField> fields) |
| { |
| if (fields != null) |
| { |
| for (int i = 0; i < fields.size(); i++) |
| { |
| builder.append(", ?"); //$NON-NLS-1$ |
| } |
| } |
| } |
| } |