This is an automated email from the ASF dual-hosted git repository. ntimofeev pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/cayenne.git
commit 4e1bd02c6c0a4ca42321b4d1f5688cf460829f92 Author: Nikita Timofeev <stari...@gmail.com> AuthorDate: Thu Jun 30 18:00:18 2022 +0300 CAY-2737 Cayenne 4.3: cleanup deprecated code - delete LegacyDataDomainFlushAction --- .../cayenne/access/DataDomainDBDiffBuilder.java | 252 -------------- .../cayenne/access/DataDomainDeleteBucket.java | 128 -------- .../cayenne/access/DataDomainFlattenedBucket.java | 208 ------------ .../cayenne/access/DataDomainFlushObserver.java | 162 --------- .../access/DataDomainIndirectDiffBuilder.java | 178 ---------- .../cayenne/access/DataDomainInsertBucket.java | 237 -------------- .../cayenne/access/DataDomainSyncBucket.java | 278 ---------------- .../cayenne/access/DataDomainUpdateBucket.java | 157 --------- .../access/DataNodeSyncQualifierDescriptor.java | 162 --------- .../java/org/apache/cayenne/access/DbArcId.java | 96 ------ .../cayenne/access/DbEntityClassDescriptor.java | 111 ------- .../org/apache/cayenne/access/FlattenedArcKey.java | 362 --------------------- .../access/LegacyDataDomainFlushAction.java | 271 --------------- .../access/LegacyDataDomainFlushActionFactory.java | 45 --- 14 files changed, 2647 deletions(-) diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainDBDiffBuilder.java b/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainDBDiffBuilder.java deleted file mode 100644 index 013a201f8..000000000 --- a/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainDBDiffBuilder.java +++ /dev/null @@ -1,252 +0,0 @@ -/***************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - ****************************************************************/ - -package org.apache.cayenne.access; - -import org.apache.cayenne.ObjectId; -import org.apache.cayenne.access.DataDomainSyncBucket.PropagatedValueFactory; -import org.apache.cayenne.exp.parser.ASTDbPath; -import org.apache.cayenne.graph.ArcId; -import org.apache.cayenne.graph.GraphChangeHandler; -import org.apache.cayenne.graph.GraphDiff; -import org.apache.cayenne.map.DbAttribute; -import org.apache.cayenne.map.DbEntity; -import org.apache.cayenne.map.DbJoin; -import org.apache.cayenne.map.DbRelationship; -import org.apache.cayenne.map.ObjAttribute; -import org.apache.cayenne.map.ObjEntity; -import org.apache.cayenne.map.ObjRelationship; - -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; - -/** - * Processes object diffs, generating DB diffs. Can be used for both UPDATE and - * INSERT. - * - * @since 1.2 - * @deprecated since 4.2 as part of deprecated {@link LegacyDataDomainFlushAction} - */ -@Deprecated -class DataDomainDBDiffBuilder implements GraphChangeHandler { - - private ObjEntity objEntity; - private DbEntity dbEntity; - - // diff snapshot expressed in terms of object properties. - private Map<Object, Object> currentPropertyDiff; - private Map<Object, Object> currentArcDiff; - private Object currentId; - - /** - * Resets the builder to process a new combination of objEntity/dbEntity. - */ - void reset(DbEntityClassDescriptor descriptor) { - this.objEntity = descriptor.getEntity(); - this.dbEntity = descriptor.getDbEntity(); - } - - /** - * Resets the builder to process a new object for the previously set - * combination of objEntity/dbEntity. - */ - private void reset() { - currentPropertyDiff = null; - currentArcDiff = null; - currentId = null; - } - - /** - * Processes GraphDiffs of a single object, converting them to DB diff. - */ - Map<String, Object> buildDBDiff(GraphDiff singleObjectDiff) { - - reset(); - singleObjectDiff.apply(this); - - if (currentPropertyDiff == null && currentArcDiff == null && currentId == null) { - return null; - } - - Map<String, Object> dbDiff = new HashMap<>(); - - appendSimpleProperties(dbDiff); - appendForeignKeys(dbDiff); - appendPrimaryKeys(dbDiff); - - return dbDiff.isEmpty() ? null : dbDiff; - } - - private void appendSimpleProperties(Map<String, Object> dbDiff) { - // populate changed columns - if (currentPropertyDiff != null) { - for (final Map.Entry<Object, Object> entry : currentPropertyDiff.entrySet()) { - ObjAttribute attribute = objEntity.getAttribute(entry.getKey().toString()); - - // In case of a flattened attribute, ensure that it belongs to this bucket... - DbAttribute dbAttribute = attribute.getDbAttribute(); - if (dbAttribute.getEntity() == dbEntity) { - dbDiff.put(dbAttribute.getName(), entry.getValue()); - } - } - } - } - - private void appendForeignKeys(Map<String, Object> dbDiff) { - // populate changed FKs - if (currentArcDiff != null) { - for (Entry<Object, Object> entry : currentArcDiff.entrySet()) { - - DbRelationship dbRelation; - - String arcIdString = entry.getKey().toString(); - ObjRelationship relation = objEntity.getRelationship(arcIdString); - if (relation == null) { - dbRelation = dbEntity.getRelationship(arcIdString.substring(ASTDbPath.DB_PREFIX.length())); - } else { - dbRelation = relation.getDbRelationships().get(relation.getDbRelationships().size() - 1); - } - - // In case of a vertical inheritance, ensure that it belongs to this bucket... - if (dbRelation.getSourceEntity() == dbEntity) { - ObjectId targetId = (ObjectId) entry.getValue(); - for (DbJoin join : dbRelation.getJoins()) { - Object value = (targetId != null) - ? new PropagatedValueFactory(targetId, join.getTargetName()) - : null; - - dbDiff.put(join.getSourceName(), value); - } - } - } - } - } - - private void appendPrimaryKeys(Map<String, Object> dbDiff) { - - // populate changed PKs; note that we might end up overriding some - // values taken - // from the object (e.g. zero PK's). - if (currentId != null) { - dbDiff.putAll(((ObjectId) currentId).getIdSnapshot()); - } - } - - // ================================================== - // GraphChangeHandler methods. - // ================================================== - - @Override - public void nodePropertyChanged(Object nodeId, String property, Object oldValue, Object newValue) { - // note - no checking for phantom mod... assuming there is no phantom - // diffs - - if (currentPropertyDiff == null) { - currentPropertyDiff = new HashMap<>(); - } - - currentPropertyDiff.put(property, newValue); - } - - @Override - public void arcCreated(Object nodeId, Object targetNodeId, ArcId arcId) { - String arcIdString = arcId.toString(); - ObjRelationship relationship = objEntity.getRelationship(arcIdString); - - if (relationship == null) { - // phantom FK - if (arcIdString.startsWith(ASTDbPath.DB_PREFIX)) { - String relName = arcIdString.substring(ASTDbPath.DB_PREFIX.length()); - DbRelationship dbRelationship = dbEntity.getRelationship(relName); - if (!dbRelationship.isSourceIndependentFromTargetChange()) { - doArcCreated(targetNodeId, arcId); - } - } else { - throw new IllegalArgumentException("Bad arcId: " + arcId); - } - - } else { - if (!relationship.isToMany() && relationship.isToPK()) { - doArcCreated(targetNodeId, arcId); - } - } - } - - private void doArcCreated(Object targetNodeId, ArcId arcId) { - if (currentArcDiff == null) { - currentArcDiff = new HashMap<>(); - } - currentArcDiff.put(arcId, targetNodeId); - } - - @Override - public void arcDeleted(Object nodeId, Object targetNodeId, ArcId arcId) { - - String arcIdString = arcId.toString(); - ObjRelationship relationship = objEntity.getRelationship(arcIdString); - - if (relationship == null) { - // phantom FK - if (arcIdString.startsWith(ASTDbPath.DB_PREFIX)) { - - DbRelationship dbRelationship = dbEntity.getRelationship(arcIdString.substring(ASTDbPath.DB_PREFIX - .length())); - if (!dbRelationship.isSourceIndependentFromTargetChange()) { - doArcDeleted(targetNodeId, arcId); - } - } else { - throw new IllegalArgumentException("Bad arcId: " + arcId); - } - - } else if (!relationship.isSourceIndependentFromTargetChange()) { - doArcDeleted(targetNodeId, arcId); - } - } - - private void doArcDeleted(Object targetNodeId, ArcId arcId) { - if (currentArcDiff == null) { - currentArcDiff = new HashMap<>(); - currentArcDiff.put(arcId, null); - } else { - // skip deletion record if a substitute arc was created prior to - // deleting the old arc... - Object existingTargetId = currentArcDiff.get(arcId); - if (existingTargetId == null || targetNodeId.equals(existingTargetId)) { - currentArcDiff.put(arcId, null); - } - } - } - - @Override - public void nodeCreated(Object nodeId) { - // need to append PK columns - this.currentId = nodeId; - } - - @Override - public void nodeRemoved(Object nodeId) { - // noop - } - - @Override - public void nodeIdChanged(Object nodeId, Object newId) { - // noop - } -} diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainDeleteBucket.java b/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainDeleteBucket.java deleted file mode 100644 index 755c01b59..000000000 --- a/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainDeleteBucket.java +++ /dev/null @@ -1,128 +0,0 @@ -/***************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - ****************************************************************/ - -package org.apache.cayenne.access; - -import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.apache.cayenne.ObjectId; -import org.apache.cayenne.Persistent; -import org.apache.cayenne.map.DbEntity; -import org.apache.cayenne.map.EntitySorter; -import org.apache.cayenne.query.DeleteBatchQuery; -import org.apache.cayenne.query.Query; - -/** - * @since 1.2 - * @deprecated since 4.2 as part of deprecated {@link LegacyDataDomainFlushAction} - */ -@Deprecated -class DataDomainDeleteBucket extends DataDomainSyncBucket { - - DataDomainDeleteBucket(LegacyDataDomainFlushAction parent) { - super(parent); - } - - @Override - void postprocess() { - - if (!objectsByDescriptor.isEmpty()) { - - Collection<ObjectId> deletedIds = parent.getResultDeletedIds(); - - for (List<Persistent> objects : objectsByDescriptor.values()) { - for (Persistent object : objects) { - deletedIds.add(object.getObjectId()); - } - } - } - } - - @Override - void appendQueriesInternal(Collection<Query> queries) { - - DataNodeSyncQualifierDescriptor qualifierBuilder = new DataNodeSyncQualifierDescriptor(); - - EntitySorter sorter = parent.getDomain().getEntitySorter(); - sorter.sortDbEntities(dbEntities, true); - - for (DbEntity dbEntity : dbEntities) { - Collection<DbEntityClassDescriptor> descriptors = descriptorsByDbEntity - .get(dbEntity); - Map<Object, Query> batches = new LinkedHashMap<>(); - - for (DbEntityClassDescriptor descriptor : descriptors) { - - qualifierBuilder.reset(descriptor); - - boolean isRootDbEntity = descriptor.isMaster(); - - // remove object set for dependent entity, so that it does not show up - // on post processing - List<Persistent> objects = objectsByDescriptor.get(descriptor - .getClassDescriptor()); - if (objects.isEmpty()) { - continue; - } - - checkReadOnly(descriptor.getEntity()); - - if (isRootDbEntity) { - sorter.sortObjectsForEntity(descriptor.getEntity(), objects, true); - } - - for (Persistent o : objects) { - ObjectDiff diff = parent.objectDiff(o.getObjectId()); - - Map<String, Object> qualifierSnapshot = qualifierBuilder - .createQualifierSnapshot(diff); - - // organize batches by the nulls in qualifier - Set<String> nullQualifierNames = new HashSet<String>(); - for (Map.Entry<String, ?> entry : qualifierSnapshot.entrySet()) { - if (entry.getValue() == null) { - nullQualifierNames.add(entry.getKey()); - } - } - - Object batchKey = Arrays.asList(nullQualifierNames); - - DeleteBatchQuery batch = (DeleteBatchQuery) batches.get(batchKey); - if (batch == null) { - batch = new DeleteBatchQuery(dbEntity, qualifierBuilder - .getAttributes(), nullQualifierNames, 27); - batch.setUsingOptimisticLocking(qualifierBuilder - .isUsingOptimisticLocking()); - batches.put(batchKey, batch); - } - - batch.add(qualifierSnapshot); - } - } - - queries.addAll(batches.values()); - } - } -} diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainFlattenedBucket.java b/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainFlattenedBucket.java deleted file mode 100644 index 488affd0c..000000000 --- a/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainFlattenedBucket.java +++ /dev/null @@ -1,208 +0,0 @@ -/***************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - ****************************************************************/ - -package org.apache.cayenne.access; - -import org.apache.cayenne.ObjectId; -import org.apache.cayenne.map.DbAttribute; -import org.apache.cayenne.map.DbEntity; -import org.apache.cayenne.query.BatchQueryRow; -import org.apache.cayenne.query.DeleteBatchQuery; -import org.apache.cayenne.query.InsertBatchQuery; -import org.apache.cayenne.query.Query; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * A sync bucket that holds flattened queries. - * - * @since 1.2 - * @deprecated since 4.2 as part of deprecated {@link LegacyDataDomainFlushAction} - */ -@Deprecated -class DataDomainFlattenedBucket { - - final LegacyDataDomainFlushAction parent; - final Map<DbEntity, List<FlattenedArcKey>> insertArcKeys; - final Map<DbEntity, DeleteBatchQuery> flattenedDeleteQueries; - - DataDomainFlattenedBucket(LegacyDataDomainFlushAction parent) { - this.parent = parent; - this.insertArcKeys = new HashMap<>(); - this.flattenedDeleteQueries = new HashMap<>(); - } - - boolean isEmpty() { - return insertArcKeys.isEmpty() && flattenedDeleteQueries.isEmpty(); - } - - void addInsertArcKey(DbEntity flattenedEntity, FlattenedArcKey flattenedArcKey) { - List<FlattenedArcKey> arcKeys = insertArcKeys.get(flattenedEntity); - - if (arcKeys == null) { - arcKeys = new ArrayList<>(); - insertArcKeys.put(flattenedEntity, arcKeys); - } - - arcKeys.add(flattenedArcKey); - } - - void addFlattenedDelete(DbEntity flattenedEntity, FlattenedArcKey flattenedDeleteInfo) { - - DeleteBatchQuery relationDeleteQuery = flattenedDeleteQueries.get(flattenedEntity); - if (relationDeleteQuery == null) { - Collection<DbAttribute> pk = flattenedEntity.getPrimaryKeys(); - List<DbAttribute> pkList = pk instanceof List ? (List<DbAttribute>) pk : new ArrayList<>(pk); - relationDeleteQuery = new DeleteBatchQuery(flattenedEntity, pkList, Collections.<String> emptySet(), 50); - relationDeleteQuery.setUsingOptimisticLocking(false); - flattenedDeleteQueries.put(flattenedEntity, relationDeleteQuery); - } - - DataNode node = parent.getDomain().lookupDataNode(flattenedEntity.getDataMap()); - List<Map<String, Object>> flattenedSnapshots = flattenedDeleteInfo.buildJoinSnapshotsForDelete(node); - if (!flattenedSnapshots.isEmpty()) { - for (Map<String, Object> flattenedSnapshot : flattenedSnapshots) { - relationDeleteQuery.add(flattenedSnapshot); - } - } - } - - /** - * responsible for adding the flattened Insert Queries. Its possible an insert query for the same DbEntity/ObjectId - * already has been added from the insert bucket queries if that Object also has an attribute. So we want to merge - * the data for each insert into a single insert. - */ - void appendInserts(Collection<Query> queries) { - - // TODO: see "O(N) lookups" TODO's below. The first is relatively benign, as N is the number of DbEntities in the - // preceeding DataDomainInsertBucket processing. The second nested one is potentially much worse, as it may - // result in a linear scan of thousands of objects. E.g. it will affect cases with vertical inheritance with - // relationships in subclasses... - - // The fix to the above is to merge this code into DataDomainInsertBucket, so that we can combine regular and - // flattened snapshots at the point of InsertBatchQuery creation. - - for (Map.Entry<DbEntity, List<FlattenedArcKey>> entry : insertArcKeys.entrySet()) { - DbEntity dbEntity = entry.getKey(); - List<FlattenedArcKey> flattenedArcKeys = entry.getValue(); - - DataNode node = parent.getDomain().lookupDataNode(dbEntity.getDataMap()); - - // TODO: O(N) lookup - InsertBatchQuery existingQuery = findInsertBatchQuery(queries, dbEntity); - InsertBatchQuery newQuery = new InsertBatchQuery(dbEntity, 50); - - // merge the snapshots of the FAKs by ObjectId for all ToOne relationships in case we have multiple Arcs per Object - Map<ObjectId, Map<String, Object>> toOneSnapshots = new HashMap<>(); - - // gather the list of the ToMany snapshots (these will actually be their own insert rows) - List<Map<String, Object>> toManySnapshots = new ArrayList<>(); - - for (FlattenedArcKey flattenedArcKey : flattenedArcKeys) { - Map<String, Object> joinSnapshot = flattenedArcKey.buildJoinSnapshotForInsert(node); - - if (flattenedArcKey.relationship.isToMany()) { - toManySnapshots.add(joinSnapshot); - } else { - ObjectId objectId = flattenedArcKey.id1.getSourceId(); - - Map<String, Object> snapshot = toOneSnapshots.get(objectId); - - if (snapshot == null) { - toOneSnapshots.put(objectId, joinSnapshot); - } else { - // merge joinSnapshot data with existing snapshot - for (Map.Entry<String, Object> dbValue : joinSnapshot.entrySet()) { - snapshot.put(dbValue.getKey(), dbValue.getValue()); - } - } - } - } - - // apply the merged ToOne snapshots information and possibly merge it with an existing BatchQueryRow - for (Map.Entry<ObjectId, Map<String, Object>> flattenedSnapshot : toOneSnapshots.entrySet()) { - ObjectId objectId = flattenedSnapshot.getKey(); - Map<String, Object> snapshot = flattenedSnapshot.getValue(); - - if (existingQuery != null) { - - // TODO: O(N) lookup - BatchQueryRow existingRow = findRowForObjectId(existingQuery.getRows(), objectId); - // todo: do we need to worry about flattenedArcKey.id2 ? - - if (existingRow != null) { - List<DbAttribute> existingQueryDbAttributes = existingQuery.getDbAttributes(); - - for(int i=0; i < existingQueryDbAttributes.size(); i++) { - Object value = existingRow.getValue(i); - if (value != null) { - snapshot.put(existingQueryDbAttributes.get(i).getName(), value); - } - } - } - } - - newQuery.add(snapshot, objectId); - } - - // apply the ToMany snapshots as new BatchQueryRows - for(Map<String, Object> toManySnapshot : toManySnapshots) { - newQuery.add(toManySnapshot); - } - - if (existingQuery != null) { - queries.remove(existingQuery); - } - - queries.add(newQuery); - } - } - - void appendDeletes(Collection<Query> queries) { - if (!flattenedDeleteQueries.isEmpty()) { - queries.addAll(flattenedDeleteQueries.values()); - } - } - - private InsertBatchQuery findInsertBatchQuery(Collection<Query> queries, DbEntity dbEntity) { - for(Query query : queries) { - if (query instanceof InsertBatchQuery) { - InsertBatchQuery insertBatchQuery = (InsertBatchQuery)query; - if (insertBatchQuery.getDbEntity().equals(dbEntity)) { - return insertBatchQuery; - } - } - } - return null; - } - - private BatchQueryRow findRowForObjectId(List<BatchQueryRow> rows, ObjectId objectId) { - for (BatchQueryRow row : rows) { - if (row.getObjectId().equals(objectId)) { - return row; - } - } - return null; - } -} diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainFlushObserver.java b/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainFlushObserver.java deleted file mode 100644 index e99a546bf..000000000 --- a/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainFlushObserver.java +++ /dev/null @@ -1,162 +0,0 @@ -/***************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - ****************************************************************/ - -package org.apache.cayenne.access; - -import java.util.List; - -import org.apache.cayenne.CayenneRuntimeException; -import org.apache.cayenne.DataRow; -import org.apache.cayenne.ObjectId; -import org.apache.cayenne.ResultIterator; -import org.apache.cayenne.log.JdbcEventLogger; -import org.apache.cayenne.map.DbAttribute; -import org.apache.cayenne.query.BatchQuery; -import org.apache.cayenne.query.InsertBatchQuery; -import org.apache.cayenne.query.Query; -import org.apache.cayenne.util.Util; - -/** - * Used as an observer for DataContext commit operations. - * - * @since 1.2 - * @deprecated since 4.2 as part of deprecated {@link LegacyDataDomainFlushAction} - */ -@Deprecated -class DataDomainFlushObserver implements OperationObserver { - - /** - * @since 3.1 - */ - private JdbcEventLogger logger; - - DataDomainFlushObserver(JdbcEventLogger logger) { - this.logger = logger; - } - - @Override - public void nextQueryException(Query query, Exception ex) { - throw new CayenneRuntimeException("Raising from query exception.", Util.unwindException(ex)); - } - - @Override - public void nextGlobalException(Exception ex) { - throw new CayenneRuntimeException("Raising from underlyingQueryEngine exception.", Util.unwindException(ex)); - } - - /** - * Processes generated keys. - * - * @since 1.2 - */ - @Override - @SuppressWarnings({ "rawtypes", "unchecked" }) - public void nextGeneratedRows(Query query, ResultIterator<?> keysIterator, List<ObjectId> idsToUpdate) { - - // read and close the iterator before doing anything else - List<DataRow> keys; - try { - keys = (List<DataRow>) keysIterator.allRows(); - } finally { - keysIterator.close(); - } - - if (!(query instanceof InsertBatchQuery)) { - throw new CayenneRuntimeException("Generated keys only supported for InsertBatchQuery, instead got %s", query); - } - - if (keys.size() != idsToUpdate.size()) { - throw new CayenneRuntimeException("Mismatching number of generated PKs: expected %d, instead got %d", idsToUpdate.size(), keys.size()); - } - - for (int i = 0; i < keys.size(); i++) { - DataRow key = keys.get(i); - - // empty key? - if (key.size() == 0) { - throw new CayenneRuntimeException("Empty key generated."); - } - - ObjectId idToUpdate = idsToUpdate.get(i); - if (idToUpdate == null || !idToUpdate.isTemporary()) { - // why would this happen? - return; - } - - BatchQuery batch = (BatchQuery) query; - for (DbAttribute attribute : batch.getDbEntity().getGeneratedAttributes()) { - - // batch can have generated attributes that are not PKs, e.g. - // columns with - // DB DEFAULT values. Ignore those. - if (attribute.isPrimaryKey()) { - - Object value = key.get(attribute.getName()); - - // As of now (01/2005) many tested drivers don't provide decent - // descriptors of - // identity result sets, so a data row may contain garbage labels. - if (value == null) { - value = key.values().iterator().next(); - } - - // Log the generated PK - logger.logGeneratedKey(attribute, value); - - // I guess we should override any existing value, - // as generated key is the latest thing that exists in the DB. - idToUpdate.getReplacementIdMap().put(attribute.getName(), value); - break; - } - } - } - } - - public void setJdbcEventLogger(JdbcEventLogger logger) { - this.logger = logger; - } - - public JdbcEventLogger getJdbcEventLogger() { - return this.logger; - } - - @Override - public void nextBatchCount(Query query, int[] resultCount) { - } - - @Override - public void nextCount(Query query, int resultCount) { - } - - @Override - public void nextRows(Query query, List<?> dataRows) { - } - - @Override - @SuppressWarnings("rawtypes") - public void nextRows(Query q, ResultIterator it) { - throw new UnsupportedOperationException( - "'nextDataRows(Query,ResultIterator)' is unsupported (and unexpected) on commit."); - } - - @Override - public boolean isIteratedResult() { - return false; - } -} diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainIndirectDiffBuilder.java b/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainIndirectDiffBuilder.java deleted file mode 100644 index ae1574e54..000000000 --- a/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainIndirectDiffBuilder.java +++ /dev/null @@ -1,178 +0,0 @@ -/***************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - ****************************************************************/ - -package org.apache.cayenne.access; - -import java.util.Collection; -import java.util.HashSet; - -import org.apache.cayenne.CayenneRuntimeException; -import org.apache.cayenne.ObjectId; -import org.apache.cayenne.graph.ArcId; -import org.apache.cayenne.graph.GraphChangeHandler; -import org.apache.cayenne.graph.GraphDiff; -import org.apache.cayenne.map.DbEntity; -import org.apache.cayenne.map.EntityResolver; -import org.apache.cayenne.map.ObjEntity; -import org.apache.cayenne.map.ObjRelationship; - -/** - * A processor of ObjectStore indirect changes, such as flattened relationships - * and to-many relationships. - * - * @since 1.2 - * @deprecated since 4.2 as part of deprecated {@link LegacyDataDomainFlushAction} - */ -@Deprecated -final class DataDomainIndirectDiffBuilder implements GraphChangeHandler { - - private final LegacyDataDomainFlushAction parent; - private final EntityResolver resolver; - private final Collection<ObjectId> indirectModifications; - private final Collection<FlattenedArcKey> flattenedInserts; - private final Collection<FlattenedArcKey> flattenedDeletes; - - DataDomainIndirectDiffBuilder(LegacyDataDomainFlushAction parent) { - this.parent = parent; - this.indirectModifications = parent.getResultIndirectlyModifiedIds(); - this.resolver = parent.getDomain().getEntityResolver(); - this.flattenedInserts = new HashSet<>(); - this.flattenedDeletes = new HashSet<>(); - } - - void processIndirectChanges(GraphDiff allChanges) { - // extract flattened and indirect changes and remove duplicate changes... - allChanges.apply(this); - - if (!flattenedInserts.isEmpty()) { - for (final FlattenedArcKey key : flattenedInserts) { - DbEntity entity = key.getJoinEntity(); - parent.addFlattenedInsert(entity, key); - } - } - - if (!flattenedDeletes.isEmpty()) { - for (final FlattenedArcKey key : flattenedDeletes) { - DbEntity entity = key.getJoinEntity(); - parent.addFlattenedDelete(entity, key); - } - } - } - - @Override - public void arcCreated(Object nodeId, Object targetNodeId, ArcId arcId) { - ObjEntity entity = resolver.getObjEntity(((ObjectId) nodeId).getEntityName()); - ObjRelationship relationship = entity.getRelationship(arcId.toString()); - - if (relationship.isSourceIndependentFromTargetChange()) { - - ObjectId nodeObjectId = (ObjectId) nodeId; - if (!nodeObjectId.isTemporary()) { - indirectModifications.add(nodeObjectId); - } - - if (relationship.isFlattened()) { - if (relationship.isReadOnly()) { - throw new CayenneRuntimeException("Cannot set the read-only flattened relationship '%s' in ObjEntity '%s'." - , relationship.getName(), relationship.getSourceEntity().getName()); - } - - // build path without last segment - StringBuilder path = new StringBuilder(); - for(int i=0; i<relationship.getDbRelationships().size() - 1; i++) { - if(path.length() > 0) { - path.append('.'); - } - path.append(relationship.getDbRelationships().get(i).getName()); - } - - if(!parent.getContext().getObjectStore().hasFlattenedPath(nodeObjectId, path.toString())) { - // Register this combination (so we can remove it later if an insert occurs before commit) - FlattenedArcKey key = new FlattenedArcKey(nodeObjectId, (ObjectId) targetNodeId, relationship); - - // If this combination has already been deleted, simply undelete it. - if (!flattenedDeletes.remove(key)) { - flattenedInserts.add(key); - } - } - } - } - } - - @Override - public void arcDeleted(Object nodeId, Object targetNodeId, ArcId arcId) { - - ObjEntity entity = resolver.getObjEntity(((ObjectId) nodeId).getEntityName()); - ObjRelationship relationship = entity.getRelationship(arcId.toString()); - - if (relationship.isSourceIndependentFromTargetChange()) { - // do not record temporary id mods... - ObjectId nodeObjectId = (ObjectId) nodeId; - if (!nodeObjectId.isTemporary()) { - indirectModifications.add(nodeObjectId); - } - - if (relationship.isFlattened()) { - if (relationship.isReadOnly()) { - throw new CayenneRuntimeException("Cannot unset the read-only flattened relationship %s" - , relationship.getName()); - } - - // build path without last segment - StringBuilder path = new StringBuilder(); - for(int i=0; i<relationship.getDbRelationships().size() - 1; i++) { - if(path.length() > 0) { - path.append('.'); - } - path.append(relationship.getDbRelationships().get(i).getName()); - } - - if(!parent.getContext().getObjectStore().hasFlattenedPath(nodeObjectId, path.toString())) { - // Register this combination (so we can remove it later if an insert occurs before commit) - FlattenedArcKey key = new FlattenedArcKey(nodeObjectId, (ObjectId) targetNodeId, relationship); - - // If this combination has already been inserted, simply "uninsert" it also do not delete it twice - if (!flattenedInserts.remove(key)) { - flattenedDeletes.add(key); - } - } - } - } - } - - @Override - public void nodeIdChanged(Object nodeId, Object newId) { - // noop - } - - @Override - public void nodeCreated(Object nodeId) { - // noop - } - - @Override - public void nodeRemoved(Object nodeId) { - // noop - } - - @Override - public void nodePropertyChanged(Object nodeId, String property, Object oldValue, Object newValue) { - // noop - } -} diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainInsertBucket.java b/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainInsertBucket.java deleted file mode 100644 index d3b59278f..000000000 --- a/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainInsertBucket.java +++ /dev/null @@ -1,237 +0,0 @@ -/***************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - ****************************************************************/ - -package org.apache.cayenne.access; - -import java.util.Collection; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -import org.apache.cayenne.CayenneRuntimeException; -import org.apache.cayenne.ObjectId; -import org.apache.cayenne.Persistent; -import org.apache.cayenne.dba.PkGenerator; -import org.apache.cayenne.map.DbAttribute; -import org.apache.cayenne.map.DbEntity; -import org.apache.cayenne.map.DbJoin; -import org.apache.cayenne.map.DbRelationship; -import org.apache.cayenne.map.EntitySorter; -import org.apache.cayenne.map.ObjAttribute; -import org.apache.cayenne.map.ObjEntity; -import org.apache.cayenne.query.InsertBatchQuery; -import org.apache.cayenne.query.Query; - -/** - * @since 1.2 - * @deprecated since 4.2 as part of deprecated {@link LegacyDataDomainFlushAction} - */ -@Deprecated -class DataDomainInsertBucket extends DataDomainSyncBucket { - - List<FlattenedInsert> flattenedInserts; - - DataDomainInsertBucket(LegacyDataDomainFlushAction parent) { - super(parent); - } - - @Override - void appendQueriesInternal(Collection<Query> queries) { - - DataDomainDBDiffBuilder diffBuilder = new DataDomainDBDiffBuilder(); - - EntitySorter sorter = parent.getDomain().getEntitySorter(); - sorter.sortDbEntities(dbEntities, false); - - for (DbEntity dbEntity : dbEntities) { - - Collection<DbEntityClassDescriptor> descriptors = descriptorsByDbEntity.get(dbEntity); - - InsertBatchQuery batch = new InsertBatchQuery(dbEntity, 27); - for (DbEntityClassDescriptor descriptor : descriptors) { - - diffBuilder.reset(descriptor); - - List<Persistent> objects = objectsByDescriptor.get(descriptor.getClassDescriptor()); - if (objects.isEmpty()) { - continue; - } - - checkReadOnly(descriptor.getEntity()); - createPermIds(descriptor, objects); - sorter.sortObjectsForEntity(descriptor.getEntity(), objects, false); - - for (Persistent o : objects) { - Map<String, Object> snapshot = diffBuilder.buildDBDiff(parent.objectDiff(o.getObjectId())); - - // we need to insert even if there is no changes to default - // values so creating an empty changes map - if (snapshot == null) { - snapshot = new HashMap<>(); - } - - batch.add(snapshot, o.getObjectId()); - if(!descriptor.isMaster()) { - trackFlattenedInsert(descriptor, o); - } - } - } - - queries.add(batch); - } - } - - void createPermIds(DbEntityClassDescriptor descriptor, Collection<Persistent> objects) { - - if (objects.isEmpty()) { - return; - } - - ObjEntity objEntity = descriptor.getEntity(); - DbEntity entity = descriptor.getDbEntity(); - - DataNode node = parent.getDomain().lookupDataNode(entity.getDataMap()); - boolean supportsGeneratedKeys = node.getAdapter().supportsGeneratedKeys(); - - PkGenerator pkGenerator = node.getAdapter().getPkGenerator(); - - for (Persistent object : objects) { - ObjectId id = object.getObjectId(); - if (id == null || !id.isTemporary()) { - continue; - } - - // modify replacement id directly... - Map<String, Object> idMap = id.getReplacementIdMap(); - - boolean autoPkDone = false; - - for (DbAttribute dbAttr : entity.getPrimaryKeys()) { - String dbAttrName = dbAttr.getName(); - - if (idMap.containsKey(dbAttrName)) { - continue; - } - - // handle meaningful PK - ObjAttribute objAttr = objEntity.getAttributeForDbAttribute(dbAttr); - if (objAttr != null) { - - Object value = descriptor.getClassDescriptor().getProperty(objAttr.getName()) - .readPropertyDirectly(object); - - if (value != null) { - Class<?> javaClass = objAttr.getJavaClass(); - if (javaClass.isPrimitive() && value instanceof Number && ((Number) value).intValue() == 0) { - // primitive 0 has to be treated as NULL, or - // otherwise we can't generate PK for POJO's - } else { - idMap.put(dbAttrName, value); - continue; - } - } - } - - // skip db-generated - if (supportsGeneratedKeys && dbAttr.isGenerated()) { - continue; - } - - // skip propagated - if (isPropagated(dbAttr)) { - continue; - } - - // only a single key can be generated from DB... if this is done - // already in this loop, we must bail out. - if (autoPkDone) { - throw new CayenneRuntimeException("Primary Key autogeneration only works for a single attribute."); - } - - // finally, use database generation mechanism - try { - Object pkValue = pkGenerator.generatePk(node, dbAttr); - idMap.put(dbAttrName, pkValue); - autoPkDone = true; - } catch (Exception ex) { - throw new CayenneRuntimeException("Error generating PK: %s", ex, ex.getMessage()); - } - } - } - } - - // TODO, andrus 4/12/2006 - move to DbAttribute in 2.0+ - boolean isPropagated(DbAttribute attribute) { - - for (DbRelationship dbRel : attribute.getEntity().getRelationships()) { - if (!dbRel.isToMasterPK()) { - continue; - } - - for (DbJoin join : dbRel.getJoins()) { - if (attribute.getName().equals(join.getSourceName())) { - return true; - } - } - } - - return false; - } - - void trackFlattenedInsert(DbEntityClassDescriptor descriptor, Persistent object) { - if(flattenedInserts == null) { - flattenedInserts = new LinkedList<>(); - } - - StringBuilder sb = new StringBuilder(); - for(DbRelationship rel : descriptor.getPathFromMaster()) { - if(sb.length() > 0) { - sb.append('.'); - } - sb.append(rel.getName()); - } - - flattenedInserts.add(new FlattenedInsert(sb.toString(), object)); - } - - @Override - void postprocess() { - super.postprocess(); - if(flattenedInserts != null) { - for(FlattenedInsert insert : flattenedInserts) { - insert.register(parent.getContext().getObjectStore()); - } - } - } - - private static class FlattenedInsert { - private final String path; - private final Persistent object; - - private FlattenedInsert(String path, Persistent object) { - this.path = path; - this.object = object; - } - - private void register(ObjectStore objectStore) { - objectStore.markFlattenedPath(object.getObjectId(), path, ObjectId.of("tmp")); - } - } -} diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainSyncBucket.java b/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainSyncBucket.java deleted file mode 100644 index 23dc5c295..000000000 --- a/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainSyncBucket.java +++ /dev/null @@ -1,278 +0,0 @@ -/***************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - ****************************************************************/ - -package org.apache.cayenne.access; - -import org.apache.cayenne.CayenneRuntimeException; -import org.apache.cayenne.DataObject; -import org.apache.cayenne.DataRow; -import org.apache.cayenne.ObjectId; -import org.apache.cayenne.Persistent; -import org.apache.cayenne.graph.CompoundDiff; -import org.apache.cayenne.graph.NodeIdChangeOperation; -import org.apache.cayenne.map.DbEntity; -import org.apache.cayenne.map.ObjAttribute; -import org.apache.cayenne.map.ObjEntity; -import org.apache.cayenne.query.Query; -import org.apache.cayenne.reflect.ArcProperty; -import org.apache.cayenne.reflect.AttributeProperty; -import org.apache.cayenne.reflect.ClassDescriptor; -import org.apache.cayenne.reflect.PropertyException; -import org.apache.cayenne.reflect.ToManyMapProperty; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.function.Supplier; - -/** - * A superclass of batch query wrappers. - * - * @since 1.2 - * @deprecated since 4.2 as part of deprecated {@link LegacyDataDomainFlushAction} - */ -@Deprecated -abstract class DataDomainSyncBucket { - - final Map<ClassDescriptor, List<Persistent>> objectsByDescriptor; - final LegacyDataDomainFlushAction parent; - - List<DbEntity> dbEntities; - Map<DbEntity, Collection<DbEntityClassDescriptor>> descriptorsByDbEntity; - - DataDomainSyncBucket(LegacyDataDomainFlushAction parent) { - this.objectsByDescriptor = new HashMap<>(); - this.parent = parent; - } - - boolean isEmpty() { - return objectsByDescriptor.isEmpty(); - } - - abstract void appendQueriesInternal(Collection<Query> queries); - - /** - * Appends all queries originated in the bucket to provided collection. - */ - void appendQueries(Collection<Query> queries) { - - if (!objectsByDescriptor.isEmpty()) { - groupObjEntitiesBySpannedDbEntities(); - appendQueriesInternal(queries); - } - } - - void checkReadOnly(ObjEntity entity) throws CayenneRuntimeException { - - if (entity == null) { - throw new NullPointerException("Entity must not be null."); - } - - if (entity.isReadOnly()) { - throw new CayenneRuntimeException("Attempt to modify object(s) mapped to a read-only entity: '%s'. " + - "Can't commit changes.", entity.getName()); - } - } - - private void groupObjEntitiesBySpannedDbEntities() { - - dbEntities = new ArrayList<>(objectsByDescriptor.size()); - descriptorsByDbEntity = new HashMap<>(objectsByDescriptor.size() * 2); - - for (ClassDescriptor descriptor : objectsByDescriptor.keySet()) { - addSpannedDbEntities(descriptor); - } - } - - void addSpannedDbEntities(ClassDescriptor descriptor) { - // root DbEntity - addDescriptor(descriptor, new DbEntityClassDescriptor(descriptor)); - - // secondary DbEntities... - // Note that this logic won't allow flattened attributes to span multiple databases... - for (ObjAttribute objAttribute : descriptor.getEntity().getAttributes()) { - if (objAttribute.isFlattened()) { - addDescriptor(descriptor, new DbEntityClassDescriptor(descriptor, objAttribute)); - } - } - } - - void addDescriptor(ClassDescriptor descriptor, DbEntityClassDescriptor dbEntityDescriptor) { - DbEntity dbEntity = dbEntityDescriptor.getDbEntity(); - Collection<DbEntityClassDescriptor> descriptors = descriptorsByDbEntity.get(dbEntity); - - if (descriptors == null) { - descriptors = new ArrayList<>(1); - dbEntities.add(dbEntity); - descriptorsByDbEntity.put(dbEntity, descriptors); - } - - if (!containsClassDescriptor(descriptors, descriptor)) { - descriptors.add(dbEntityDescriptor); - } - } - - private boolean containsClassDescriptor( - Collection<DbEntityClassDescriptor> descriptors, - ClassDescriptor classDescriptor) { - for (DbEntityClassDescriptor descriptor : descriptors) { - if (classDescriptor.equals(descriptor.getClassDescriptor())) { - return true; - } - } - return false; - } - - void addDirtyObject(Persistent object, ClassDescriptor descriptor) { - objectsByDescriptor.computeIfAbsent(descriptor, k -> new ArrayList<>()).add(object); - } - - void postprocess() { - - if (!objectsByDescriptor.isEmpty()) { - - CompoundDiff result = parent.getResultDiff(); - Map<ObjectId, DataRow> modifiedSnapshots = parent - .getResultModifiedSnapshots(); - Collection<ObjectId> deletedIds = parent.getResultDeletedIds(); - - for (Map.Entry<ClassDescriptor, List<Persistent>> entry : objectsByDescriptor.entrySet()) { - - ClassDescriptor descriptor = entry.getKey(); - - for (Persistent object : entry.getValue()) { - ObjectId id = object.getObjectId(); - - ObjectId finalId; - - // record id change and update attributes for generated ids - if (id.isReplacementIdAttached()) { - - Map<String, Object> replacement = id.getReplacementIdMap(); - for (AttributeProperty property : descriptor.getIdProperties()) { - - Object value = replacement.get(property - .getAttribute() - .getDbAttributeName()); - - // TODO: andrus, 11/28/2006: this operation may be redundant - // if the id wasn't generated. We may need to optimize it... - if (value != null) { - property.writePropertyDirectly(object, null, value); - } - } - - ObjectId replacementId = id.createReplacementId(); - - result.add(new NodeIdChangeOperation(id, replacementId)); - - // classify replaced permanent ids as "deleted", as - // DataRowCache has no notion of replaced id... - if (!id.isTemporary()) { - deletedIds.add(id); - } - - finalId = replacementId; - } else if (id.isTemporary()) { - throw new CayenneRuntimeException("Temporary ID hasn't been replaced on commit: %s", object); - } else { - finalId = id; - } - - // do not take the snapshot until generated columns are processed (see code above) - DataRow dataRow = parent.getContext().currentSnapshot(object); - - if (object instanceof DataObject) { - DataObject dataObject = (DataObject) object; - dataRow.setReplacesVersion(dataObject.getSnapshotVersion()); - dataObject.setSnapshotVersion(dataRow.getVersion()); - } - - modifiedSnapshots.put(finalId, dataRow); - - // update Map reverse relationships - for (ArcProperty arc : descriptor.getMapArcProperties()) { - ToManyMapProperty reverseArc = (ToManyMapProperty) arc.getComplimentaryReverseArc(); - - // must resolve faults... hopefully for to-one this will not cause extra fetches... - Object source = arc.readProperty(object); - if (source != null && !reverseArc.isFault(source)) { - remapTarget(reverseArc, source, object); - } - } - } - } - } - } - - private void remapTarget( - ToManyMapProperty property, - Object source, - Object target) throws PropertyException { - - Map<Object, Object> map = (Map<Object, Object>) property.readProperty(source); - Object newKey = property.getMapKey(target); - Object currentValue = map.get(newKey); - - if (currentValue == target) { - // nothing to do - return; - } - // else - do not check for conflicts here (i.e. another object mapped for the same - // key), as we have no control of the order in which this method is called, so - // another object may be remapped later by the caller - - // must do a slow map scan to ensure the object is not mapped under a different key... - Iterator<Map.Entry<Object, Object>> it = map.entrySet().iterator(); - while (it.hasNext()) { - Map.Entry<Object, Object> e = it.next(); - if (e.getValue() == target) { - it.remove(); - break; - } - } - - map.put(newKey, target); - } - - // a factory for extracting PKs generated on commit. - final static class PropagatedValueFactory implements Supplier { - - ObjectId masterID; - String masterKey; - - PropagatedValueFactory(ObjectId masterID, String masterKey) { - this.masterID = masterID; - this.masterKey = masterKey; - } - - public Object get() { - Object value = masterID.getIdSnapshot().get(masterKey); - if (value == null) { - throw new CayenneRuntimeException("Can't extract a master key. " - + "Missing key (%s), master ID (%s)", masterKey, masterID); - } - - return value; - } - } -} diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainUpdateBucket.java b/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainUpdateBucket.java deleted file mode 100644 index 55d426ca6..000000000 --- a/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainUpdateBucket.java +++ /dev/null @@ -1,157 +0,0 @@ -/***************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - ****************************************************************/ - -package org.apache.cayenne.access; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.apache.cayenne.Persistent; -import org.apache.cayenne.map.Attribute; -import org.apache.cayenne.map.DbAttribute; -import org.apache.cayenne.map.DbEntity; -import org.apache.cayenne.map.ObjEntity; -import org.apache.cayenne.map.ObjRelationship; -import org.apache.cayenne.query.Query; -import org.apache.cayenne.query.UpdateBatchQuery; -import org.apache.cayenne.reflect.ClassDescriptor; - -/** - * @since 1.2 - * @deprecated since 4.2 as part of deprecated {@link LegacyDataDomainFlushAction} - */ -@Deprecated -class DataDomainUpdateBucket extends DataDomainSyncBucket { - - DataDomainUpdateBucket(LegacyDataDomainFlushAction parent) { - super(parent); - } - - @Override - void addSpannedDbEntities(ClassDescriptor descriptor) { - super.addSpannedDbEntities(descriptor); - // for update we need to add entities for flattened toOne relationships - for(ObjRelationship objRelationship : descriptor.getEntity().getRelationships()) { - if(objRelationship.isFlattened() && !objRelationship.isToMany()) { - addDescriptor(descriptor, new DbEntityClassDescriptor(descriptor, objRelationship)); - } - } - } - - @Override - void appendQueriesInternal(Collection<Query> queries) { - - DataDomainDBDiffBuilder diffBuilder = new DataDomainDBDiffBuilder(); - DataNodeSyncQualifierDescriptor qualifierBuilder = new DataNodeSyncQualifierDescriptor(); - - for (DbEntity dbEntity : dbEntities) { - - Collection<DbEntityClassDescriptor> descriptors = descriptorsByDbEntity.get(dbEntity); - Map<Object, Query> batches = new LinkedHashMap<>(); - - for (DbEntityClassDescriptor descriptor : descriptors) { - ObjEntity entity = descriptor.getEntity(); - - diffBuilder.reset(descriptor); - qualifierBuilder.reset(descriptor); - boolean isRootDbEntity = entity.getDbEntity() == dbEntity; - - for (Persistent o : objectsByDescriptor.get(descriptor.getClassDescriptor())) { - ObjectDiff diff = parent.objectDiff(o.getObjectId()); - - Map<String, Object> snapshot = diffBuilder.buildDBDiff(diff); - - // check whether MODIFIED object has real db-level modifications - if (snapshot == null) { - continue; - } - - // after we filtered out "fake" modifications, check if an - // attempt is made to modify a read only entity - checkReadOnly(entity); - - Map<String, Object> qualifierSnapshot = qualifierBuilder.createQualifierSnapshot(diff); - - // organize batches by the updated columns + nulls in qualifier - Set<String> snapshotSet = snapshot.keySet(); - Set<String> nullQualifierNames = new HashSet<>(); - for (Map.Entry<String, Object> entry : qualifierSnapshot.entrySet()) { - if (entry.getValue() == null) { - nullQualifierNames.add(entry.getKey()); - } - } - - List<Set<String>> batchKey = Arrays.asList(snapshotSet, nullQualifierNames); - - UpdateBatchQuery batch = (UpdateBatchQuery) batches.get(batchKey); - if (batch == null) { - batch = new UpdateBatchQuery( - dbEntity, - qualifierBuilder.getAttributes(), - updatedAttributes(dbEntity, snapshot), - nullQualifierNames, - 10); - - batch.setUsingOptimisticLocking(qualifierBuilder.isUsingOptimisticLocking()); - batches.put(batchKey, batch); - } - - batch.add(qualifierSnapshot, snapshot, o.getObjectId()); - - // update replacement id with meaningful PK changes - if (isRootDbEntity) { - Map<String, Object> replacementId = o - .getObjectId() - .getReplacementIdMap(); - - for (DbAttribute pk : dbEntity.getPrimaryKeys()) { - String name = pk.getName(); - if (snapshot.containsKey(name) - && !replacementId.containsKey(name)) { - replacementId.put(name, snapshot.get(name)); - } - } - } - } - } - - queries.addAll(batches.values()); - } - } - - /** - * Creates a list of DbAttributes that are updated in a snapshot - */ - private List<DbAttribute> updatedAttributes(DbEntity entity, Map<String, Object> updatedSnapshot) { - List<DbAttribute> attributes = new ArrayList<>(updatedSnapshot.size()); - Map<String, ? extends Attribute> entityAttributes = entity.getAttributeMap(); - - for (String name : updatedSnapshot.keySet()) { - attributes.add((DbAttribute)entityAttributes.get(name)); - } - - return attributes; - } -} diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/DataNodeSyncQualifierDescriptor.java b/cayenne-server/src/main/java/org/apache/cayenne/access/DataNodeSyncQualifierDescriptor.java deleted file mode 100644 index 49c225b03..000000000 --- a/cayenne-server/src/main/java/org/apache/cayenne/access/DataNodeSyncQualifierDescriptor.java +++ /dev/null @@ -1,162 +0,0 @@ -/***************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - ****************************************************************/ - -package org.apache.cayenne.access; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Function; - -import org.apache.cayenne.CayenneRuntimeException; -import org.apache.cayenne.ObjectId; -import org.apache.cayenne.map.DbAttribute; -import org.apache.cayenne.map.DbJoin; -import org.apache.cayenne.map.DbRelationship; -import org.apache.cayenne.map.ObjAttribute; -import org.apache.cayenne.map.ObjEntity; -import org.apache.cayenne.map.ObjRelationship; - -/** - * Builds update qualifier snapshots, including optimistic locking. - * - * @since 1.2 - * @deprecated since 4.2 as part of deprecated {@link LegacyDataDomainFlushAction} - */ -@Deprecated -class DataNodeSyncQualifierDescriptor { - - private List<DbAttribute> attributes; - private List<Function<ObjectDiff, Object>> valueTransformers; - private boolean usingOptimisticLocking; - - public boolean isUsingOptimisticLocking() { - return usingOptimisticLocking; - } - - List<DbAttribute> getAttributes() { - return attributes; - } - - Map<String, Object> createQualifierSnapshot(ObjectDiff diff) { - int len = attributes.size(); - - Map<String, Object> map = new HashMap<>(len * 2); - for (int i = 0; i < len; i++) { - DbAttribute attribute = attributes.get(i); - if (!map.containsKey(attribute.getName())) { - Object value = valueTransformers.get(i).apply(diff); - map.put(attribute.getName(), value); - } - } - - return map; - } - - void reset(DbEntityClassDescriptor descriptor) { - - attributes = new ArrayList<>(3); - valueTransformers = new ArrayList<>(3); - usingOptimisticLocking = descriptor.getEntity().getLockType() == ObjEntity.LOCK_TYPE_OPTIMISTIC; - - // master PK columns - if (descriptor.isMaster()) { - for (final DbAttribute attribute : descriptor.getDbEntity().getPrimaryKeys()) { - attributes.add(attribute); - valueTransformers.add(input -> { - ObjectId id = (ObjectId) input.getNodeId(); - return id.getIdSnapshot().get(attribute.getName()); - }); - } - } else { - - // TODO: andrus 12/23/2007 - only one step relationship is supported... - if (descriptor.getPathFromMaster().size() != 1) { - throw new CayenneRuntimeException( - "Only single step dependent relationships are currently supported. Actual path length: %d" - , descriptor.getPathFromMaster().size()); - } - - DbRelationship masterDependentDbRel = descriptor.getPathFromMaster().get(0); - - if (masterDependentDbRel != null) { - for (final DbJoin dbAttrPair : masterDependentDbRel.getJoins()) { - DbAttribute dbAttribute = dbAttrPair.getTarget(); - if (!attributes.contains(dbAttribute)) { - - attributes.add(dbAttribute); - valueTransformers.add(input -> { - ObjectId id = (ObjectId) input.getNodeId(); - return id.getIdSnapshot().get(dbAttrPair.getTargetName()); - }); - } - } - } - } - - if (descriptor.isMaster() && usingOptimisticLocking) { - - for (final ObjAttribute attribute : descriptor.getEntity().getAttributes()) { - - if (attribute.isUsedForLocking() && !attribute.isFlattened()) { - // only care about first step in a flattened attribute - DbAttribute dbAttribute = (DbAttribute) attribute.getDbPathIterator().next(); - - // only use qualifier if dbEntities match - if (dbAttribute.getEntity().equals(descriptor.getDbEntity()) && !attributes.contains(dbAttribute)) { - attributes.add(dbAttribute); - valueTransformers.add(input -> input.getSnapshotValue(attribute.getName())); - } - } - } - - for (final ObjRelationship relationship : descriptor.getEntity().getRelationships()) { - - if (relationship.isUsedForLocking()) { - // only care about the first DbRelationship - DbRelationship dbRelationship = relationship.getDbRelationships().get(0); - - for (final DbJoin dbAttrPair : dbRelationship.getJoins()) { - DbAttribute dbAttribute = dbAttrPair.getSource(); - - // relationship transformers override attribute transformers for meaningful FK's... - // why meaningful FKs can go out of sync is another story (CAY-595) - int index = attributes.indexOf(dbAttribute); - if (index >= 0 && !dbAttribute.isForeignKey()) { - continue; - } - - Function<ObjectDiff, Object> transformer = input -> { - ObjectId targetId = input.getArcSnapshotValue(relationship.getName()); - return targetId != null ? targetId.getIdSnapshot().get(dbAttrPair.getTargetName()) : null; - }; - - if (index < 0) { - attributes.add(dbAttribute); - valueTransformers.add(transformer); - } else { - valueTransformers.set(index, transformer); - } - } - } - } - } - } -} diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/DbArcId.java b/cayenne-server/src/main/java/org/apache/cayenne/access/DbArcId.java deleted file mode 100644 index a18626f8d..000000000 --- a/cayenne-server/src/main/java/org/apache/cayenne/access/DbArcId.java +++ /dev/null @@ -1,96 +0,0 @@ -/***************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - ****************************************************************/ -package org.apache.cayenne.access; - -import org.apache.cayenne.ObjectId; -import org.apache.cayenne.map.DbEntity; -import org.apache.cayenne.map.DbRelationship; -import org.apache.cayenne.util.EqualsBuilder; -import org.apache.cayenne.util.HashCodeBuilder; - -/** - * An id similar to ObjectId that identifies a DbEntity snapshot for implicit - * DbEntities of flattened attributes or relationships. Provides 'equals' and - * 'hashCode' implementations adequate for use as a map key. - * - * @since 4.0 - * @deprecated since 4.2 as part of deprecated {@link LegacyDataDomainFlushAction} - */ -@Deprecated -final class DbArcId { - - private int hashCode; - - private ObjectId sourceId; - private DbRelationship incomingArc; - - private DbEntity entity; - - DbArcId(ObjectId sourceId, DbRelationship incomingArc) { - this.sourceId = sourceId; - this.incomingArc = incomingArc; - } - - DbEntity getEntity() { - if (entity == null) { - entity = (DbEntity) incomingArc.getTargetEntity(); - } - - return entity; - } - - ObjectId getSourceId() { - return sourceId; - } - - DbRelationship getIncominArc() { - return incomingArc; - } - - @Override - public int hashCode() { - - if (this.hashCode == 0) { - HashCodeBuilder builder = new HashCodeBuilder(3, 5); - builder.append(sourceId); - builder.append(incomingArc.getName()); - this.hashCode = builder.toHashCode(); - } - - return hashCode; - } - - @Override - public boolean equals(Object object) { - - if (this == object) { - return true; - } - - if (!(object instanceof DbArcId)) { - return false; - } - - DbArcId id = (DbArcId) object; - - return new EqualsBuilder().append(sourceId, id.sourceId) - .append(incomingArc.getName(), id.incomingArc.getName()) - .isEquals(); - } -} diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/DbEntityClassDescriptor.java b/cayenne-server/src/main/java/org/apache/cayenne/access/DbEntityClassDescriptor.java deleted file mode 100644 index 496a968bf..000000000 --- a/cayenne-server/src/main/java/org/apache/cayenne/access/DbEntityClassDescriptor.java +++ /dev/null @@ -1,111 +0,0 @@ -/***************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - ****************************************************************/ -package org.apache.cayenne.access; - -import org.apache.cayenne.CayenneRuntimeException; -import org.apache.cayenne.map.DbAttribute; -import org.apache.cayenne.map.DbEntity; -import org.apache.cayenne.map.DbRelationship; -import org.apache.cayenne.map.ObjAttribute; -import org.apache.cayenne.map.ObjEntity; -import org.apache.cayenne.map.ObjRelationship; -import org.apache.cayenne.reflect.ClassDescriptor; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -/** - * A descriptor of a primary or secondary DbEntity for a given persistent class during - * commit. - * - * @since 3.0 - * @deprecated since 4.2 as part of deprecated {@link LegacyDataDomainFlushAction} - */ -@Deprecated -class DbEntityClassDescriptor { - - private ClassDescriptor classDescriptor; - private List<DbRelationship> pathFromMaster; - private DbEntity dbEntity; - - DbEntityClassDescriptor(ClassDescriptor classDescriptor) { - this.classDescriptor = classDescriptor; - this.dbEntity = classDescriptor.getEntity().getDbEntity(); - } - - DbEntityClassDescriptor(ClassDescriptor classDescriptor, ObjAttribute masterAttribute) { - this.classDescriptor = classDescriptor; - - Iterator<?> it = masterAttribute.getDbPathIterator(); - - if (masterAttribute.isFlattened()) { - - while (it.hasNext()) { - Object object = it.next(); - if (object instanceof DbRelationship) { - - if (pathFromMaster == null) { - pathFromMaster = new ArrayList<>(1); - } - - pathFromMaster.add((DbRelationship) object); - } - else if (object instanceof DbAttribute) { - this.dbEntity = ((DbAttribute) object).getEntity(); - } - } - } - - if (dbEntity == null) { - dbEntity = classDescriptor.getEntity().getDbEntity(); - } - } - - DbEntityClassDescriptor(ClassDescriptor classDescriptor, ObjRelationship masterRelationship) { - if(masterRelationship.getDbRelationships().size() > 2) { - throw new CayenneRuntimeException("Only two step flattened relationships are supported, " + masterRelationship.getDbRelationshipPath()); - } - this.classDescriptor = classDescriptor; - DbRelationship pathRelationship = masterRelationship.getDbRelationships().get(0); - pathFromMaster = new ArrayList<>(1); - pathFromMaster.add(pathRelationship); - dbEntity = pathRelationship.getTargetEntity(); - } - - boolean isMaster() { - return pathFromMaster == null; - } - - ClassDescriptor getClassDescriptor() { - return classDescriptor; - } - - List<DbRelationship> getPathFromMaster() { - return pathFromMaster; - } - - DbEntity getDbEntity() { - return dbEntity; - } - - ObjEntity getEntity() { - return classDescriptor.getEntity(); - } -} diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/FlattenedArcKey.java b/cayenne-server/src/main/java/org/apache/cayenne/access/FlattenedArcKey.java deleted file mode 100644 index 64e410c1d..000000000 --- a/cayenne-server/src/main/java/org/apache/cayenne/access/FlattenedArcKey.java +++ /dev/null @@ -1,362 +0,0 @@ -/***************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - ****************************************************************/ - -package org.apache.cayenne.access; - -import org.apache.cayenne.CayenneRuntimeException; -import org.apache.cayenne.DataRow; -import org.apache.cayenne.ObjectId; -import org.apache.cayenne.access.DataDomainSyncBucket.PropagatedValueFactory; -import org.apache.cayenne.access.util.DefaultOperationObserver; -import org.apache.cayenne.dba.DbAdapter; -import org.apache.cayenne.dba.QuotingStrategy; -import org.apache.cayenne.dba.TypesMapping; -import org.apache.cayenne.map.DbAttribute; -import org.apache.cayenne.map.DbEntity; -import org.apache.cayenne.map.DbJoin; -import org.apache.cayenne.map.DbRelationship; -import org.apache.cayenne.map.ObjRelationship; -import org.apache.cayenne.query.Query; -import org.apache.cayenne.query.SQLTemplate; -import org.apache.cayenne.util.HashCodeBuilder; -import org.apache.cayenne.util.Util; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * A holder of flattened relationship modification data. - * - * @since 1.2 - * @deprecated since 4.2 as part of deprecated {@link LegacyDataDomainFlushAction} - */ -@Deprecated -final class FlattenedArcKey { - - ObjRelationship relationship; - - DbArcId id1; - DbArcId id2; - - FlattenedArcKey(ObjectId sourceId, ObjectId destinationId, ObjRelationship relationship) { - - this.relationship = relationship; - - List<DbRelationship> dbRelationships = relationship.getDbRelationships(); - if (dbRelationships.size() != 2) { - throw new CayenneRuntimeException( - "Only single-step flattened relationships are supported in this operation, whereas the relationship '%s' has %s", - relationship, dbRelationships.size()); - } - - DbRelationship r1 = dbRelationships.get(0); - DbRelationship r2 = dbRelationships.get(1).getReverseRelationship(); - - if (r2 == null) { - throw new IllegalStateException("No reverse relationship for DbRelationship " + dbRelationships.get(1)); - } - - id1 = new DbArcId(sourceId, r1); - id2 = new DbArcId(destinationId, r2); - } - - /** - * Returns a join DbEntity for the single-step flattened relationship. - */ - DbEntity getJoinEntity() { - return id1.getEntity(); - } - - /** - * Returns a snapshot for join record for the single-step flattened - * relationship, generating value for the primary key column if it is not - * propagated via the relationships. - */ - Map<String, Object> buildJoinSnapshotForInsert(DataNode node) { - Map<String, Object> snapshot = lazyJoinSnapshot(); - - boolean autoPkDone = false; - DbEntity joinEntity = getJoinEntity(); - - for (DbAttribute dbAttr : joinEntity.getPrimaryKeys()) { - String dbAttrName = dbAttr.getName(); - if (snapshot.containsKey(dbAttrName)) { - continue; - } - - DbAdapter adapter = node.getAdapter(); - - // skip db-generated... looks like we don't care about the actual PK - // value here, so no need to retrieve db-generated pk back to Java. - if (adapter.supportsGeneratedKeys() && dbAttr.isGenerated()) { - continue; - } - - if (autoPkDone) { - throw new CayenneRuntimeException("Primary Key autogeneration only works for a single attribute."); - } - - // finally, use database generation mechanism - try { - Object pkValue = adapter.getPkGenerator().generatePk(node, dbAttr); - snapshot.put(dbAttrName, pkValue); - autoPkDone = true; - } catch (Exception ex) { - throw new CayenneRuntimeException("Error generating PK: %s", ex, ex.getMessage()); - } - } - - return snapshot; - } - - /** - * Returns pk snapshots for join records for the single-step flattened - * relationship. Multiple joins between the same pair of objects are - * theoretically possible, so the return value is a list. - */ - List buildJoinSnapshotsForDelete(DataNode node) { - Map<String, Object> snapshot = eagerJoinSnapshot(); - - DbEntity joinEntity = getJoinEntity(); - - boolean fetchKey = false; - for (DbAttribute dbAttr : joinEntity.getPrimaryKeys()) { - String dbAttrName = dbAttr.getName(); - if (!snapshot.containsKey(dbAttrName)) { - fetchKey = true; - break; - } - } - - if (!fetchKey) { - return Collections.singletonList(snapshot); - } - - // ok, the key is not included in snapshot, must do the fetch... - // TODO: this should be optimized in the future, but now - // DeleteBatchQuery - // expects a PK snapshot, so we must provide it. - - QuotingStrategy quoter = node.getAdapter().getQuotingStrategy(); - - StringBuilder sql = new StringBuilder("SELECT "); - Collection<DbAttribute> pk = joinEntity.getPrimaryKeys(); - final List<DbAttribute> pkList = pk instanceof List ? (List<DbAttribute>) pk : new ArrayList<>(pk); - - for (int i = 0; i < pkList.size(); i++) { - - if (i > 0) { - sql.append(", "); - } - - DbAttribute attribute = pkList.get(i); - - sql.append("#result('"); - sql.append(quoter.quotedName(attribute)); - - // since the name of the column can potentially be quoted and - // use reserved keywords as name, let's specify generated column - // name parameters to ensure the query doesn't explode - sql.append("' '").append(TypesMapping.getJavaBySqlType(attribute)); - sql.append("' '").append("pk").append(i); - sql.append("')"); - } - - sql.append(" FROM ").append(quoter.quotedFullyQualifiedName(joinEntity)).append(" WHERE "); - int i = snapshot.size(); - for (Object key : snapshot.keySet()) { - sql.append(quoter.quotedIdentifier(joinEntity, String.valueOf(key))).append(" #bindEqual($").append(key) - .append(")"); - - if (--i > 0) { - sql.append(" AND "); - } - } - - SQLTemplate query = new SQLTemplate(joinEntity.getDataMap(), sql.toString(), true); - query.setParams(snapshot); - - final List[] result = new List[1]; - - node.performQueries(Collections.singleton((Query) query), new DefaultOperationObserver() { - - @Override - public void nextRows(Query query, List<?> dataRows) { - - if (!dataRows.isEmpty()) { - // decode results... - - List<DataRow> fixedRows = new ArrayList<>(dataRows.size()); - for (Object o : dataRows) { - DataRow row = (DataRow) o; - - DataRow fixedRow = new DataRow(2); - - for (int i = 0; i < pkList.size(); i++) { - DbAttribute attribute = pkList.get(i); - fixedRow.put(attribute.getName(), row.get("pk" + i)); - } - - fixedRows.add(fixedRow); - } - - dataRows = fixedRows; - } - - result[0] = dataRows; - } - - @Override - public void nextQueryException(Query query, Exception ex) { - throw new CayenneRuntimeException("Raising from query exception.", Util.unwindException(ex)); - } - - @Override - public void nextGlobalException(Exception ex) { - throw new CayenneRuntimeException("Raising from underlyingQueryEngine exception.", Util - .unwindException(ex)); - } - }); - - return result[0]; - } - - @Override - public int hashCode() { - // order ids in array for hashcode consistency purposes. The actual - // order direction is not important, as long as it - // is consistent across invocations - - int compare = id1.getSourceId().getEntityName().compareTo(id2.getSourceId().getEntityName()); - - if (compare == 0) { - compare = id1.getIncominArc().getName().compareTo(id2.getIncominArc().getName()); - - if (compare == 0) { - // since ordering is mostly important for detecting equivalent - // FlattenedArc keys coming from 2 opposite directions, the name - // of ObjRelationship can be a good criteria - - ObjRelationship or2 = relationship.getReverseRelationship(); - compare = or2 != null ? relationship.getName().compareTo(or2.getName()) : 1; - - // TODO: if(compare == 0) ?? - } - } - - DbArcId[] ordered; - if (compare < 0) { - ordered = new DbArcId[] { id1, id2 }; - } else { - ordered = new DbArcId[] { id2, id1 }; - } - - return new HashCodeBuilder().append(ordered).toHashCode(); - } - - /** - * Defines equal based on whether the relationship is bidirectional. - */ - @Override - public boolean equals(Object object) { - - if (this == object) { - return true; - } - - if (!(object instanceof FlattenedArcKey)) { - return false; - } - - FlattenedArcKey key = (FlattenedArcKey) object; - - // ignore id order in comparison - if (id1.equals(key.id1)) { - return id2.equals(key.id2); - } else if (id1.equals(key.id2)) { - return id2.equals(key.id1); - } - - return false; - } - - private Map<String, Object> eagerJoinSnapshot() { - - List<DbRelationship> relList = relationship.getDbRelationships(); - if (relList.size() != 2) { - throw new CayenneRuntimeException( - "Only single-step flattened relationships are supported in this operation: %s", relationship); - } - - DbRelationship firstDbRel = relList.get(0); - DbRelationship secondDbRel = relList.get(1); - - // here ordering of ids is determined by 'relationship', so use id1, id2 - // instead of orderedIds - Map<String, ?> sourceId = id1.getSourceId().getIdSnapshot(); - Map<String, ?> destinationId = id2.getSourceId().getIdSnapshot(); - - Map<String, Object> snapshot = new HashMap<>(sourceId.size() + destinationId.size(), 1); - for (DbJoin join : firstDbRel.getJoins()) { - snapshot.put(join.getTargetName(), sourceId.get(join.getSourceName())); - } - - for (DbJoin join : secondDbRel.getJoins()) { - snapshot.put(join.getSourceName(), destinationId.get(join.getTargetName())); - } - - return snapshot; - } - - private Map<String, Object> lazyJoinSnapshot() { - - List<DbRelationship> relList = relationship.getDbRelationships(); - if (relList.size() != 2) { - throw new CayenneRuntimeException( - "Only single-step flattened relationships are supported in this operation: %s", relationship); - } - - DbRelationship firstDbRel = relList.get(0); - DbRelationship secondDbRel = relList.get(1); - - List<DbJoin> fromSourceJoins = firstDbRel.getJoins(); - List<DbJoin> toTargetJoins = secondDbRel.getJoins(); - - Map<String, Object> snapshot = new HashMap<>(fromSourceJoins.size() + toTargetJoins.size(), 1); - - // here ordering of ids is determined by 'relationship', so use id1, id2 - // instead of orderedIds - - for (DbJoin join : fromSourceJoins) { - Object value = new PropagatedValueFactory(id1.getSourceId(), join.getSourceName()); - snapshot.put(join.getTargetName(), value); - } - - for (DbJoin join : toTargetJoins) { - Object value = new PropagatedValueFactory(id2.getSourceId(), join.getTargetName()); - snapshot.put(join.getSourceName(), value); - } - - return snapshot; - } -} diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/LegacyDataDomainFlushAction.java b/cayenne-server/src/main/java/org/apache/cayenne/access/LegacyDataDomainFlushAction.java deleted file mode 100644 index a461f1393..000000000 --- a/cayenne-server/src/main/java/org/apache/cayenne/access/LegacyDataDomainFlushAction.java +++ /dev/null @@ -1,271 +0,0 @@ -/***************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - ****************************************************************/ - -package org.apache.cayenne.access; - -import org.apache.cayenne.CayenneRuntimeException; -import org.apache.cayenne.DataRow; -import org.apache.cayenne.ObjectId; -import org.apache.cayenne.PersistenceState; -import org.apache.cayenne.Persistent; -import org.apache.cayenne.access.flush.DefaultDataDomainFlushAction; -import org.apache.cayenne.graph.CompoundDiff; -import org.apache.cayenne.graph.GraphDiff; -import org.apache.cayenne.log.JdbcEventLogger; -import org.apache.cayenne.map.DbEntity; -import org.apache.cayenne.query.BatchQuery; -import org.apache.cayenne.query.Query; -import org.apache.cayenne.reflect.ClassDescriptor; -import org.apache.cayenne.tx.BaseTransaction; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; - -/** - * A stateful commit handler used by DataContext to perform commit operation. - * DataContextCommitAction resolves primary key dependencies, referential integrity - * dependencies (including multi-reflexive entities), generates primary keys, creates - * batches for massive data modifications, assigns operations to data nodes. - * - * @since 1.2 - * @deprecated this implementation is deprecated since 4.2, {@link DefaultDataDomainFlushAction} is used - */ -@Deprecated -public class LegacyDataDomainFlushAction implements org.apache.cayenne.access.flush.DataDomainFlushAction { - - private final DataDomain domain; - private DataContext context; - private Map<Object, ObjectDiff> changesByObjectId; - - private CompoundDiff resultDiff; - private Collection<ObjectId> resultDeletedIds; - private Map<ObjectId, DataRow> resultModifiedSnapshots; - private Collection<ObjectId> resultIndirectlyModifiedIds; - - private DataDomainInsertBucket insertBucket; - private DataDomainUpdateBucket updateBucket; - private DataDomainDeleteBucket deleteBucket; - private DataDomainFlattenedBucket flattenedBucket; - - private List<Query> queries; - - private JdbcEventLogger logger; - - public LegacyDataDomainFlushAction(DataDomain domain) { - this.domain = domain; - } - - DataDomain getDomain() { - return domain; - } - - DataContext getContext() { - return context; - } - - Collection<ObjectId> getResultDeletedIds() { - return resultDeletedIds; - } - - CompoundDiff getResultDiff() { - return resultDiff; - } - - Collection<ObjectId> getResultIndirectlyModifiedIds() { - return resultIndirectlyModifiedIds; - } - - Map<ObjectId, DataRow> getResultModifiedSnapshots() { - return resultModifiedSnapshots; - } - - public void setJdbcEventLogger(JdbcEventLogger logger) { - this.logger = logger; - } - - public JdbcEventLogger getJdbcEventLogger() { - return this.logger; - } - - ObjectDiff objectDiff(Object objectId) { - return changesByObjectId.get(objectId); - } - - void addFlattenedInsert(DbEntity flattenedEntity, FlattenedArcKey flattenedInsertInfo) { - flattenedBucket.addInsertArcKey(flattenedEntity, flattenedInsertInfo); - } - - void addFlattenedDelete(DbEntity flattenedEntity, FlattenedArcKey flattenedDeleteInfo) { - flattenedBucket.addFlattenedDelete(flattenedEntity, flattenedDeleteInfo); - } - - public GraphDiff flush(DataContext context, GraphDiff changes) { - - if (changes == null) { - return new CompoundDiff(); - } - - // TODO: Andrus, 3/13/2006 - support categorizing an arbitrary diff - if (!(changes instanceof ObjectStoreGraphDiff)) { - throw new IllegalArgumentException("Expected 'ObjectStoreGraphDiff', got: " + changes.getClass().getName()); - } - - this.context = context; - - // ObjectStoreGraphDiff contains changes already categorized by objectId... - this.changesByObjectId = ((ObjectStoreGraphDiff) changes).getChangesByObjectId(); - this.insertBucket = new DataDomainInsertBucket(this); - this.deleteBucket = new DataDomainDeleteBucket(this); - this.updateBucket = new DataDomainUpdateBucket(this); - this.flattenedBucket = new DataDomainFlattenedBucket(this); - - this.queries = new ArrayList<>(); - this.resultIndirectlyModifiedIds = new HashSet<>(); - - preprocess(context, changes); - - if (queries.isEmpty()) { - return new CompoundDiff(); - } - - this.resultDiff = new CompoundDiff(); - this.resultDeletedIds = new ArrayList<>(); - this.resultModifiedSnapshots = new HashMap<>(); - - runQueries(); - - postprocess(context); - return resultDiff; - } - - private void preprocess(DataContext context, GraphDiff changes) { - - // categorize dirty objects by state - - ObjectStore objectStore = context.getObjectStore(); - - for (Object o : changesByObjectId.keySet()) { - ObjectId id = (ObjectId) o; - Persistent object = (Persistent) objectStore.getNode(id); - ClassDescriptor descriptor = context.getEntityResolver().getClassDescriptor(id.getEntityName()); - - switch (object.getPersistenceState()) { - case PersistenceState.NEW: - insertBucket.addDirtyObject(object, descriptor); - break; - case PersistenceState.MODIFIED: - updateBucket.addDirtyObject(object, descriptor); - break; - case PersistenceState.DELETED: - deleteBucket.addDirtyObject(object, descriptor); - break; - } - } - - new DataDomainIndirectDiffBuilder(this).processIndirectChanges(changes); - - insertBucket.appendQueries(queries); - - // TODO: the following line depends on the "queries" collection filled by insertBucket.. Moreover it may - // potentially remove values from the passed collection. Replace with something with fewer unobvious - // side-effects... - flattenedBucket.appendInserts(queries); - - updateBucket.appendQueries(queries); - flattenedBucket.appendDeletes(queries); - deleteBucket.appendQueries(queries); - } - - private void runQueries() { - DataDomainFlushObserver observer = new DataDomainFlushObserver( - domain.getJdbcEventLogger()); - - // split query list by spanned nodes and run each single node range individually. - // Since connections are reused per node within an open transaction, there should - // not be much overhead in accessing the same node multiple times (may happen due - // to imperfect sorting) - - try { - - DataNode lastNode = null; - DbEntity lastEntity = null; - int rangeStart = 0; - int len = queries.size(); - - for (int i = 0; i < len; i++) { - - BatchQuery query = (BatchQuery) queries.get(i); - if (query.getDbEntity() != lastEntity) { - lastEntity = query.getDbEntity(); - - DataNode node = domain.lookupDataNode(lastEntity.getDataMap()); - if (node != lastNode) { - - if (i - rangeStart > 0) { - lastNode.performQueries(queries.subList(rangeStart, i), observer); - } - - rangeStart = i; - lastNode = node; - } - } - } - - // process last segment of the query list... - lastNode.performQueries(queries.subList(rangeStart, len), observer); - } catch (Throwable th) { - BaseTransaction.getThreadTransaction().setRollbackOnly(); - throw new CayenneRuntimeException("Transaction was rolledback.", th); - } - } - - /* - * Sends notification of changes to the DataRowStore, returns GraphDiff with replaced - * ObjectIds. - */ - private void postprocess(DataContext context) { - - deleteBucket.postprocess(); - updateBucket.postprocess(); - insertBucket.postprocess(); - - // notify cache... - if (!resultDeletedIds.isEmpty() - || !resultModifiedSnapshots.isEmpty() - || !resultIndirectlyModifiedIds.isEmpty()) { - - context - .getObjectStore() - .getDataRowCache() - .processSnapshotChanges( - context.getObjectStore(), - resultModifiedSnapshots, - resultDeletedIds, - Collections.<ObjectId>emptyList(), - resultIndirectlyModifiedIds); - } - - context.getObjectStore().postprocessAfterCommit(resultDiff); - } -} diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/LegacyDataDomainFlushActionFactory.java b/cayenne-server/src/main/java/org/apache/cayenne/access/LegacyDataDomainFlushActionFactory.java deleted file mode 100644 index 5ac9b2c05..000000000 --- a/cayenne-server/src/main/java/org/apache/cayenne/access/LegacyDataDomainFlushActionFactory.java +++ /dev/null @@ -1,45 +0,0 @@ -/***************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - ****************************************************************/ - -package org.apache.cayenne.access; - -import org.apache.cayenne.access.flush.DataDomainFlushAction; -import org.apache.cayenne.access.flush.DataDomainFlushActionFactory; -import org.apache.cayenne.di.Inject; -import org.apache.cayenne.log.JdbcEventLogger; - -/** - * Factory for {@link LegacyDataDomainFlushAction}. - * A fallback factory to use deprecated implementation if absolutely needed. - * - * @since 4.2 - */ -@Deprecated -public class LegacyDataDomainFlushActionFactory implements DataDomainFlushActionFactory { - - @Inject - private JdbcEventLogger jdbcEventLogger; - - @Override - public DataDomainFlushAction createFlushAction(DataDomain dataDomain) { - LegacyDataDomainFlushAction action = new LegacyDataDomainFlushAction(dataDomain); - action.setJdbcEventLogger(jdbcEventLogger); - return action; - } -}