Guard rails to copy fields from parent entity using annotations. - #171
Guard rails to copy fields from parent entity using annotations.#171kanhapatro37 wants to merge 15 commits into
Conversation
|
|
|
|
|
||
| <T, R> P visit(Select<T, R> opContext); | ||
|
|
||
| default <T, R, U> P visit(SaveWithParent<T, R, U> opContext) { |
There was a problem hiding this comment.
🔴 Existing visitors compile via this default, then every LockedContext.save* dispatch throws here. Preserve legacy visit(Save) behavior and add a compatibility test.
| } | ||
|
|
||
| @Override | ||
| public <T, R, U> Void visit(SaveWithParent<T, R, U> opContext) { |
There was a problem hiding this comment.
🔴 This wrapper runs after copy wraps the saver, so addBucketId() executes first and reads a stale/default copied sharding key. Enforce copy → bucket calculation → save and test the full chain.
|
|
||
| private void validateField(VariableElement childField) { | ||
| TypeElement childClass = (TypeElement) childField.getEnclosingElement(); | ||
| ParentEntity parentAnn = childClass.getAnnotation(ParentEntity.class); |
There was a problem hiding this comment.
🔴 Runtime inherits @ParentEntity, but the processor checks only the direct class here. Walk the superclass hierarchy and add a subclass-field compile test.
| // 3. Type mismatch check | ||
| TypeMirror parentFieldType = parentField.asType(); | ||
| TypeMirror childFieldType = childField.asType(); | ||
| if (!processingEnv.getTypeUtils().isAssignable(parentFieldType, childFieldType)) { |
There was a problem hiding this comment.
🔴 Validation accepts static source/target and final target fields, but runtime MethodHandles cannot invoke or set them safely. Reject these modifiers and test each case.
|
|
||
| TransactionExecutionContext ctx = createContext(opContext); | ||
|
|
||
| IllegalStateException exception = assertThrows(IllegalStateException.class, |
There was a problem hiding this comment.
🟡 This bypasses the bundle/transaction chain with saver(e -> e) and apply(null), so it does not verify persistence or rollback. Add a registered-observer DB integration test.
| "}")); | ||
|
|
||
| Compilation compilation = Compiler.javac() | ||
| .withProcessors(new CopyFromParentProcessor()) |
There was a problem hiding this comment.
🟡 Explicit .withProcessors(...) proves validation logic, not packaged service discovery. Add a downstream compile test without explicitly installing the processor.
| } | ||
|
|
||
| // Copy from parent to child | ||
| m.childSetter.invoke(child, parentValue); |
There was a problem hiding this comment.
🟡 This mutates each field immediately; a later default-only violation leaves earlier fields changed. Validate all fields first, then apply mutations in a second pass.
| "io.appform.dropwizard.sharding.sharding.CopyFromParent", | ||
| "io.appform.dropwizard.sharding.sharding.ParentEntity" | ||
| }) | ||
| @SupportedSourceVersion(SourceVersion.RELEASE_11) |
There was a problem hiding this comment.
This processor is auto-registered via META-INF/services, so downstream javac loads it even when the new annotations are unused. I reproduced an unrelated Java 17 compilation failing under -Werror because this emits the RELEASE_11 less than -source 17 warning. Please override getSupportedSourceVersion() to return SourceVersion.latestSupported() and add a packaged-JAR service-discovery test on Java 17 with -Werror.



Summary
Adds a new
SaveWithParentOpContext that carries the parent entity reference during LockedContext.save() operations. The existing Save OpContext has no access to the parent, so any observer in the chain that needs the parent (e.g., CopyFromParentObserver to copy annotated fields from parent to child before persistence) cannot do so today. This new OpContext makes the parent available to observers without modifying the existing Save contract.Adds new annotations: @CopyFromParent / @ParentEntity to configure the source and destination fields to copy.
Also adds compile-time annotation processing to validate @CopyFromParent / @ParentEntity usage at build time, and a CopyFromParentUtils utility that:
Changes
New SaveWithParent<T, R, P> OpContext
LockedContext and DAO changes
@CopyFromParent and @ParentEntity annotations
CopyFromParentObserver and CopyFromParentPersistor
copyEnabled,mismatchDetectionEnabled,copyIfDefaultOnlyandmismatchListenerSupplierparameters.CopyFromParentUtils (MethodHandles-based)
Compile-time annotation processor (CopyFromParentProcessor)
Validates at compile time:
@Transient(both javax.persistence and jakarta.persistence)E2E testing:
mismatchDetectionEnabled=truecopyIfDefaultOnly=true.copyEnabled=true