Skip to content

[Fix] Improve match, round, referee, CompetitioTable entity adding lambok notation#71

Closed
xji650 wants to merge 23 commits intoUdL-EPS-SoftArch-Igualada:mainfrom
UdL-Arch-Software-Empresite:main
Closed

[Fix] Improve match, round, referee, CompetitioTable entity adding lambok notation#71
xji650 wants to merge 23 commits intoUdL-EPS-SoftArch-Igualada:mainfrom
UdL-Arch-Software-Empresite:main

Conversation

@xji650
Copy link
Contributor

@xji650 xji650 commented Mar 3, 2026

Closes #9
Closes #45

Copilot AI review requested due to automatic review settings March 3, 2026 14:17
@udl-softarch udl-softarch bot added the pr-not-ready This PR cannot be merged until you have reviewed the code analysis results. label Mar 3, 2026
@udl-softarch
Copy link

udl-softarch bot commented Mar 3, 2026

Thank you for your PR @xji650! Now, you should wait for the automated code analysis by CodeRabbit, Copilot and SonarQube.
Please review all warnings carefully, as some of them might be false positives.

Once you are confident that you have fixed all the detected issues and this PR is ready to be merged, add a comment with exactly one word: ready.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 3, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Replaces explicit constructors/getters/setters with Lombok on four domain entities and adds/rewrites association-management methods for matches and referees; adjusts cascade on CompetitionTable.matches and prevents public setters for collection fields.

Changes

Cohort / File(s) Summary
CompetitionTable
src/main/java/cat/udl/eps/softarch/fll/domain/CompetitionTable.java
Added Lombok annotations (@Getter, @Setter, @NoArgsConstructor, @EqualsAndHashCode); included id in equals/hashCode; changed matches cascade from ALL to {PERSIST, MERGE}; disabled Lombok setter for collections; removed explicit accessors; added/updated setMatches, addMatch, removeMatch, setReferees, addReferee, removeReferee with bidirectional/link-cleanup and referee-count constraint.
Round
src/main/java/cat/udl/eps/softarch/fll/domain/Round.java
Added Lombok (@Getter, @Setter, @NoArgsConstructor, @EqualsAndHashCode); included id in equals/hashCode; disabled generated setter for matches; removed explicit accessors; setMatches now removes existing links before adding, addMatch/removeMatch handle nulls, duplicates, and re-linking bidirectionally.
Match
src/main/java/cat/udl/eps/softarch/fll/domain/Match.java
Converted to Lombok-generated boilerplate (@Getter, @Setter, @NoArgsConstructor, @EqualsAndHashCode); marked id for equals/hashCode; removed explicit getters/setters and removed referee and state fields/methods; retained JPA mappings and back-references for round and competitionTable.
Referee
src/main/java/cat/udl/eps/softarch/fll/domain/Referee.java
Added Lombok annotations and equals/hashCode config; removed explicit accessor methods (isExpert, setExpert, getSupervisesTable, setSupervisesTable); preserved fields and JPA mappings.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs


Caution

Pre-merge checks failed

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

  • Ignore

❌ Failed checks (2 errors, 1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Linked Issues check ❌ Error Issue #9 requires domain model implementation and repositories. While the PR modifies Match, Round, Referee, and CompetitionTable classes, it focuses on refactoring with Lombok rather than creating missing domain entities or repositories. Ensure the PR implements the required domain model entities and repositories as specified in issue #9. Clarify which entities and repositories were added or verify the scope aligns with the issue requirements.
Out of Scope Changes check ❌ Error The PR applies Lombok refactoring across multiple domain entities but does not show implementation of new domain models or repositories as required by issue #9. Verify that Match and Round domain models are newly implemented per issue #9, and confirm that corresponding repository classes were created as part of this PR.
Has Tests ⚠️ Warning The pull request modifies four domain classes (CompetitionTable, Match, Referee, Round) but contains no corresponding test files. Create test files for each modified domain class covering add/remove methods, bidirectional relationship management, null handling, and constraint validation.
Title check ❓ Inconclusive The PR title mentions adding Lombok notation to domain entities, which is partially related but not the main objective of issue #9 that requires domain model implementation. Clarify whether this PR is for implementing the domain model (issue #9) or refactoring with Lombok notation. Consider a more specific title describing the primary change.
✅ Passed checks (2 passed)
Check name Status Explanation
Code Style ✅ Passed Code adheres to all style requirements: English language used throughout, self-documenting variable/method names, indentation at most 2 levels with early-return patterns, and Javadoc appropriately omitted per requirements.
Description check ✅ Passed The PR description references closing issue #9 and #45, which aligns with refactoring domain entities (Match, Round, Referee, CompetitionTable) to use Lombok annotations.

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
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: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/main/java/cat/udl/eps/softarch/fll/domain/Round.java (1)

54-57: ⚠️ Potential issue | 🟡 Minor

Inconsistent removeMatch implementation – potential null reference issue.

Unlike CompetitionTable.removeMatch which guards the null-setter call with if (matches.remove(match)), this implementation unconditionally calls match.setRound(null) even if:

  1. match is null → NPE
  2. match wasn't in the list → incorrectly clears its round reference
🐛 Proposed fix for consistency and safety
 public void removeMatch(Match match) {
-    matches.remove(match);
-    match.setRound(null);
+    if (match != null && matches.remove(match)) {
+        match.setRound(null);
+    }
 }

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 37b58df and 71038e6.

📒 Files selected for processing (4)
  • src/main/java/cat/udl/eps/softarch/fll/domain/CompetitionTable.java
  • src/main/java/cat/udl/eps/softarch/fll/domain/Match.java
  • src/main/java/cat/udl/eps/softarch/fll/domain/Referee.java
  • src/main/java/cat/udl/eps/softarch/fll/domain/Round.java

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR refactors several domain entities (Match, Round, Referee, CompetitionTable) to reduce boilerplate via Lombok and to adjust bidirectional relationship handling within the domain model.

Changes:

  • Added Lombok annotations (@Getter, @Setter, @NoArgsConstructor, @EqualsAndHashCode) to multiple entities and removed manual getters/setters/constructors.
  • Updated collection association methods (e.g., addMatch, removeMatch, setReferees) with additional guards and relationship updates.
  • Changed CompetitionTable.matches mapping to use orphanRemoval = true.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 6 comments.

File Description
src/main/java/cat/udl/eps/softarch/fll/domain/Round.java Lombok adoption and updated addMatch behavior for round–match association.
src/main/java/cat/udl/eps/softarch/fll/domain/Referee.java Lombok adoption for referee entity boilerplate.
src/main/java/cat/udl/eps/softarch/fll/domain/Match.java Lombok adoption plus new equals/hashCode based on id.
src/main/java/cat/udl/eps/softarch/fll/domain/CompetitionTable.java Lombok adoption and significant changes to match/referee association management, including orphanRemoval.

Comment on lines 47 to +51
public void addMatch(Match match) {
matches.add(match);
match.setRound(this);
if (match != null && !matches.contains(match)) {
matches.add(match);
match.setRound(this);
}
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

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

Round.addMatch uses !matches.contains(match), but Match now has Lombok equals/hashCode based only on id. For transient Match instances (where id is null), different matches become equal, so only the first one gets added (this breaks the existing Cucumber test that adds multiple new Match() to a round). Consider either removing the contains guard, or changing the duplicate check to use reference equality when id is null, or updating Match.equals to never treat two null ids as equal.

Copilot uses AI. Check for mistakes.
Comment on lines +20 to 29
@Getter
@Setter
@NoArgsConstructor
@EqualsAndHashCode(callSuper = false, onlyExplicitlyIncluded = true)
public class Match extends UriEntity<Long> {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@EqualsAndHashCode.Include
private Long id;
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

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

@EqualsAndHashCode(onlyExplicitlyIncluded = true) with only id included makes two transient Match objects (both with id == null) compare equal. This breaks collection operations like List.contains and can prevent adding multiple new matches. Consider implementing equals/hashCode so that entities with null ids are only equal to themselves (reference equality), or avoid using id-only equality until persistence assigns an id.

Copilot uses AI. Check for mistakes.
Comment on lines 48 to 49
if (match != null && !matches.contains(match)) {
matches.add(match);
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

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

CompetitionTable.addMatch no longer handles the case where the Match is already associated with a different table. Setting match.setCompetitionTable(this) without removing it from the previous table’s matches collection can leave both tables referencing the same match in-memory and lead to inconsistent persistence state. Consider restoring the "move" logic (remove from previous table before adding) to keep the bidirectional association consistent.

Suggested change
if (match != null && !matches.contains(match)) {
matches.add(match);
if (match == null) {
return;
}
CompetitionTable previousTable = match.getCompetitionTable();
if (previousTable != null && previousTable != this) {
previousTable.removeMatch(match);
}
if (!matches.contains(match)) {
matches.add(match);
}
if (match.getCompetitionTable() != this) {

Copilot uses AI. Check for mistakes.
Comment on lines +60 to +65
public void setReferees(List<Referee> referees) {
this.referees.clear();
if (referees != null) {
referees.forEach(this::addReferee);
}
if (referees.contains(referee)) {
}
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

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

CompetitionTable.setReferees clears the collection without calling removeReferee (or otherwise nulling Referee.supervisesTable) for previously associated referees. Since Referee owns the relationship (ManyToOne supervisesTable), this can leave old referees still pointing at this table, producing an inconsistent bidirectional association. Consider iterating existing referees and removing them via removeReferee before re-adding the new list.

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

Comment on lines 31 to 45
@EqualsAndHashCode.Include
private LocalTime startTime;

private LocalTime endTime;

@EqualsAndHashCode.Include
@JsonBackReference("round-matches")
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "round_id")
private Round round;

@EqualsAndHashCode.Include
@JsonBackReference("table-matches")
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "table_id")
private CompetitionTable competitionTable;
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

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

@EqualsAndHashCode.Include on round / competitionTable (both LAZY @ManyToOne) makes Match.equals/hashCode depend on associations, which can trigger lazy-loading, makes equality mutable when a match is moved between rounds/tables, and interacts badly with List.contains checks in addMatch. Align with the other entities in this module by basing equality on a stable identifier (typically id only) and avoid including entity relationships.

Copilot uses AI. Check for mistakes.
Comment on lines 29 to 33
@OneToMany(mappedBy = "competitionTable", cascade = CascadeType.ALL, orphanRemoval = true)
@JsonManagedReference("table-matches")
@Setter(lombok.AccessLevel.NONE)
private List<Match> matches = new ArrayList<>();

Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

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

orphanRemoval = true was added to CompetitionTable.matches. Since Match is also associated to Round, this means removing a match from a table (via removeMatch/collection updates) will mark it for deletion even if it is still linked to a round. Please confirm that “removing from a table” should delete the Match; otherwise drop orphanRemoval and just null/update the owning side.

Copilot uses AI. Check for mistakes.
Comment on lines +47 to 59
public void addMatch(Match match) {
if (match == null || matches.contains(match)) {
return;
}
}

public void addMatch(Match match) {
CompetitionTable previousTable = match.getCompetitionTable();
if (previousTable != null && previousTable != this) {
previousTable.removeMatch(match);
}
if (!matches.contains(match)) {
matches.add(match);
previousTable.getMatches().remove(match);
}

matches.add(match);
match.setCompetitionTable(this);
}
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

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

There are Cucumber tests for addReferee/addMatch, but the changed semantics around moving/removing associations (especially setMatches/setReferees and the new duplicate/relocation logic) aren’t covered. Please add scenarios that verify removed matches/referees have their owning-side references cleared and that moving a match/referee between tables/rounds keeps both sides consistent.

Copilot generated this review using guidance from repository custom instructions.
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.

}
if (!matches.contains(match)) {
matches.add(match);
previousTable.getMatches().remove(match);
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

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

In addMatch, removing from previousTable via previousTable.getMatches().remove(match) bypasses previousTable.removeMatch(...), so the bidirectional invariants are not consistently maintained in one place. Use the existing removeMatch helper on the previous table to ensure Match.competitionTable is updated consistently (and to avoid duplicating relationship-maintenance logic).

Suggested change
previousTable.getMatches().remove(match);
previousTable.removeMatch(match);

Copilot uses AI. Check for mistakes.

CompetitionTable previousTable = referee.getSupervisesTable();
if (previousTable != null && previousTable != this) {
previousTable.getReferees().remove(referee);
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

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

In addReferee, removing the referee from previousTable via previousTable.getReferees().remove(referee) bypasses previousTable.removeReferee(...) and duplicates relationship-management logic. Calling removeReferee on the previous table keeps bidirectional invariants centralized and avoids future inconsistencies if removeReferee changes.

Suggested change
previousTable.getReferees().remove(referee);
previousTable.removeReferee(referee);

Copilot uses AI. Check for mistakes.
Comment on lines 40 to 45
@@ -58,12 +45,22 @@ public void setMatches(List<Match> matches) {
}
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

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

setMatches clears the inverse collection without nulling the owning side (Match.round). With mappedBy = "round", this can leave removed matches still referencing this Round, so setMatches won't reliably replace the association. Prefer removing existing matches via removeMatch (or explicitly match.setRound(null)) before adding the new ones.

Copilot uses AI. Check for mistakes.

Round previousRound = match.getRound();
if (previousRound != null && previousRound != this) {
previousRound.getMatches().remove(match);
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

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

In addMatch, removing from previousRound via previousRound.getMatches().remove(match) bypasses previousRound.removeMatch(...), duplicating relationship-management logic and making it easier to get the bidirectional association out of sync over time. Prefer calling previousRound.removeMatch(match) to keep invariants centralized.

Suggested change
previousRound.getMatches().remove(match);
previousRound.removeMatch(match);

Copilot uses AI. Check for mistakes.
Comment on lines 47 to 59
public void addMatch(Match match) {
if (match == null || matches.contains(match)) {
return;
}

Round previousRound = match.getRound();
if (previousRound != null && previousRound != this) {
previousRound.getMatches().remove(match);
}

matches.add(match);
match.setRound(this);
}
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

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

Round.addMatch now includes logic to ignore duplicates/nulls and to detach a Match from its previous Round, but there are no Cucumber scenarios validating these behaviors (especially moving a match between rounds and ensuring the previous round no longer lists it). Please add scenarios to cover these invariants.

Copilot generated this review using guidance from repository custom instructions.
Copy link
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: 2

♻️ Duplicate comments (1)
src/main/java/cat/udl/eps/softarch/fll/domain/Match.java (1)

23-29: ⚠️ Potential issue | 🟠 Major

id-only Lombok equality can collapse distinct unsaved Match instances.

With Line 23 + Line 28, transient entities (id == null) may compare equal. This can make Line 48 in src/main/java/cat/udl/eps/softarch/fll/domain/Round.java and Line 48 in src/main/java/cat/udl/eps/softarch/fll/domain/CompetitionTable.java incorrectly treat different new matches as duplicates or remove the wrong element.

#!/bin/bash
set -euo pipefail

echo "Check Match equality configuration:"
rg -nP --type=java '@EqualsAndHashCode\(callSuper\s*=\s*false,\s*onlyExplicitlyIncluded\s*=\s*true\)|@EqualsAndHashCode\.Include|private\s+Long\s+id' \
  src/main/java/cat/udl/eps/softarch/fll/domain/Match.java

echo
echo "Check association helpers relying on equals/remove:"
rg -nP --type=java '\bmatches\.(contains|remove)\(' \
  src/main/java/cat/udl/eps/softarch/fll/domain/Round.java \
  src/main/java/cat/udl/eps/softarch/fll/domain/CompetitionTable.java
Suggested fix (entity-safe equality)
-@EqualsAndHashCode(callSuper = false, onlyExplicitlyIncluded = true)
 public class Match extends UriEntity<Long> {
@@
-	`@EqualsAndHashCode.Include`
 	private Long id;
+
+	`@Override`
+	public boolean equals(Object o) {
+		if (this == o) return true;
+		if (!(o instanceof Match other)) return false;
+		return id != null && id.equals(other.id);
+	}
+
+	`@Override`
+	public int hashCode() {
+		return getClass().hashCode();
+	}

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 71038e6 and 678083a.

📒 Files selected for processing (3)
  • src/main/java/cat/udl/eps/softarch/fll/domain/CompetitionTable.java
  • src/main/java/cat/udl/eps/softarch/fll/domain/Match.java
  • src/main/java/cat/udl/eps/softarch/fll/domain/Round.java

Copy link
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: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/main/java/cat/udl/eps/softarch/fll/domain/Round.java (1)

40-45: ⚠️ Potential issue | 🟠 Major

setMatches breaks bidirectional consistency when clearing existing matches.

Using this.matches.clear() removes matches from the collection without unlinking their round reference back to null. The same issue was identified and fixed in CompetitionTable.setMatches (which now uses new ArrayList<>(this.matches).forEach(this::removeMatch)).

🔧 Proposed fix
 public void setMatches(List<Match> matches) {
-    this.matches.clear();
+    new ArrayList<>(this.matches).forEach(this::removeMatch);
     if (matches != null) {
         matches.forEach(this::addMatch);
     }
 }

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 678083a and 15c352b.

📒 Files selected for processing (2)
  • src/main/java/cat/udl/eps/softarch/fll/domain/CompetitionTable.java
  • src/main/java/cat/udl/eps/softarch/fll/domain/Round.java

xji650 added 2 commits March 4, 2026 01:28
Refactor match handling in Round class to simplify match addition and…
Copilot AI review requested due to automatic review settings March 4, 2026 00:36
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

Comment on lines 40 to 45
@@ -58,12 +45,30 @@ public void setMatches(List<Match> matches) {
}
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

setMatches clears the inverse collection with this.matches.clear() but does not update the owning side (Match.round) for matches that were previously in the round. In JPA, this can leave removed Match rows still pointing at this Round (and with orphanRemoval = true it also won’t trigger deletes unless the owning side is updated). Prefer removing existing matches via removeMatch (similar to CompetitionTable#setMatches) so bidirectional consistency is maintained.

Copilot uses AI. Check for mistakes.
Copilot AI review requested due to automatic review settings March 4, 2026 00:53
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

Comment on lines +78 to +79
public void addReferee(Referee referee) {
if (referee == null || referees.contains(referee)) {
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

addReferee uses referees.contains(referee) to prevent duplicates, but Referee inherits equals/hashCode from Volunteer (based on id). For new referees with id == null, different instances compare equal, so you won't be able to add more than one transient referee to a table. Consider changing the duplicate check to use reference identity or to compare IDs only when non-null.

Suggested change
public void addReferee(Referee referee) {
if (referee == null || referees.contains(referee)) {
private boolean hasRefereeAssigned(Referee referee) {
return referees.stream().anyMatch(existingReferee -> isSameReferee(existingReferee, referee));
}
private boolean isSameReferee(Referee existingReferee, Referee newReferee) {
if (existingReferee == newReferee) {
return true;
}
if (existingReferee == null || newReferee == null) {
return false;
}
if (existingReferee.getId() == null || newReferee.getId() == null) {
return false;
}
return existingReferee.getId().equals(newReferee.getId());
}
public void addReferee(Referee referee) {
if (referee == null || hasRefereeAssigned(referee)) {

Copilot uses AI. Check for mistakes.
Comment on lines +36 to +37
@Enumerated(EnumType.STRING)
private MatchState state;
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

state is declared as String but annotated with @Enumerated(EnumType.STRING), which is only valid for enum-typed fields. This also breaks existing code/tests that expect MatchState (e.g., MatchAssignmentService compares match.getState() to MatchState.FINISHED). Change the field type back to MatchState (and consider restoring a sensible default like SCHEDULED).

Copilot uses AI. Check for mistakes.
Copilot AI review requested due to automatic review settings March 4, 2026 01:33
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 9 comments.

Comment on lines +26 to 30
@EqualsAndHashCode(callSuper = false, onlyExplicitlyIncluded = true)
public class Match extends UriEntity<Long> {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

@EqualsAndHashCode(onlyExplicitlyIncluded = true) with only the generated id included means two transient Match instances (both with id == null) will compare equal and share the same hash code. That can break Set/List.contains behavior and forces identity (==) workarounds elsewhere. Consider a custom equals/hashCode that uses id only when non-null and otherwise falls back to reference equality.

Copilot uses AI. Check for mistakes.
Comment on lines +61 to 63
this.matches.add(match);
match.setCompetitionTable(this);
}
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

addMatch now supports transferring a Match from its previous CompetitionTable. There are no Cucumber scenarios covering moving a match between tables (and verifying the previous table no longer contains it), so regressions could slip in. Please add a test scenario for that transfer behavior.

Copilot generated this review using guidance from repository custom instructions.
Comment on lines +24 to 28
@EqualsAndHashCode(callSuper = false, onlyExplicitlyIncluded = true)
public class Round extends UriEntity<Long> {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

@EqualsAndHashCode(onlyExplicitlyIncluded = true) with only id included means two transient Round instances (both with id == null) will compare equal and have the same hash code. That can break collection semantics and is likely why addMatch/removeMatch needed identity (==) checks. Consider implementing equals/hashCode so it uses id only when non-null and otherwise falls back to reference equality, or base equality on a stable business key (e.g., number, which is unique).

Copilot uses AI. Check for mistakes.
Comment on lines +57 to +60
Round previousRound = match.getRound();
if (previousRound != null && previousRound != this) {
previousRound.getMatches().removeIf(m -> m == match);
}
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

addMatch now removes a Match from its previous Round before adding it to this one. There are no Cucumber scenarios covering this transfer case (and ensuring the previous round no longer contains the match), so it’s easy to regress. Please add a test scenario for moving a match between rounds.

Copilot generated this review using guidance from repository custom instructions.
Comment on lines +87 to 89
if (this.referees.stream().anyMatch(r -> r == referee)) {
return;
}
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

addReferee checks duplicates using identity (r == referee). With JPA proxies/detached instances this can allow duplicates for the same underlying row. Prefer comparing by a stable key (e.g., non-null id) and falling back to reference equality only when the key is null.

Copilot uses AI. Check for mistakes.
Comment on lines +96 to +98
if (previousTable != null && previousTable != this) {
previousTable.getReferees().removeIf(r -> r == referee);
}
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

Removing from previousTable via previousTable.getReferees().removeIf(r -> r == referee) relies on reference equality and can fail when the same referee is represented by a different instance (detached/proxy). Prefer removal by stable key (e.g., non-null id) and only fall back to reference equality for transient entities.

Copilot uses AI. Check for mistakes.
Comment on lines +100 to 102
this.referees.add(referee);
referee.setSupervisesTable(this);
}
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

addReferee now supports transferring a referee from a previous table. There are no Cucumber tests covering moving a referee between tables and asserting the previous table no longer lists the referee. Please add a scenario for that transfer behavior.

Copilot generated this review using guidance from repository custom instructions.
Comment on lines +52 to +53
// USA IDENTIDAD (==) para que los tests con IDs nulos no fallen
if (this.matches.stream().anyMatch(m -> m == match)) {
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

The inline comment // USA IDENTIDAD (==) para que los tests con IDs nulos no fallen is not written in English. Please rewrite it in English (or remove it if it’s only documenting a workaround) to follow the project guideline that code/comments must be in English.

Copilot generated this review using guidance from repository custom instructions.
Comment on lines +53 to +55
if (this.matches.stream().anyMatch(m -> m == match)) {
return;
}
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

addMatch uses identity comparison (m == match) to detect duplicates. This is brittle with JPA/Hibernate because the same database row can be represented by different object instances (detached entities, proxies), so duplicates may not be detected. Prefer comparing by a stable key (e.g., non-null id) and falling back to reference equality only when the key is null.

Copilot uses AI. Check for mistakes.
Copy link
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

♻️ Duplicate comments (3)
src/main/java/cat/udl/eps/softarch/fll/domain/CompetitionTable.java (1)

56-59: 🧹 Nitpick | 🔵 Trivial

Use removeMatch/removeReferee helpers when rehoming entities.

Line 58 and Line 97 directly mutate previousTable collections. This bypasses centralized unlink behavior and can drift over time. Call previousTable.removeMatch(match) / previousTable.removeReferee(referee) instead.

♻️ Refactor sketch
 		CompetitionTable previousTable = match.getCompetitionTable();
 		if (previousTable != null && previousTable != this) {
-			previousTable.getMatches().removeIf(m -> m == match);
+			previousTable.removeMatch(match);
 		}
@@
 		CompetitionTable previousTable = referee.getSupervisesTable();
 		if (previousTable != null && previousTable != this) {
-			previousTable.getReferees().removeIf(r -> r == referee);
+			previousTable.removeReferee(referee);
 		}

Also applies to: 95-98

src/main/java/cat/udl/eps/softarch/fll/domain/Round.java (1)

57-60: 🧹 Nitpick | 🔵 Trivial

Use previousRound.removeMatch(match) when moving a match.

Line 59 directly mutates previousRound.getMatches(), bypassing the unlink logic encapsulated in removeMatch.

♻️ Refactor sketch
 		Round previousRound = match.getRound();
 		if (previousRound != null && previousRound != this) {
-			previousRound.getMatches().removeIf(m -> m == match);
+			previousRound.removeMatch(match);
 		}
src/main/java/cat/udl/eps/softarch/fll/domain/Match.java (1)

26-32: ⚠️ Potential issue | 🟠 Major

equals/hashCode based only on nullable id breaks transient-entity semantics.

With Line 26 + Line 31, two unsaved Match instances (id == null) compare equal, which can corrupt contains/dedup behavior and has already forced identity-based workarounds elsewhere.

#!/bin/bash
# Verify equality configuration and identity-based workarounds in domain entities.
rg -n --type=java '@EqualsAndHashCode|EqualsAndHashCode.Include|anyMatch\(m -> m == match\)|removeIf\(m -> m == match\)' \
  src/main/java/cat/udl/eps/softarch/fll/domain/Match.java \
  src/main/java/cat/udl/eps/softarch/fll/domain/Round.java \
  src/main/java/cat/udl/eps/softarch/fll/domain/CompetitionTable.java

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 15c352b and 62fc1e3.

📒 Files selected for processing (3)
  • src/main/java/cat/udl/eps/softarch/fll/domain/CompetitionTable.java
  • src/main/java/cat/udl/eps/softarch/fll/domain/Match.java
  • src/main/java/cat/udl/eps/softarch/fll/domain/Round.java

Copy link
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

♻️ Duplicate comments (2)
src/main/java/cat/udl/eps/softarch/fll/domain/Round.java (2)

52-59: ⚠️ Potential issue | 🟠 Major

Use entity-aware removal path when rehoming a Match.

At Line 52 and Line 58, using reference equality (m == match) plus direct mutation of previousRound.getMatches() can fail to unlink correctly in detached/proxy cases and bypasses removeMatch invariants.

Suggested fix
-    if (this.matches.stream().anyMatch(m -> m == match)) {
+    if (this.matches.contains(match)) {
         return;
     }

     Round previousRound = match.getRound();
     if (previousRound != null && previousRound != this) {
-        previousRound.getMatches().removeIf(m -> m == match);
+        previousRound.removeMatch(match);
     }

71-73: ⚠️ Potential issue | 🟠 Major

Avoid reference-based removeIf in removeMatch.

At Line 71, removeIf(m -> m == match) relies on identity equality and can miss logically same entities. Use remove(match) for consistency with entity equality behavior.

Suggested fix
-    if (this.matches.removeIf(m -> m == match)) {
+    if (this.matches.remove(match)) {
         match.setRound(null);
     }

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 79535e98-4400-44e7-99e0-b6dbe777d25f

📥 Commits

Reviewing files that changed from the base of the PR and between 62fc1e3 and 956367a.

📒 Files selected for processing (1)
  • src/main/java/cat/udl/eps/softarch/fll/domain/Round.java

Copilot AI review requested due to automatic review settings March 4, 2026 02:11
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 6 comments.

Comment on lines +52 to 62
if (this.matches.stream().anyMatch(m -> m == match)) {
return;
}
}

public void addMatch(Match match) {
CompetitionTable previousTable = match.getCompetitionTable();
if (previousTable != null && previousTable != this) {
previousTable.removeMatch(match);
}
if (!matches.contains(match)) {
matches.add(match);
previousTable.getMatches().removeIf(m -> m == match);
}

this.matches.add(match);
match.setCompetitionTable(this);
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

addMatch uses reference equality (m == match) to detect duplicates and to detach from the previous table. Since Match equality is id-based, this can allow duplicates (or fail to detach) when the same DB row is represented by another instance/proxy. Prefer matches.contains(match) / previousTable.removeMatch(match) for equality-based behavior and consistent bidirectional updates.

Copilot uses AI. Check for mistakes.
Comment on lines 65 to +72
public void removeMatch(Match match) {
matches.remove(match);
match.setCompetitionTable(null);
if (match == null) {
return;
}

if (this.matches.removeIf(m -> m == match)) {
match.setCompetitionTable(null);
}
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

removeMatch uses removeIf(m -> m == match), so removal only works for the exact same instance. With JPA, it’s common to have a different instance/proxy representing the same Match id, in which case removal will silently fail. Use equality-based removal (e.g., matches.remove(match) / removeIf(match::equals)).

Copilot uses AI. Check for mistakes.
Comment on lines +87 to 101
if (this.referees.stream().anyMatch(r -> r == referee)) {
return;
}
if (referees.size() >= 3) {

if (this.referees.size() >= 3) {
throw new IllegalStateException("A table can have a maximum of 3 referees");
}
referees.add(referee);

CompetitionTable previousTable = referee.getSupervisesTable();
if (previousTable != null && previousTable != this) {
previousTable.getReferees().removeIf(r -> r == referee);
}

this.referees.add(referee);
referee.setSupervisesTable(this);
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

addReferee prevents duplicates using reference equality (r == referee) and detaches from the previous table with removeIf(r -> r == referee). If the same referee is represented by another instance/proxy with the same id, this can lead to duplicates and inconsistent lists. Prefer referees.contains(referee) and previousTable.removeReferee(referee) for equality-based behavior and bidirectional consistency.

Copilot uses AI. Check for mistakes.
Comment on lines 104 to +111
public void removeReferee(Referee referee) {
referees.remove(referee);
referee.setSupervisesTable(null);
if (referee == null) {
return;
}

if (this.referees.removeIf(r -> r == referee)) {
referee.setSupervisesTable(null);
}
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

removeReferee uses removeIf(r -> r == referee), which won’t remove an equal referee represented by a different instance/proxy. Use equality-based removal (e.g., referees.remove(referee) / removeIf(referee::equals)) to ensure the association is reliably cleared.

Copilot uses AI. Check for mistakes.
Comment on lines +55 to 65
if (this.matches.stream().anyMatch(m -> m == match)) {
return;
}

public void setMatches(List<Match> matches) {
this.matches.clear();
if (matches != null) {
matches.forEach(this::addMatch);
Round previousRound = match.getRound();
if (previousRound != null && previousRound != this) {
previousRound.getMatches().removeIf(m -> m == match);
}
}

public void addMatch(Match match) {
matches.add(match);
this.matches.add(match);
match.setRound(this);
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

addMatch/removeMatch (and the reassignment logic) relies on reference equality (==) for duplicate detection and removal. Since Match has an equals/hashCode based on id, passing a different instance/proxy representing the same match will not be removed and can lead to duplicates or stale associations. Prefer contains(match) / remove(match) (or removeIf(match::equals)) and call previousRound.removeMatch(match) to keep bidirectional consistency using equality semantics.

Copilot uses AI. Check for mistakes.
Comment on lines 68 to +76
public void removeMatch(Match match) {
matches.remove(match);
match.setRound(null);
if (match == null) {
return;

}

if (this.matches.removeIf(m -> m == match)) {
match.setRound(null);
}
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

removeMatch uses removeIf(m -> m == match) which only removes the exact same object instance. If callers pass a different Match instance with the same id (or a JPA proxy), the match won't be removed. Use equality-based removal (e.g., matches.remove(match) or removeIf(match::equals)) to match the entity equals contract.

Copilot uses AI. Check for mistakes.
xji650 added 2 commits March 4, 2026 03:49
…_XiaoLong

Add step definitions for CompetitionTable, Match, Referee, and Round …
@sonarqubecloud
Copy link

sonarqubecloud bot commented Mar 4, 2026

@xji650 xji650 closed this Mar 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr-not-ready This PR cannot be merged until you have reviewed the code analysis results.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Refactor]: Fix tests from #18 [Feat]: Create domain model and repository classes: Match, Round

2 participants