From cbddce6703fd066701820eea7cdd86ba4de8e1a5 Mon Sep 17 00:00:00 2001 From: Mikhail Dzianishchyts Date: Wed, 23 Aug 2023 15:38:48 +0300 Subject: [PATCH] Implement parametrization of tasks with name of target ObjEntity --- .../link/move/runtime/task/ITaskService.java | 41 +++++- .../link/move/runtime/task/TaskService.java | 132 +++++++++++++----- .../task/create/CreateTargetMapper.java | 10 +- .../CreateOrUpdateTargetMapper.java | 20 ++- .../CreateOrUpdateTargetMatcher.java | 15 +- .../DefaultCreateOrUpdateBuilder.java | 47 ++++++- .../task/delete/DefaultDeleteBuilder.java | 40 +++++- .../move/runtime/task/delete/DeleteTask.java | 40 +++++- .../deleteall/DefaultDeleteAllBuilder.java | 6 +- .../runtime/task/deleteall/DeleteAllTask.java | 7 +- .../writer/ITargetPropertyWriterService.java | 1 + .../writer/TargetPropertyWriterFactory.java | 31 ++-- .../writer/TargetPropertyWriterService.java | 34 ++++- 13 files changed, 352 insertions(+), 72 deletions(-) diff --git a/link-move/src/main/java/com/nhl/link/move/runtime/task/ITaskService.java b/link-move/src/main/java/com/nhl/link/move/runtime/task/ITaskService.java index 8d7bf2aa..603b7a75 100644 --- a/link-move/src/main/java/com/nhl/link/move/runtime/task/ITaskService.java +++ b/link-move/src/main/java/com/nhl/link/move/runtime/task/ITaskService.java @@ -14,37 +14,76 @@ public interface ITaskService { * population of the target DB). * * @since 2.6 + * @see #create(String) */ CreateBuilder create(Class type); + /** + * Returns a builder of "create" ETL synchronization task. + * + * @since 3.0 + * @see #create(Class) + */ + CreateBuilder create(String objEntityName); + /** * Returns a builder of "create-or-update" ETL synchronization task. * * @since 1.3 + * @see #createOrUpdate(String) */ CreateOrUpdateBuilder createOrUpdate(Class type); + /** + * Returns a builder of "create-or-update" ETL synchronization task. + * + * @since 3.0 + * @see #createOrUpdate(Class) + */ + CreateOrUpdateBuilder createOrUpdate(String objEntityName); + /** * Returns a builder of target delete ETL synchronization task. * * @since 1.3 + * @see #delete(String) */ DeleteBuilder delete(Class type); + + /** + * Returns a builder of target delete ETL synchronization task. + * + * @since 3.0 + * @see #delete(Class) + */ + DeleteBuilder delete(String objEntityName); + /** * Returns a builder of target deleteAll ETL synchronization task. * * @since 3.0 + * @see #deleteAll(String) */ DeleteAllBuilder deleteAll(Class type); + /** + * Returns a builder of target deleteAll ETL synchronization task. + * + * @since 3.0 + * @see #deleteAll(Class) + */ + DeleteAllBuilder deleteAll(String objEntityName); + /** * @since 1.3 + * @see #extractSourceKeys(String) */ SourceKeysBuilder extractSourceKeys(Class type); /** * @since 1.4 + * @see #extractSourceKeys(Class) */ - SourceKeysBuilder extractSourceKeys(String targetEntityName); + SourceKeysBuilder extractSourceKeys(String objEntityName); } diff --git a/link-move/src/main/java/com/nhl/link/move/runtime/task/TaskService.java b/link-move/src/main/java/com/nhl/link/move/runtime/task/TaskService.java index c020afc0..587b012f 100644 --- a/link-move/src/main/java/com/nhl/link/move/runtime/task/TaskService.java +++ b/link-move/src/main/java/com/nhl/link/move/runtime/task/TaskService.java @@ -63,7 +63,6 @@ public TaskService( @Override public CreateBuilder create(Class type) { - ObjEntity entity = lookupEntity(type); TargetEntity targetEntity = targetEntityMap.get(entity); CreateTargetMerger merger = new CreateTargetMerger(writerService.getWriterFactory(type)); @@ -82,8 +81,26 @@ public CreateBuilder create(Class type) { } @Override - public CreateOrUpdateBuilder createOrUpdate(Class type) { + public CreateBuilder create(String objEntityName) { + ObjEntity entity = lookupEntity(objEntityName); + TargetEntity targetEntity = targetEntityMap.get(entity); + CreateTargetMerger merger = new CreateTargetMerger(writerService.getWriterFactory(objEntityName)); + FkResolver fkResolver = new FkResolver(targetEntity); + RowConverter rowConverter = new RowConverter(targetEntity, valueConverterFactory); + + return new DefaultCreateBuilder( + new CreateTargetMapper(objEntityName), + merger, + fkResolver, + rowConverter, + targetCayenneService, + extractorService, + tokenManager, + logger); + } + @Override + public CreateOrUpdateBuilder createOrUpdate(Class type) { ObjEntity entity = lookupEntity(type); TargetEntity targetEntity = targetEntityMap.get(entity); MapperBuilder mapperBuilder = new MapperBuilder(entity, targetEntity, keyAdapterFactory); @@ -103,51 +120,50 @@ public CreateOrUpdateBuilder createOrUpdate(Class type) { logger); } - protected ObjEntity lookupEntity(Class type) { - ObjEntity entity = targetCayenneService.entityResolver().getObjEntity(type); - if (entity == null) { - throw new LmRuntimeException("Java class " + type.getName() + " is not mapped in Cayenne"); - } - return entity; - } - @Override - public SourceKeysBuilder extractSourceKeys(Class type) { - ObjEntity targetEntity = targetCayenneService.entityResolver().getObjEntity(type); - return new DefaultSourceKeysBuilder( - targetEntityMap.get(targetEntity), + public CreateOrUpdateBuilder createOrUpdate(String objEntityName) { + ObjEntity entity = lookupEntity(objEntityName); + TargetEntity targetEntity = targetEntityMap.get(entity); + MapperBuilder mapperBuilder = new MapperBuilder(entity, targetEntity, keyAdapterFactory); + RowConverter rowConverter = new RowConverter(targetEntity, valueConverterFactory); + CreateOrUpdateTargetMerger merger = new CreateOrUpdateTargetMerger(writerService.getWriterFactory(objEntityName)); + FkResolver fkResolver = new FkResolver(targetEntity); + + return new DefaultCreateOrUpdateBuilder( + objEntityName, + merger, + fkResolver, + rowConverter, + targetCayenneService, extractorService, tokenManager, - keyAdapterFactory, - valueConverterFactory, + mapperBuilder, logger); } @Override - public SourceKeysBuilder extractSourceKeys(String targetEntityName) { - ObjEntity targetEntity = targetCayenneService.entityResolver().getObjEntity(targetEntityName); - return new DefaultSourceKeysBuilder( - targetEntityMap.get(targetEntity), - extractorService, + public DeleteBuilder delete(Class type) { + ObjEntity entity = lookupEntity(type); + TargetEntity targetEntity = targetEntityMap.get(entity); + MapperBuilder mapperBuilder = new MapperBuilder(entity, targetEntity, keyAdapterFactory); + + return new DefaultDeleteBuilder( + type, + targetCayenneService, tokenManager, - keyAdapterFactory, - valueConverterFactory, + this, + mapperBuilder, logger); } @Override - public DeleteBuilder delete(Class type) { - - ObjEntity entity = targetCayenneService.entityResolver().getObjEntity(type); - if (entity == null) { - throw new LmRuntimeException("Java class " + type.getName() + " is not mapped in Cayenne"); - } - + public DeleteBuilder delete(String objEntityName) { + ObjEntity entity = lookupEntity(objEntityName); TargetEntity targetEntity = targetEntityMap.get(entity); MapperBuilder mapperBuilder = new MapperBuilder(entity, targetEntity, keyAdapterFactory); return new DefaultDeleteBuilder( - type, + entity.getDbEntityName(), targetCayenneService, tokenManager, this, @@ -160,10 +176,62 @@ public DeleteAllBuilder deleteAll(Class type) { ObjEntity entity = lookupEntity(type); return new DefaultDeleteAllBuilder( - type, targetCayenneService, tokenManager, entity.getDbEntity(), logger); } + + @Override + public DeleteAllBuilder deleteAll(String objEntityName) { + ObjEntity entity = lookupEntity(objEntityName); + + return new DefaultDeleteAllBuilder( + targetCayenneService, + tokenManager, + entity.getDbEntity(), + logger); + } + + @Override + public SourceKeysBuilder extractSourceKeys(Class type) { + ObjEntity targetEntity = lookupEntity(type); + + return new DefaultSourceKeysBuilder( + targetEntityMap.get(targetEntity), + extractorService, + tokenManager, + keyAdapterFactory, + valueConverterFactory, + logger); + } + + @Override + public SourceKeysBuilder extractSourceKeys(String objEntityName) { + ObjEntity targetEntity = lookupEntity(objEntityName); + + return new DefaultSourceKeysBuilder( + targetEntityMap.get(targetEntity), + extractorService, + tokenManager, + keyAdapterFactory, + valueConverterFactory, + logger); + } + + protected ObjEntity lookupEntity(Class type) { + ObjEntity entity = targetCayenneService.entityResolver().getObjEntity(type); + if (entity == null) { + throw new LmRuntimeException("Java class " + type.getName() + " is not mapped in Cayenne"); + } + return entity; + } + + protected ObjEntity lookupEntity(String objEntityName) { + ObjEntity entity = targetCayenneService.entityResolver().getObjEntity(objEntityName); + if (entity == null) { + throw new LmRuntimeException("ObjEntity " + objEntityName + " was not found in Cayenne"); + } + return entity; + } } diff --git a/link-move/src/main/java/com/nhl/link/move/runtime/task/create/CreateTargetMapper.java b/link-move/src/main/java/com/nhl/link/move/runtime/task/create/CreateTargetMapper.java index 2836c155..8783b89e 100644 --- a/link-move/src/main/java/com/nhl/link/move/runtime/task/create/CreateTargetMapper.java +++ b/link-move/src/main/java/com/nhl/link/move/runtime/task/create/CreateTargetMapper.java @@ -5,6 +5,7 @@ import com.nhl.dflib.row.RowBuilder; import com.nhl.dflib.row.RowProxy; import org.apache.cayenne.ObjectContext; +import org.apache.cayenne.access.DataContext; /** * @since 2.6 @@ -12,9 +13,16 @@ public class CreateTargetMapper { private final Class type; + private final String objEntityName; public CreateTargetMapper(Class type) { this.type = type; + this.objEntityName = null; + } + + public CreateTargetMapper(String objEntityName) { + this.type = null; + this.objEntityName = objEntityName; } public DataFrame map(ObjectContext cayenneContext, DataFrame sources) { @@ -34,6 +42,6 @@ protected Object create(ObjectContext context) { // Note that "context.newObject" is an impure function. Though we don't see its undesired side effects on // multiple iterations due to DataFrame "materialized" feature that transparently caches the results.. - return context.newObject(type); + return type != null ? context.newObject(type) : ((DataContext) context).newObject(objEntityName); } } diff --git a/link-move/src/main/java/com/nhl/link/move/runtime/task/createorupdate/CreateOrUpdateTargetMapper.java b/link-move/src/main/java/com/nhl/link/move/runtime/task/createorupdate/CreateOrUpdateTargetMapper.java index 9230b900..e25c9e6c 100644 --- a/link-move/src/main/java/com/nhl/link/move/runtime/task/createorupdate/CreateOrUpdateTargetMapper.java +++ b/link-move/src/main/java/com/nhl/link/move/runtime/task/createorupdate/CreateOrUpdateTargetMapper.java @@ -5,18 +5,29 @@ import com.nhl.dflib.builder.BoolAccum; import com.nhl.link.move.mapper.Mapper; import org.apache.cayenne.ObjectContext; +import org.apache.cayenne.access.DataContext; /** * @since 2.6 */ public class CreateOrUpdateTargetMapper { - private Class type; - private Mapper mapper; + private final Class type; + private final String objEntityName; + private final Mapper mapper; public CreateOrUpdateTargetMapper(Class type, Mapper mapper) { + this(type, null ,mapper); + } + + public CreateOrUpdateTargetMapper(String objEntityName, Mapper mapper) { + this(null, objEntityName, mapper); + } + + protected CreateOrUpdateTargetMapper(Class type, String objEntityName, Mapper mapper) { this.mapper = mapper; this.type = type; + this.objEntityName = objEntityName; } public DataFrame map( @@ -45,6 +56,9 @@ private Object createIfMissing(Object v, ObjectContext context) { // Note that "context.newObject" is an impure function. Though we don't see its undesired side effects on // multiple iterations due to DataFrame "materialized" feature that transparently caches the results.. - return v != null ? v : context.newObject(type); + if (v != null) { + return v; + } + return type != null ? context.newObject(type) : ((DataContext) context).newObject(objEntityName); } } diff --git a/link-move/src/main/java/com/nhl/link/move/runtime/task/createorupdate/CreateOrUpdateTargetMatcher.java b/link-move/src/main/java/com/nhl/link/move/runtime/task/createorupdate/CreateOrUpdateTargetMatcher.java index 3de3f4e8..6ca8d11f 100644 --- a/link-move/src/main/java/com/nhl/link/move/runtime/task/createorupdate/CreateOrUpdateTargetMatcher.java +++ b/link-move/src/main/java/com/nhl/link/move/runtime/task/createorupdate/CreateOrUpdateTargetMatcher.java @@ -19,11 +19,21 @@ public class CreateOrUpdateTargetMatcher { private final Class type; + private final String objEntityName; private final Mapper mapper; private final Index index; public CreateOrUpdateTargetMatcher(Class type, Mapper mapper) { + this(type, null, mapper); + } + + public CreateOrUpdateTargetMatcher(String objEntityName, Mapper mapper) { + this(null, objEntityName, mapper); + } + + protected CreateOrUpdateTargetMatcher(Class type, String objEntityName, Mapper mapper) { this.type = type; + this.objEntityName = objEntityName; this.mapper = mapper; this.index = Index.forLabels(CreateOrUpdateSegment.TARGET_COLUMN); } @@ -38,7 +48,10 @@ public DataFrame match(ObjectContext context, DataFrame df) { if (expressions.isEmpty()) { return DataFrame.empty(index); } else { - List objects = ObjectSelect.query(type).where(ExpressionFactory.or(expressions.values())).select(context); + List objects = + (type != null ? ObjectSelect.query(type) : ObjectSelect.query(Object.class, objEntityName)) + .where(ExpressionFactory.or(expressions.values())) + .select(context); return DataFrame.byColumn(index).of(Series.ofIterable(objects)); } } diff --git a/link-move/src/main/java/com/nhl/link/move/runtime/task/createorupdate/DefaultCreateOrUpdateBuilder.java b/link-move/src/main/java/com/nhl/link/move/runtime/task/createorupdate/DefaultCreateOrUpdateBuilder.java index 911b1b06..32b33c4f 100644 --- a/link-move/src/main/java/com/nhl/link/move/runtime/task/createorupdate/DefaultCreateOrUpdateBuilder.java +++ b/link-move/src/main/java/com/nhl/link/move/runtime/task/createorupdate/DefaultCreateOrUpdateBuilder.java @@ -30,6 +30,7 @@ public class DefaultCreateOrUpdateBuilder extends BaseTaskBuilder implements CreateOrUpdateBuilder { private final Class type; + private final String objEntityName; private final CreateOrUpdateTargetMerger merger; private final IExtractorService extractorService; private final ITargetCayenneService targetCayenneService; @@ -51,10 +52,40 @@ public DefaultCreateOrUpdateBuilder( ITokenManager tokenManager, MapperBuilder mapperBuilder, LmLogger logger) { + this(type, null, merger, fkResolver, rowConverter, targetCayenneService, extractorService, + tokenManager, mapperBuilder, logger); + } + + public DefaultCreateOrUpdateBuilder( + String objEntityName, + CreateOrUpdateTargetMerger merger, + FkResolver fkResolver, + RowConverter rowConverter, + ITargetCayenneService targetCayenneService, + IExtractorService extractorService, + ITokenManager tokenManager, + MapperBuilder mapperBuilder, + LmLogger logger) { + this(null, objEntityName, merger, fkResolver, rowConverter, targetCayenneService, extractorService, + tokenManager, mapperBuilder, logger); + } + + protected DefaultCreateOrUpdateBuilder( + Class type, + String objEntityName, + CreateOrUpdateTargetMerger merger, + FkResolver fkResolver, + RowConverter rowConverter, + ITargetCayenneService targetCayenneService, + IExtractorService extractorService, + ITokenManager tokenManager, + MapperBuilder mapperBuilder, + LmLogger logger) { super(logger); this.type = type; + this.objEntityName = objEntityName; this.merger = merger; this.fkResolver = fkResolver; this.targetCayenneService = targetCayenneService; @@ -141,9 +172,12 @@ public LmTask task() throws IllegalStateException { } private CreateOrUpdateSegmentProcessor createProcessor() { - Mapper mapper = this.mapper != null ? this.mapper : mapperBuilder.build(); + return type != null ? createProcessor(type, mapper) : createProcessor(objEntityName, mapper); + } + + private CreateOrUpdateSegmentProcessor createProcessor(Class type, Mapper mapper) { return new CreateOrUpdateSegmentProcessor( rowConverter, new SourceMapper(mapper), @@ -153,4 +187,15 @@ private CreateOrUpdateSegmentProcessor createProcessor() { fkResolver, getCallbackExecutor()); } + + private CreateOrUpdateSegmentProcessor createProcessor(String objEntityName, Mapper mapper) { + return new CreateOrUpdateSegmentProcessor( + rowConverter, + new SourceMapper(mapper), + new CreateOrUpdateTargetMatcher(objEntityName, mapper), + new CreateOrUpdateTargetMapper(objEntityName, mapper), + merger, + fkResolver, + getCallbackExecutor()); + } } diff --git a/link-move/src/main/java/com/nhl/link/move/runtime/task/delete/DefaultDeleteBuilder.java b/link-move/src/main/java/com/nhl/link/move/runtime/task/delete/DefaultDeleteBuilder.java index 7993bd50..f800922f 100644 --- a/link-move/src/main/java/com/nhl/link/move/runtime/task/delete/DefaultDeleteBuilder.java +++ b/link-move/src/main/java/com/nhl/link/move/runtime/task/delete/DefaultDeleteBuilder.java @@ -29,6 +29,7 @@ public class DefaultDeleteBuilder extends BaseTaskBuilder type; + private final String dbEntityName; private final MapperBuilder mapperBuilder; private Expression targetFilter; @@ -42,6 +43,27 @@ public DefaultDeleteBuilder( ITaskService taskService, MapperBuilder mapperBuilder, LmLogger logger) { + this(type, null, targetCayenneService, tokenManager, taskService, mapperBuilder, logger); + } + + public DefaultDeleteBuilder( + String dbEntityName, + ITargetCayenneService targetCayenneService, + ITokenManager tokenManager, + ITaskService taskService, + MapperBuilder mapperBuilder, + LmLogger logger) { + this(null, dbEntityName, targetCayenneService, tokenManager, taskService, mapperBuilder, logger); + } + + protected DefaultDeleteBuilder( + Class type, + String dbEntityName, + ITargetCayenneService targetCayenneService, + ITokenManager tokenManager, + ITaskService taskService, + MapperBuilder mapperBuilder, + LmLogger logger) { super(logger); @@ -49,6 +71,7 @@ public DefaultDeleteBuilder( this.taskService = taskService; this.targetCayenneService = targetCayenneService; this.type = type; + this.dbEntityName = dbEntityName; this.mapperBuilder = mapperBuilder; setupStatsCallbacks(); @@ -111,9 +134,12 @@ public DefaultDeleteBuilder matchById() { @Override public DeleteTask task() throws IllegalStateException { - Mapper mapper = this.mapper != null ? this.mapper : mapperBuilder.build(); + return type != null ? createTask(type, mapper) : createTask(dbEntityName, mapper); + } + + protected DeleteTask createTask(Class type, Mapper mapper) throws IllegalStateException { return new DeleteTask( batchSize, type, @@ -125,6 +151,18 @@ public DeleteTask task() throws IllegalStateException { logger); } + protected DeleteTask createTask(String dbEntityName, Mapper mapper) throws IllegalStateException { + return new DeleteTask( + batchSize, + dbEntityName, + targetFilter, + targetCayenneService, + tokenManager, + createSourceKeysSubtask(mapper), + createProcessor(mapper), + logger); + } + private LmTask createSourceKeysSubtask(Mapper mapper) { return taskService.extractSourceKeys(type).sourceExtractor(extractorName).matchBy(mapper).task(); } diff --git a/link-move/src/main/java/com/nhl/link/move/runtime/task/delete/DeleteTask.java b/link-move/src/main/java/com/nhl/link/move/runtime/task/delete/DeleteTask.java index 7e44291c..8ddb648e 100644 --- a/link-move/src/main/java/com/nhl/link/move/runtime/task/delete/DeleteTask.java +++ b/link-move/src/main/java/com/nhl/link/move/runtime/task/delete/DeleteTask.java @@ -13,7 +13,6 @@ import com.nhl.link.move.runtime.task.sourcekeys.SourceKeysTask; import com.nhl.link.move.runtime.token.ITokenManager; import org.apache.cayenne.ObjectContext; -import org.apache.cayenne.Persistent; import org.apache.cayenne.ResultIterator; import org.apache.cayenne.exp.Expression; import org.apache.cayenne.query.ObjectSelect; @@ -29,6 +28,7 @@ public class DeleteTask extends BaseTask { private final Class type; + private final String dbEntityName; private final Expression targetFilter; private final LmTask sourceKeysSubtask; private final DeleteSegmentProcessor processor; @@ -43,6 +43,33 @@ public DeleteTask( LmTask sourceKeysSubtask, DeleteSegmentProcessor processor, LmLogger logger) { + this(batchSize, type, null, targetFilter, targetCayenneService, tokenManager, sourceKeysSubtask, + processor, logger); + } + + public DeleteTask( + int batchSize, + String dbEntityName, + Expression targetFilter, + ITargetCayenneService targetCayenneService, + ITokenManager tokenManager, + LmTask sourceKeysSubtask, + DeleteSegmentProcessor processor, + LmLogger logger) { + this(batchSize, null, dbEntityName, targetFilter, targetCayenneService, tokenManager, sourceKeysSubtask, + processor, logger); + } + + protected DeleteTask( + int batchSize, + Class type, + String dbEntityName, + Expression targetFilter, + ITargetCayenneService targetCayenneService, + ITokenManager tokenManager, + LmTask sourceKeysSubtask, + DeleteSegmentProcessor processor, + LmLogger logger) { // extractor is not used by the "delete" task, only by its key extraction subtask, // so set it to null @@ -50,6 +77,7 @@ public DeleteTask( super(null, batchSize, tokenManager, logger); this.type = type; + this.dbEntityName = dbEntityName; this.targetFilter = targetFilter; this.targetCayenneService = targetCayenneService; this.sourceKeysSubtask = sourceKeysSubtask; @@ -69,7 +97,7 @@ protected void doRun(Execution exec) { // do not preload the keys if there are no target objects Set keys = data.hasNextRow() ? loadKeys(exec) : Collections.emptySet(); - BatchProcessor batchProcessor = createBatchProcessor(exec, keys); + BatchProcessor batchProcessor = createBatchProcessor(exec, keys); createBatchRunner(batchProcessor).run(data); } } @@ -93,18 +121,18 @@ protected void onExecFinished(Execution exec) { protected ResultIterator createTargetSelect(Execution exec) { exec.getLogger().targetFilterApplied(targetFilter); - ObjectSelect query = ObjectSelect.query(type).where(targetFilter); + ObjectSelect query = (type != null ? ObjectSelect.query(type): ObjectSelect.dbQuery(dbEntityName)) + .where(targetFilter); return targetCayenneService.newContext().iterator(query); } - protected BatchProcessor createBatchProcessor(Execution exec, Set keys) { - + protected BatchProcessor createBatchProcessor(Execution exec, Set keys) { + ObjectContext context = targetCayenneService.newContext(); Index columns = Index.forLabels(DeleteSegment.TARGET_COLUMN); return rows -> { // executing in the select context - ObjectContext context = rows.get(0).getObjectContext(); DeleteSegment segment = new DeleteSegment(context) .setTargets(DataFrame.byColumn(columns).of(Series.ofIterable(rows))) .setSourceKeys(keys); diff --git a/link-move/src/main/java/com/nhl/link/move/runtime/task/deleteall/DefaultDeleteAllBuilder.java b/link-move/src/main/java/com/nhl/link/move/runtime/task/deleteall/DefaultDeleteAllBuilder.java index 1ffdabf3..8fb6c3c0 100644 --- a/link-move/src/main/java/com/nhl/link/move/runtime/task/deleteall/DefaultDeleteAllBuilder.java +++ b/link-move/src/main/java/com/nhl/link/move/runtime/task/deleteall/DefaultDeleteAllBuilder.java @@ -19,14 +19,12 @@ public class DefaultDeleteAllBuilder extends BaseTaskBuilder type; private final DbEntity dbEntity; private Expression targetFilter; private boolean skipExecutionStats = false; public DefaultDeleteAllBuilder( - Class type, ITargetCayenneService targetCayenneService, ITokenManager tokenManager, DbEntity dbEntity, @@ -36,7 +34,6 @@ public DefaultDeleteAllBuilder( this.tokenManager = tokenManager; this.targetCayenneService = targetCayenneService; - this.type = type; this.dbEntity = dbEntity; } @@ -60,8 +57,7 @@ public DefaultDeleteAllBuilder skipExecutionStats() { @Override public DeleteAllTask task() throws IllegalStateException { - return new DeleteAllTask(type, - targetFilter, + return new DeleteAllTask(targetFilter, targetCayenneService, tokenManager, dbEntity, diff --git a/link-move/src/main/java/com/nhl/link/move/runtime/task/deleteall/DeleteAllTask.java b/link-move/src/main/java/com/nhl/link/move/runtime/task/deleteall/DeleteAllTask.java index 0f8514b2..ec9030dc 100644 --- a/link-move/src/main/java/com/nhl/link/move/runtime/task/deleteall/DeleteAllTask.java +++ b/link-move/src/main/java/com/nhl/link/move/runtime/task/deleteall/DeleteAllTask.java @@ -53,14 +53,12 @@ public class DeleteAllTask extends BaseTask { SybaseAdapter.class ); - private final Class type; private final Expression targetFilter; private final DbEntity dbEntity; private final boolean skipExecutionStats; private final ITargetCayenneService targetCayenneService; public DeleteAllTask( - Class type, Expression targetFilter, ITargetCayenneService targetCayenneService, ITokenManager tokenManager, @@ -71,7 +69,6 @@ public DeleteAllTask( //"deleteAll" task doesn't use extractor and doesn't support batches super(null, 0, tokenManager, logger); - this.type = type; this.targetFilter = targetFilter; this.targetCayenneService = targetCayenneService; this.dbEntity = dbEntity; @@ -105,7 +102,7 @@ private void deleteWithExpression(ObjectContext context, Execution exec) { exec.getLogger().targetFilterApplied(targetFilter); - StringBuilder queryBuilder = new StringBuilder("DELETE FROM " + type.getName() + " e WHERE "); + StringBuilder queryBuilder = new StringBuilder("DELETE FROM " + dbEntity.getName() + " e WHERE "); try { targetFilter.appendAsEJBQL(queryBuilder, "e"); @@ -141,7 +138,7 @@ private void deleteAllData(ObjectContext context, Execution execution) { */ private Optional prefetchCountIfRequired(DbAdapter adapter, ObjectContext context) { if (!skipExecutionStats && TRUNCATE_AWARE_ADAPTERS.contains(adapter.getClass())) { - return Optional.of(ObjectSelect.query(type).count().select(context).get(0)); + return Optional.of(ObjectSelect.dbQuery(dbEntity.getName()).count().select(context).get(0)); } else { return Optional.empty(); } diff --git a/link-move/src/main/java/com/nhl/link/move/writer/ITargetPropertyWriterService.java b/link-move/src/main/java/com/nhl/link/move/writer/ITargetPropertyWriterService.java index 5be48a71..f646bb58 100644 --- a/link-move/src/main/java/com/nhl/link/move/writer/ITargetPropertyWriterService.java +++ b/link-move/src/main/java/com/nhl/link/move/writer/ITargetPropertyWriterService.java @@ -6,4 +6,5 @@ public interface ITargetPropertyWriterService { TargetPropertyWriterFactory getWriterFactory(Class type); + TargetPropertyWriterFactory getWriterFactory(String objEntityName); } diff --git a/link-move/src/main/java/com/nhl/link/move/writer/TargetPropertyWriterFactory.java b/link-move/src/main/java/com/nhl/link/move/writer/TargetPropertyWriterFactory.java index 0c43ab91..f69e2baa 100644 --- a/link-move/src/main/java/com/nhl/link/move/writer/TargetPropertyWriterFactory.java +++ b/link-move/src/main/java/com/nhl/link/move/writer/TargetPropertyWriterFactory.java @@ -7,6 +7,7 @@ import org.apache.cayenne.map.DbRelationship; import org.apache.cayenne.map.ObjEntity; import org.apache.cayenne.reflect.AttributeProperty; +import org.apache.cayenne.reflect.ClassDescriptor; import org.apache.cayenne.reflect.ToOneProperty; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,12 +31,16 @@ public class TargetPropertyWriterFactory { private static final Logger LOGGER = LoggerFactory.getLogger(TargetPropertyWriterFactory.class); private final Class type; - private final ObjEntity entity; + private final ClassDescriptor classDescriptor; private final Map writers = new ConcurrentHashMap<>(); - public TargetPropertyWriterFactory(Class type, ObjEntity entity) { + public TargetPropertyWriterFactory(ClassDescriptor classDescriptor) { + this(null, classDescriptor); + } + + public TargetPropertyWriterFactory(Class type, ClassDescriptor classDescriptor) { this.type = type; - this.entity = entity; + this.classDescriptor = classDescriptor; } private static String getSetterName(String propertyName) { @@ -45,7 +50,7 @@ private static String getSetterName(String propertyName) { // TODO: this and other protected methods are called by another class - TargetPropertyWriterService... Refactor.. protected void initPkWriter(DbAttribute pkAttribute) { - if (!entity.getDbEntity().equals(pkAttribute.getEntity())) { + if (!getEntity().getDbEntity().equals(pkAttribute.getEntity())) { throw new LmRuntimeException("Attribute belongs to different entity: " + pkAttribute.getName()); } @@ -58,7 +63,7 @@ protected void initPkWriter(DbAttribute pkAttribute) { protected void initWriter(AttributeProperty property) { - if (!entity.equals(property.getAttribute().getEntity())) { + if (!getEntity().equals(property.getAttribute().getEntity())) { throw new LmRuntimeException("Property belongs to a different entity: " + property.getName()); } @@ -71,7 +76,7 @@ protected void initWriter(AttributeProperty property) { protected void initWriter(ToOneProperty property) { - if (!entity.equals(property.getRelationship().getSourceEntity())) { + if (!getEntity().equals(property.getRelationship().getSourceEntity())) { throw new LmRuntimeException("Property belongs to a different entity: " + property.getName()); } @@ -98,8 +103,8 @@ protected void initWriter(ToOneProperty property) { ); } - public TargetPropertyWriter getOrCreateWriter(String property) { - return getOrCreateWriter(property, property, () -> NULL_WRITER); + public TargetPropertyWriter getOrCreateWriter(String propertyName) { + return getOrCreateWriter(propertyName, propertyName, () -> NULL_WRITER); } public TargetPropertyWriter getOrCreateWriter( @@ -113,7 +118,7 @@ public TargetPropertyWriter getOrCreateWriter( private TargetPropertyWriter createWriter(String propertyName, Supplier defaultWriterSupplier) { // TODO: setter lookup does not check for the value type. E.g. a setter for to-one relationship will not - // work properly if the value is presented as an FK (e.g. an "int" or a "long"). + // work properly if the value is presented as an FK (e.g. an "int" or a "long"). Method setter = getSetter(propertyName); if (setter != null) { @@ -128,8 +133,14 @@ private TargetPropertyWriter createWriter(String propertyName, Supplier "Null property writer for " + propertyName); } - private Method getSetter(String propertyName) { + protected ObjEntity getEntity() { + return classDescriptor.getEntity(); + } + private Method getSetter(String propertyName) { + if (type == null) { + return null; + } Method setter = null; String setterName = getSetterName(propertyName); for (Method m : type.getDeclaredMethods()) { diff --git a/link-move/src/main/java/com/nhl/link/move/writer/TargetPropertyWriterService.java b/link-move/src/main/java/com/nhl/link/move/writer/TargetPropertyWriterService.java index 80ea13a2..8c20dc8d 100644 --- a/link-move/src/main/java/com/nhl/link/move/writer/TargetPropertyWriterService.java +++ b/link-move/src/main/java/com/nhl/link/move/writer/TargetPropertyWriterService.java @@ -19,7 +19,7 @@ public class TargetPropertyWriterService implements ITargetPropertyWriterService { private final ITargetCayenneService targetCayenneService; - private final ConcurrentMap, TargetPropertyWriterFactory> writerFactories; + private final ConcurrentMap writerFactories; public TargetPropertyWriterService(@Inject ITargetCayenneService targetCayenneService) { this.targetCayenneService = targetCayenneService; @@ -28,7 +28,12 @@ public TargetPropertyWriterService(@Inject ITargetCayenneService targetCayenneSe @Override public TargetPropertyWriterFactory getWriterFactory(Class type) { - return writerFactories.computeIfAbsent(type, this::createWriterFactory); + return writerFactories.computeIfAbsent(type, t -> createWriterFactory(((Class) t))); + } + + @Override + public TargetPropertyWriterFactory getWriterFactory(String objEntityName) { + return writerFactories.computeIfAbsent(objEntityName, name -> createWriterFactory(((String) name))); } private TargetPropertyWriterFactory createWriterFactory(Class type) { @@ -38,12 +43,31 @@ private TargetPropertyWriterFactory createWriterFactory(Class type) { throw new LmRuntimeException("Java class " + type.getName() + " is not mapped in Cayenne"); } - TargetPropertyWriterFactory writerFactory = new TargetPropertyWriterFactory(type, entity); ClassDescriptor descriptor = targetCayenneService.entityResolver().getClassDescriptor(entity.getName()); + TargetPropertyWriterFactory writerFactory = new TargetPropertyWriterFactory(type, descriptor); + prepareWriterFactory(entity, descriptor, writerFactory); + + return writerFactory; + } + + private TargetPropertyWriterFactory createWriterFactory(String objEntityName) { + + ObjEntity entity = targetCayenneService.entityResolver().getObjEntity(objEntityName); + if (entity == null) { + throw new LmRuntimeException("ObjEntity " + objEntityName + " was not found in Cayenne"); + } + + ClassDescriptor descriptor = targetCayenneService.entityResolver().getClassDescriptor(entity.getName()); + TargetPropertyWriterFactory writerFactory = new TargetPropertyWriterFactory(descriptor); + prepareWriterFactory(entity, descriptor, writerFactory); + return writerFactory; + } + + private static void prepareWriterFactory(ObjEntity entity, ClassDescriptor descriptor, + TargetPropertyWriterFactory writerFactory) { // precompile all possible obj: and db: invariants // TODO: should we normalize the source map instead of doing this? - descriptor.visitProperties(new PropertyVisitor() { @Override @@ -68,7 +92,5 @@ public boolean visitToMany(ToManyProperty property) { }); entity.getDbEntity().getPrimaryKeys().forEach(writerFactory::initPkWriter); - - return writerFactory; } }