Skip to content

refactor(jpa): apply Hibernate-safe equals and hashCode implementations#1089

Open
balazs-szucs wants to merge 5 commits intogrimmory-tools:developfrom
balazs-szucs:JPA-buddy-recommendation
Open

refactor(jpa): apply Hibernate-safe equals and hashCode implementations#1089
balazs-szucs wants to merge 5 commits intogrimmory-tools:developfrom
balazs-szucs:JPA-buddy-recommendation

Conversation

@balazs-szucs
Copy link
Copy Markdown
Member

@balazs-szucs balazs-szucs commented May 4, 2026

Description

Resolves the JPA Buddy warning:

Using @EqualsAndHashCode for JPA entities is not recommended. It can cause severe performance and memory consumption issues.

This change establishes a consistent, reliable, and Hibernate-safe equals() and hashCode() implementation across the codebase. By utilizing org.hibernate.Hibernate.getClass() and getter methods, it ensures entities are compared accurately, even when wrapped in lazy-loaded proxies.

Linked Issue

Changes

Entity equality and hash code standardization:

  • Updated all entity classes (AuthorEntity, BookEntity, BookLoreUserEntity, CategoryEntity, LibraryEntity, MoodEntity, ShelfEntity, TagEntity) to use Hibernate.getClass(this) and getId() in their equals and hashCode implementations, making them final and proxy-safe.

Manual Testing Steps

  1. the app compiles

Screenshots (Optional)

Additional Context (Optional)

AI Disclosure

Checklist

  • This PR links and implements an accepted issue.
  • This PR is a single focused change.
  • There are new or updated tests validating this change.
  • I ran just ui check and just api check.
  • I have added screenshots if there were any UI changes.
  • I have disclosed any AI usage as per the organization AI Policy above.
  • I understand all of my submitted changes.

Summary by CodeRabbit

  • Bug Fixes
    • Improved reliability of entity identification and comparison operations to ensure consistent behavior across all data objects.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 4, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro

Run ID: 867e6963-33e8-492a-8692-1c0c4b438941

📥 Commits

Reviewing files that changed from the base of the PR and between f3138a8 and 7b5c445.

📒 Files selected for processing (8)
  • backend/src/main/java/org/booklore/model/entity/AuthorEntity.java
  • backend/src/main/java/org/booklore/model/entity/BookEntity.java
  • backend/src/main/java/org/booklore/model/entity/BookLoreUserEntity.java
  • backend/src/main/java/org/booklore/model/entity/CategoryEntity.java
  • backend/src/main/java/org/booklore/model/entity/LibraryEntity.java
  • backend/src/main/java/org/booklore/model/entity/MoodEntity.java
  • backend/src/main/java/org/booklore/model/entity/ShelfEntity.java
  • backend/src/main/java/org/booklore/model/entity/TagEntity.java
📜 Recent review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Test Suite / Frontend Tests
  • GitHub Check: Test Suite / Backend Tests
  • GitHub Check: Frontend Lint Threshold Check
  • GitHub Check: Analyze (java-kotlin)
  • GitHub Check: Analyze (javascript-typescript)
🧰 Additional context used
📓 Path-based instructions (3)
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/model/entity/MoodEntity.java
  • backend/src/main/java/org/booklore/model/entity/BookEntity.java
  • backend/src/main/java/org/booklore/model/entity/BookLoreUserEntity.java
  • backend/src/main/java/org/booklore/model/entity/TagEntity.java
  • backend/src/main/java/org/booklore/model/entity/CategoryEntity.java
  • backend/src/main/java/org/booklore/model/entity/AuthorEntity.java
  • backend/src/main/java/org/booklore/model/entity/LibraryEntity.java
  • backend/src/main/java/org/booklore/model/entity/ShelfEntity.java
**/*

⚙️ CodeRabbit configuration file

**/*: This project is being developed using current and future-facing technologies:

  • Java 25 with --enable-preview (preview features are INTENTIONAL and encouraged)
  • Spring Boot 4 (latest major version, check APIs accordingly)
  • Jackson 3 (new package: tools.jackson.* instead of com.fasterxml.jackson.*)
  • Hibernate 7.3.x (Jakarta Persistence 3.2, new APIs; avoid deprecated Hibernate 5/6 patterns)
  • Angular 21 (signals-based reactivity, no NgModules unless legacy)

Grimmory Internal Tools

Metadata Standards and Compliance

  • For all metadata writing and parsing logic, double-check against Dublin Core and ANSI standards to ensure perfect official compliance.
  • We strictly follow the widespread and official XML-compliant methods for EPUB2, EPUB3, CBX, and PDF formats.

General Java and Spring rules

  • ALWAYS prefer modern, idiomatic Java 25 constructs over legacy patterns.
  • Preview features (--enable-preview) are enabled and intentional; do NOT flag them as risky unless there is a concrete runtime issue.
  • Prefer: records, sealed classes/interfaces, pattern matching (switch expressions, instanceof), structured concurrency (StructuredTaskScope), scoped values, string templates, unnamed patterns/variables.
  • Prefer virtual threads (Thread.ofVirtual(), Executors.newVirtualThreadPerTaskExecutor()) over platform threads for I/O-bound work.
  • Prefer the new Sequenced Collections API (SequencedCollection, SequencedMap) where applicable.
  • Prefer var for local variables when the type is obvious from context.
  • Use stream().toList() instead of stream().collect(Collectors.toList()) for imm...

Files:

  • backend/src/main/java/org/booklore/model/entity/MoodEntity.java
  • backend/src/main/java/org/booklore/model/entity/BookEntity.java
  • backend/src/main/java/org/booklore/model/entity/BookLoreUserEntity.java
  • backend/src/main/java/org/booklore/model/entity/TagEntity.java
  • backend/src/main/java/org/booklore/model/entity/CategoryEntity.java
  • backend/src/main/java/org/booklore/model/entity/AuthorEntity.java
  • backend/src/main/java/org/booklore/model/entity/LibraryEntity.java
  • backend/src/main/java/org/booklore/model/entity/ShelfEntity.java
**/entity/**/*.java

⚙️ CodeRabbit configuration file

**/entity/**/*.java: Hibernate 7.3 entity review:

  • Jakarta Persistence (jakarta.persistence.*) only; never javax.
  • Every @Entity must override equals() and hashCode() using business key or @NaturalId.
  • Flag EAGER fetch on collections; must always be LAZY.
  • Flag @OneToMany without mappedBy.
  • Flag missing @Index annotations on foreign key columns.
  • Flag @Where usage; use @SQLRestriction instead (Hibernate 7.x).
  • Flag any mutable collection field that is not initialized (e.g., new ArrayList<>()).

Files:

  • backend/src/main/java/org/booklore/model/entity/MoodEntity.java
  • backend/src/main/java/org/booklore/model/entity/BookEntity.java
  • backend/src/main/java/org/booklore/model/entity/BookLoreUserEntity.java
  • backend/src/main/java/org/booklore/model/entity/TagEntity.java
  • backend/src/main/java/org/booklore/model/entity/CategoryEntity.java
  • backend/src/main/java/org/booklore/model/entity/AuthorEntity.java
  • backend/src/main/java/org/booklore/model/entity/LibraryEntity.java
  • backend/src/main/java/org/booklore/model/entity/ShelfEntity.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/model/entity/MoodEntity.java
  • backend/src/main/java/org/booklore/model/entity/BookEntity.java
  • backend/src/main/java/org/booklore/model/entity/BookLoreUserEntity.java
  • backend/src/main/java/org/booklore/model/entity/TagEntity.java
  • backend/src/main/java/org/booklore/model/entity/CategoryEntity.java
  • backend/src/main/java/org/booklore/model/entity/AuthorEntity.java
  • backend/src/main/java/org/booklore/model/entity/LibraryEntity.java
  • backend/src/main/java/org/booklore/model/entity/ShelfEntity.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/model/entity/MoodEntity.java
  • backend/src/main/java/org/booklore/model/entity/BookEntity.java
  • backend/src/main/java/org/booklore/model/entity/BookLoreUserEntity.java
  • backend/src/main/java/org/booklore/model/entity/TagEntity.java
  • backend/src/main/java/org/booklore/model/entity/CategoryEntity.java
  • backend/src/main/java/org/booklore/model/entity/AuthorEntity.java
  • backend/src/main/java/org/booklore/model/entity/LibraryEntity.java
  • backend/src/main/java/org/booklore/model/entity/ShelfEntity.java
🔇 Additional comments (8)
backend/src/main/java/org/booklore/model/entity/BookEntity.java (1)

136-148: LGTM!

The Hibernate proxy-safe equals/hashCode implementation is correct. The final modifier prevents subclass override issues with lazy-loaded proxies, Hibernate.getClass() ensures correct class comparison even when comparing proxied and unproxied instances, and the constant class-based hashCode() maintains the contract when entities transition from transient (null id) to persistent state.

backend/src/main/java/org/booklore/model/entity/BookLoreUserEntity.java (1)

95-106: LGTM!

The equals/hashCode implementation correctly follows the Hibernate proxy-safe pattern with final methods and Hibernate.getClass() for reliable entity comparison.

backend/src/main/java/org/booklore/model/entity/CategoryEntity.java (1)

33-44: LGTM!

Consistent Hibernate proxy-safe equals/hashCode implementation.

backend/src/main/java/org/booklore/model/entity/ShelfEntity.java (1)

61-73: LGTM!

Good replacement of Lombok's @EqualsAndHashCode(of = "id") with the Hibernate proxy-safe implementation. The explicit methods handle proxy instances correctly, which Lombok-generated code cannot.

backend/src/main/java/org/booklore/model/entity/MoodEntity.java (1)

33-44: LGTM!

Consistent Hibernate proxy-safe equals/hashCode implementation.

backend/src/main/java/org/booklore/model/entity/AuthorEntity.java (1)

51-62: LGTM!

Consistent Hibernate proxy-safe equals/hashCode implementation.

backend/src/main/java/org/booklore/model/entity/LibraryEntity.java (1)

78-89: LGTM!

Consistent Hibernate proxy-safe equals/hashCode implementation.

backend/src/main/java/org/booklore/model/entity/TagEntity.java (1)

33-44: LGTM!

Consistent Hibernate proxy-safe equals/hashCode implementation.


Walkthrough

Eight JPA entity classes are updated to use Hibernate-aware proxy-safe equality and hashing implementations. Each entity's equals and hashCode methods are now final, use Hibernate.getClass() for runtime class comparison, and determine equality based on non-null entity id values.

Changes

Entity Equality & Hashing Modernization

Layer / File(s) Summary
Import Updates
backend/src/main/java/org/booklore/model/entity/AuthorEntity.java, CategoryEntity.java, LibraryEntity.java, MoodEntity.java, TagEntity.java, BookEntity.java, ShelfEntity.java
Hibernate import added to support proxy-safe class resolution; Objects import added where needed for Objects.equals() comparisons.
Core Equality & Hashing
backend/src/main/java/org/booklore/model/entity/AuthorEntity.java, BookEntity.java, BookLoreUserEntity.java, CategoryEntity.java, LibraryEntity.java, MoodEntity.java, ShelfEntity.java, TagEntity.java
equals(Object) and hashCode() methods now marked final; equality compares non-null id via Objects.equals(getId(), that.getId()) or equivalent; class comparison uses Hibernate.getClass(this) == Hibernate.getClass(o) instead of plain getClass().
Annotation & Formatting
BookLoreUserEntity.java
@JoinTable annotation for user-library many-to-many mapping reformatted to single-line declaration; ShelfEntity.java removes @EqualsAndHashCode(of = "id") Lombok annotation in favor of explicit method implementations.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

backend, enhancement

Suggested reviewers

  • imajes
  • zachyale
  • imnotjames

Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error, 1 warning)

Check name Status Explanation Resolution
Description check ❌ Error The description is incomplete: no linked issue provided, manual testing steps are incomplete (only '1. the app compiles' listed), and AI Disclosure field is blank despite being required. Complete the Linked Issue section with an issue number, fill out all manual testing steps, and specify AI tool usage (or 'None' if no tools were used) in the AI Disclosure section.
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 (3 passed)
Check name Status Explanation
Title check ✅ Passed The title follows conventional commit format with type 'refactor' and scope 'jpa', clearly summarizing the main change of implementing Hibernate-safe equals/hashCode methods.
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.
✨ 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
Review rate limit: 5/8 reviews remaining, refill in 17 minutes and 8 seconds.

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

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant