Skip to content

fix(repositories): add automatic flushing to delete methods in various repositories#791

Open
balazs-szucs wants to merge 2 commits intogrimmory-tools:developfrom
balazs-szucs:fix-flushing-stuff-again
Open

fix(repositories): add automatic flushing to delete methods in various repositories#791
balazs-szucs wants to merge 2 commits intogrimmory-tools:developfrom
balazs-szucs:fix-flushing-stuff-again

Conversation

@balazs-szucs
Copy link
Copy Markdown
Member

@balazs-szucs balazs-szucs commented Apr 23, 2026

Description

Linked Issue: Fixes #

Changes

This pull request refactors a number of repository classes to improve the handling of modifying queries in Spring Data JPA. The main focus is on ensuring that changes to the database are immediately synchronized with the persistence context and database by enabling both clearAutomatically and flushAutomatically in the @Modifying annotation. Additionally, several repository methods that previously relied on Spring Data's derived query methods for deletes have been converted to explicit JPQL @Query methods, often with added transactional annotations and parameter bindings for clarity and consistency.

Repository method improvements:

  • Added clearAutomatically = true, flushAutomatically = true to all @Modifying annotations on update and delete methods across repositories, ensuring the persistence context is kept in sync after modifying queries.
  • Converted several delete methods from derived query methods to explicit JPQL queries with @Query, @Modifying, and @Transactional for greater control and clarity. This includes methods such as deleteByBookIdAndUserId, deleteByUser, deleteByRevokedTrueAndCreatedAtBefore, and others.
  • Added missing imports for @Modifying, @Transactional, @Query, and @Param where needed to support the new method implementations.

Consistency and code clarity:

  • Ensured that all repositories use a consistent pattern for modifying and deleting queries, improving maintainability and reducing the risk of stale data in the persistence context. (all references above)
  • Removed unused or redundant query methods, such as a duplicate count query in MetadataFetchJobRepository.

Summary by CodeRabbit

  • Refactor

    • Improved backend bulk delete/update behavior and transaction handling for more reliable and consistent data operations.
    • Tuned automated cleanup processes for better stability during maintenance tasks.
  • Bug Fixes

    • Fixed a task’s reported pending cleanup count source for more accurate reporting.
    • Made user restriction updates more deterministic by adjusting the update ordering.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 23, 2026

📝 Walkthrough

Walkthrough

Systematically converts derived delete and update repository methods across multiple repositories to explicit JPQL queries or updates, adds/adjusts @Modifying(clearAutomatically = true, flushAutomatically = true) and @Transactional where applicable, and introduces explicit @Param bindings. Also swaps a countAll() call for count() in one task.

Changes

Cohort / File(s) Summary
Bulk delete conversions
backend/src/main/java/org/booklore/repository/AnnotationRepository.java, backend/src/main/java/org/booklore/repository/BookNoteV2Repository.java, backend/src/main/java/org/booklore/repository/ComicCreatorMappingRepository.java, backend/src/main/java/org/booklore/repository/PdfAnnotationRepository.java, backend/src/main/java/org/booklore/repository/RefreshTokenRepository.java, backend/src/main/java/org/booklore/repository/UserContentRestrictionRepository.java, backend/src/main/java/org/booklore/repository/UserEmailProviderPreferenceRepository.java
Replaces derived delete methods with explicit JPQL DELETE queries; adds @Query with named params, @Modifying(clearAutomatically = true, flushAutomatically = true), and @Transactional to each method.
Session delete conversions
backend/src/main/java/org/booklore/repository/OidcSessionRepository.java
Replaces derived deletes with explicit JPQL DELETE queries for time-based cleanup, adds named @Param, @Modifying(clearAutomatically = true, flushAutomatically = true), and @Transactional.
Modify @Modifying attributes on updates
backend/src/main/java/org/booklore/repository/BookMetadataRepository.java, backend/src/main/java/org/booklore/repository/EmailRecipientV2Repository.java
Existing JPQL UPDATE methods now use @Modifying(clearAutomatically = true, flushAutomatically = true); query logic unchanged.
Adjust @Modifying on deletes
backend/src/main/java/org/booklore/repository/BookdropFileRepository.java, backend/src/main/java/org/booklore/repository/KoboDeletedBookProgressRepository.java
Existing JPQL DELETE methods updated to @Modifying(clearAutomatically = true, flushAutomatically = true); signatures/queries unchanged.
Task count method change
backend/src/main/java/org/booklore/task/tasks/TempFetchedMetadataCleanupTask.java
Replaces MetadataFetchJobRepository.countAll() with MetadataFetchJobRepository.count() in getMetadata() output logic.
Service flow adjustment
backend/src/main/java/org/booklore/service/restriction/ContentRestrictionService.java
Changes operation order in updateRestrictions to delete existing restrictions before fetching the user entity; rest of flow unchanged.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

chore

Suggested reviewers

  • imajes
  • zachyale

Poem

🐰 I hopped through queries, tidy and neat,

Deleted by name with a rhythmic beat,
Params all lined up, transactions in tow,
Persistence refreshed — onward we go! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title follows the conventional commit format with 'fix' scope and a clear, descriptive subject about adding automatic flushing to repository delete methods.
Description check ✅ Passed The PR description includes all required template sections (Description, Linked Issue, Changes) with comprehensive details about the refactoring, repository method improvements, and consistency measures.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
✨ Simplify code
  • Create PR with simplified code

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
backend/src/main/java/org/booklore/repository/RefreshTokenRepository.java (1)

17-20: Consider matching refresh tokens by user ID instead of entity instance.

The new bulk delete uses t.user = :user, while the established user-delete pattern in UserContentRestrictionRepository.java:26-29 uses r.user.id = :userId. Matching by ID avoids entity-parameter binding in a bulk DML query and keeps user-scoped deletes consistent.

Optional consistency refactor
-    `@Query`("DELETE FROM RefreshTokenEntity t WHERE t.user = :user")
-    void deleteByUser(`@Param`("user") BookLoreUserEntity user);
+    `@Query`("DELETE FROM RefreshTokenEntity t WHERE t.user.id = :userId")
+    void deleteByUserId(`@Param`("userId") Long userId);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/src/main/java/org/booklore/repository/RefreshTokenRepository.java`
around lines 17 - 20, The deleteByUser bulk DML should match by the user's ID
rather than binding an entity instance: change the RefreshTokenRepository method
deleteByUser to accept a userId (e.g., Long userId) and update the `@Query` to
"DELETE FROM RefreshTokenEntity t WHERE t.user.id = :userId" with the
corresponding `@Param`("userId") to avoid entity-parameter binding and align with
the user-scoped delete pattern used elsewhere.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@backend/src/main/java/org/booklore/repository/UserContentRestrictionRepository.java`:
- Around line 26-34: The bulk-delete methods in UserContentRestrictionRepository
(deleteByUserId and deleteByUserIdAndRestrictionType) use
clearAutomatically=true which clears the persistence context and detaches the
BookLoreUserEntity loaded in ContentRestrictionService.updateRestrictions; to
fix, either remove clearAutomatically from these `@Modifying` annotations so the
persistence context is not cleared, or (if you must keep clearAutomatically)
reload the user entity (e.g., re-fetch via UserRepository.findById or
entityManager.find) inside ContentRestrictionService.updateRestrictions
immediately after calling deleteByUserId before constructing new
UserContentRestrictionEntity instances; update the repository annotations or add
the user reload in the service accordingly.

---

Nitpick comments:
In `@backend/src/main/java/org/booklore/repository/RefreshTokenRepository.java`:
- Around line 17-20: The deleteByUser bulk DML should match by the user's ID
rather than binding an entity instance: change the RefreshTokenRepository method
deleteByUser to accept a userId (e.g., Long userId) and update the `@Query` to
"DELETE FROM RefreshTokenEntity t WHERE t.user.id = :userId" with the
corresponding `@Param`("userId") to avoid entity-parameter binding and align with
the user-scoped delete pattern used elsewhere.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1991f872-6b4c-4178-8df2-35ef2eab63e0

📥 Commits

Reviewing files that changed from the base of the PR and between f21cf1a and 3ca9a79.

📒 Files selected for processing (13)
  • backend/src/main/java/org/booklore/repository/AnnotationRepository.java
  • backend/src/main/java/org/booklore/repository/BookMetadataRepository.java
  • backend/src/main/java/org/booklore/repository/BookNoteV2Repository.java
  • backend/src/main/java/org/booklore/repository/BookdropFileRepository.java
  • backend/src/main/java/org/booklore/repository/ComicCreatorMappingRepository.java
  • backend/src/main/java/org/booklore/repository/EmailRecipientV2Repository.java
  • backend/src/main/java/org/booklore/repository/KoboDeletedBookProgressRepository.java
  • backend/src/main/java/org/booklore/repository/OidcSessionRepository.java
  • backend/src/main/java/org/booklore/repository/PdfAnnotationRepository.java
  • backend/src/main/java/org/booklore/repository/RefreshTokenRepository.java
  • backend/src/main/java/org/booklore/repository/UserContentRestrictionRepository.java
  • backend/src/main/java/org/booklore/repository/UserEmailProviderPreferenceRepository.java
  • backend/src/main/java/org/booklore/task/tasks/TempFetchedMetadataCleanupTask.java
📜 Review details
🧰 Additional context used
📓 Path-based instructions (1)
backend/src/**/*.java

📄 CodeRabbit inference engine (AGENTS.md)

backend/src/**/*.java: Use 4-space indentation and match surrounding Java style in backend code
Prefer constructor injection via Lombok patterns already used in the codebase. Do not introduce @Autowired field injection in backend code
Use MapStruct for entity/DTO mapping in backend code
Keep JPA entities on the *Entity suffix in backend code

Files:

  • backend/src/main/java/org/booklore/repository/KoboDeletedBookProgressRepository.java
  • backend/src/main/java/org/booklore/repository/EmailRecipientV2Repository.java
  • backend/src/main/java/org/booklore/repository/BookMetadataRepository.java
  • backend/src/main/java/org/booklore/repository/BookdropFileRepository.java
  • backend/src/main/java/org/booklore/task/tasks/TempFetchedMetadataCleanupTask.java
  • backend/src/main/java/org/booklore/repository/ComicCreatorMappingRepository.java
  • backend/src/main/java/org/booklore/repository/RefreshTokenRepository.java
  • backend/src/main/java/org/booklore/repository/UserEmailProviderPreferenceRepository.java
  • backend/src/main/java/org/booklore/repository/PdfAnnotationRepository.java
  • backend/src/main/java/org/booklore/repository/BookNoteV2Repository.java
  • backend/src/main/java/org/booklore/repository/AnnotationRepository.java
  • backend/src/main/java/org/booklore/repository/OidcSessionRepository.java
  • backend/src/main/java/org/booklore/repository/UserContentRestrictionRepository.java
🧠 Learnings (2)
📚 Learning: 2026-04-10T08:15:37.436Z
Learnt from: imnotjames
Repo: grimmory-tools/grimmory PR: 449
File: booklore-api/src/main/java/org/booklore/service/book/BookDownloadService.java:139-145
Timestamp: 2026-04-10T08:15:37.436Z
Learning: When using Spring `ContentDisposition.builder(...).filename(name, StandardCharsets.UTF_8).build()` (i.e., explicitly providing UTF-8), the resulting header value should include both the quoted `filename="=?UTF-8?..."` and the RFC 5987 `filename*=` parameters. In this case, any extra ASCII fallback computation (e.g., deriving an ASCII `fallbackFilename` via `NON_ASCII_PATTERN` and calling `.filename(fallbackFilename)`) is likely redundant—prefer calling only `.filename(fallbackName?, StandardCharsets.UTF_8)` as appropriate and let Spring handle the UTF-8 header parameters. Verify by comparing the emitted header for `filename` and `filename*` before deciding to keep an ASCII fallback.

Applied to files:

  • backend/src/main/java/org/booklore/repository/KoboDeletedBookProgressRepository.java
  • backend/src/main/java/org/booklore/repository/EmailRecipientV2Repository.java
  • backend/src/main/java/org/booklore/repository/BookMetadataRepository.java
  • backend/src/main/java/org/booklore/repository/BookdropFileRepository.java
  • backend/src/main/java/org/booklore/task/tasks/TempFetchedMetadataCleanupTask.java
  • backend/src/main/java/org/booklore/repository/ComicCreatorMappingRepository.java
  • backend/src/main/java/org/booklore/repository/RefreshTokenRepository.java
  • backend/src/main/java/org/booklore/repository/UserEmailProviderPreferenceRepository.java
  • backend/src/main/java/org/booklore/repository/PdfAnnotationRepository.java
  • backend/src/main/java/org/booklore/repository/BookNoteV2Repository.java
  • backend/src/main/java/org/booklore/repository/AnnotationRepository.java
  • backend/src/main/java/org/booklore/repository/OidcSessionRepository.java
  • backend/src/main/java/org/booklore/repository/UserContentRestrictionRepository.java
📚 Learning: 2026-04-14T12:43:08.698Z
Learnt from: balazs-szucs
Repo: grimmory-tools/grimmory PR: 502
File: booklore-api/src/main/java/org/booklore/service/reader/ChapterCacheService.java:0-0
Timestamp: 2026-04-14T12:43:08.698Z
Learning: For this codebase (booklore-api), target Java 25 with `--enable-preview`, so `_` is intentionally used as an unnamed/ignored variable (e.g., lambda parameter or pattern variable) per Java’s preview feature JEP 456. Do not flag `_` in those contexts as an invalid/reserved identifier; only flag it if it’s used in a non-supported position (e.g., where an unnamed variable is not applicable for the Java preview rules).

Applied to files:

  • backend/src/main/java/org/booklore/repository/KoboDeletedBookProgressRepository.java
  • backend/src/main/java/org/booklore/repository/EmailRecipientV2Repository.java
  • backend/src/main/java/org/booklore/repository/BookMetadataRepository.java
  • backend/src/main/java/org/booklore/repository/BookdropFileRepository.java
  • backend/src/main/java/org/booklore/task/tasks/TempFetchedMetadataCleanupTask.java
  • backend/src/main/java/org/booklore/repository/ComicCreatorMappingRepository.java
  • backend/src/main/java/org/booklore/repository/RefreshTokenRepository.java
  • backend/src/main/java/org/booklore/repository/UserEmailProviderPreferenceRepository.java
  • backend/src/main/java/org/booklore/repository/PdfAnnotationRepository.java
  • backend/src/main/java/org/booklore/repository/BookNoteV2Repository.java
  • backend/src/main/java/org/booklore/repository/AnnotationRepository.java
  • backend/src/main/java/org/booklore/repository/OidcSessionRepository.java
  • backend/src/main/java/org/booklore/repository/UserContentRestrictionRepository.java
🔇 Additional comments (11)
backend/src/main/java/org/booklore/task/tasks/TempFetchedMetadataCleanupTask.java (1)

74-75: LGTM — count() preserves the intended pending-row count.

Using the inherited repository count() keeps the metadata calculation equivalent while removing the redundant custom count method.

backend/src/main/java/org/booklore/repository/BookMetadataRepository.java (1)

18-26: LGTM — the bulk timestamp updates now use the intended flush/clear behavior.

The annotation change is scoped to the two JPQL UPDATE methods and does not alter their predicates or signatures.

backend/src/main/java/org/booklore/repository/KoboDeletedBookProgressRepository.java (1)

14-17: LGTM — this matches the new modifying-query pattern.

The existing bulk DELETE remains transactional and now flushes/clears consistently.

backend/src/main/java/org/booklore/repository/BookdropFileRepository.java (1)

27-30: LGTM — the modifying annotation update is consistent here.

No concerns with adding automatic flush/clear to this existing bulk delete.

backend/src/main/java/org/booklore/repository/EmailRecipientV2Repository.java (1)

20-23: LGTM — this update query now follows the same flush/clear policy.

The behavior change is limited to persistence-context synchronization.

backend/src/main/java/org/booklore/repository/UserEmailProviderPreferenceRepository.java (1)

18-21: LGTM — this bulk delete matches the entity shape.

Using p.userId = :userId is appropriate for the scalar user-id column and keeps the operation explicit.

backend/src/main/java/org/booklore/repository/AnnotationRepository.java (1)

5-8: LGTM — the converted delete is explicit and correctly scoped.

The bulk delete predicates on scalar bookId and userId, with the expected transactional and modifying-query annotations.

Also applies to: 35-38

backend/src/main/java/org/booklore/repository/ComicCreatorMappingRepository.java (1)

19-22: The bulk delete query using association path traversal should work as-is with Hibernate/JPA.

Line 21 navigates m.comicMetadata.bookId in a JPQL DELETE query. While the concern about provider restrictions in bulk DML is valid, this pattern is supported for ManyToOne associations (unlike collection associations). The current query is correctly using a standard JPA construct and should work across all JPA providers.

However, if explicit defensive coding or cross-provider compatibility is a priority, a subquery approach is available as an alternative:

Optional subquery alternative
-    `@Query`("DELETE FROM ComicCreatorMappingEntity m WHERE m.comicMetadata.bookId = :bookId")
+    `@Query`("DELETE FROM ComicCreatorMappingEntity m WHERE m.comicMetadata IN (SELECT cm FROM ComicMetadataEntity cm WHERE cm.bookId = :bookId)")
     void deleteByComicMetadataBookId(`@Param`("bookId") Long bookId);
backend/src/main/java/org/booklore/repository/OidcSessionRepository.java (1)

26-34: LGTM — the bulk deletes are scoped correctly.

The JPQL predicates match the derived method names, and the scheduled cleanup caller does not reuse managed OidcSessionEntity instances after these calls.

backend/src/main/java/org/booklore/repository/PdfAnnotationRepository.java (1)

16-19: LGTM — explicit delete matches the caller’s predicate.

The query deletes only annotations for the provided bookId and userId, matching PdfAnnotationService.deleteAnnotations.

backend/src/main/java/org/booklore/repository/BookNoteV2Repository.java (1)

35-38: LGTM — the bulk delete is correctly parameterized.

The JPQL entity and named parameters match the existing repository field usage.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@backend/src/main/java/org/booklore/service/restriction/ContentRestrictionService.java`:
- Around line 64-69: The current updateRestrictions method issues
restrictionRepository.deleteByUserId(userId) before validating the user, which
can cause an immediate flush of deletes and leaves validation relying on
rollback; change the ordering in ContentRestrictionService.updateRestrictions so
you first call userRepository.findById(userId).orElseThrow(...) to validate the
user exists, then perform restrictionRepository.deleteByUserId(userId), and only
after that proceed to persist the new ContentRestriction rows (keeping the
`@Transactional` on updateRestrictions). Ensure you reference the existing methods
updateRestrictions, deleteByUserId, and userRepository.findById when making the
change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5c08906d-21ad-4f36-8989-9248074bb591

📥 Commits

Reviewing files that changed from the base of the PR and between 3ca9a79 and df1bac2.

📒 Files selected for processing (1)
  • backend/src/main/java/org/booklore/service/restriction/ContentRestrictionService.java
📜 Review details
🧰 Additional context used
📓 Path-based instructions (1)
backend/src/**/*.java

📄 CodeRabbit inference engine (AGENTS.md)

backend/src/**/*.java: Use 4-space indentation and match surrounding Java style in backend code
Prefer constructor injection via Lombok patterns already used in the codebase. Do not introduce @Autowired field injection in backend code
Use MapStruct for entity/DTO mapping in backend code
Keep JPA entities on the *Entity suffix in backend code

Files:

  • backend/src/main/java/org/booklore/service/restriction/ContentRestrictionService.java
🧠 Learnings (3)
📚 Learning: 2026-04-01T17:50:06.817Z
Learnt from: pedronave
Repo: grimmory-tools/grimmory PR: 247
File: booklore-api/src/main/java/org/booklore/service/hardcover/HardcoverSyncService.java:156-159
Timestamp: 2026-04-01T17:50:06.817Z
Learning: In grimmory-tools/grimmory (HardcoverSyncService.java), the Hardcover API already returns `user_book_reads` in a consistent, ordered fashion (ascending by recency), so `getLast()` reliably retrieves the most recent read entry. Adding an explicit `order_by` to the `user_book_reads` query is unnecessary.

Applied to files:

  • backend/src/main/java/org/booklore/service/restriction/ContentRestrictionService.java
📚 Learning: 2026-04-10T08:15:37.436Z
Learnt from: imnotjames
Repo: grimmory-tools/grimmory PR: 449
File: booklore-api/src/main/java/org/booklore/service/book/BookDownloadService.java:139-145
Timestamp: 2026-04-10T08:15:37.436Z
Learning: When using Spring `ContentDisposition.builder(...).filename(name, StandardCharsets.UTF_8).build()` (i.e., explicitly providing UTF-8), the resulting header value should include both the quoted `filename="=?UTF-8?..."` and the RFC 5987 `filename*=` parameters. In this case, any extra ASCII fallback computation (e.g., deriving an ASCII `fallbackFilename` via `NON_ASCII_PATTERN` and calling `.filename(fallbackFilename)`) is likely redundant—prefer calling only `.filename(fallbackName?, StandardCharsets.UTF_8)` as appropriate and let Spring handle the UTF-8 header parameters. Verify by comparing the emitted header for `filename` and `filename*` before deciding to keep an ASCII fallback.

Applied to files:

  • backend/src/main/java/org/booklore/service/restriction/ContentRestrictionService.java
📚 Learning: 2026-04-14T12:43:08.698Z
Learnt from: balazs-szucs
Repo: grimmory-tools/grimmory PR: 502
File: booklore-api/src/main/java/org/booklore/service/reader/ChapterCacheService.java:0-0
Timestamp: 2026-04-14T12:43:08.698Z
Learning: For this codebase (booklore-api), target Java 25 with `--enable-preview`, so `_` is intentionally used as an unnamed/ignored variable (e.g., lambda parameter or pattern variable) per Java’s preview feature JEP 456. Do not flag `_` in those contexts as an invalid/reserved identifier; only flag it if it’s used in a non-supported position (e.g., where an unnamed variable is not applicable for the Java preview rules).

Applied to files:

  • backend/src/main/java/org/booklore/service/restriction/ContentRestrictionService.java

Comment on lines 64 to 69
@Transactional
public List<ContentRestriction> updateRestrictions(Long userId, List<ContentRestriction> restrictions) {
restrictionRepository.deleteByUserId(userId);

BookLoreUserEntity user = userRepository.findById(userId)
.orElseThrow(() -> ApiError.USER_NOT_FOUND.createException(userId));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Validate the user before issuing the flushed bulk delete.

Line 66 now deletes restrictions before Lines 68-69 confirm the user exists. Since this PR makes delete queries flush immediately, avoid relying on rollback for a predictable validation failure; validate first, then delete and load the user for the new rows.

🛡️ Proposed ordering fix
 `@Transactional`
 public List<ContentRestriction> updateRestrictions(Long userId, List<ContentRestriction> restrictions) {
-    restrictionRepository.deleteByUserId(userId);
-
+    if (!userRepository.existsById(userId)) {
+        throw ApiError.USER_NOT_FOUND.createException(userId);
+    }
+
+    restrictionRepository.deleteByUserId(userId);
     BookLoreUserEntity user = userRepository.findById(userId)
             .orElseThrow(() -> ApiError.USER_NOT_FOUND.createException(userId));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Transactional
public List<ContentRestriction> updateRestrictions(Long userId, List<ContentRestriction> restrictions) {
restrictionRepository.deleteByUserId(userId);
BookLoreUserEntity user = userRepository.findById(userId)
.orElseThrow(() -> ApiError.USER_NOT_FOUND.createException(userId));
`@Transactional`
public List<ContentRestriction> updateRestrictions(Long userId, List<ContentRestriction> restrictions) {
if (!userRepository.existsById(userId)) {
throw ApiError.USER_NOT_FOUND.createException(userId);
}
restrictionRepository.deleteByUserId(userId);
BookLoreUserEntity user = userRepository.findById(userId)
.orElseThrow(() -> ApiError.USER_NOT_FOUND.createException(userId));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@backend/src/main/java/org/booklore/service/restriction/ContentRestrictionService.java`
around lines 64 - 69, The current updateRestrictions method issues
restrictionRepository.deleteByUserId(userId) before validating the user, which
can cause an immediate flush of deletes and leaves validation relying on
rollback; change the ordering in ContentRestrictionService.updateRestrictions so
you first call userRepository.findById(userId).orElseThrow(...) to validate the
user exists, then perform restrictionRepository.deleteByUserId(userId), and only
after that proceed to persist the new ContentRestriction rows (keeping the
`@Transactional` on updateRestrictions). Ensure you reference the existing methods
updateRestrictions, deleteByUserId, and userRepository.findById when making the
change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant