Reputation: 604
I am trying to create a very simple Spring Boot application for storing sports data.
It has two entities: player and tournament. However, I want to store how each player placed in each tournament.
For this, I created the following ER Diagram. The junction table PlayerPlacement_map
contains a relationship-attribute placement
for storing how the players place in a tournament:
I have followed this guide on how to map the relationship between Players and Tournament, with Id from those two tables as a composite key in the junction table called PlayerPlacementMap
: https://www.baeldung.com/jpa-many-to-many
That has given me the following classes:
Player.java (Tournament.java follows a similar pattern - left out for brevity)
@Entity
@Table(name = "players")
public class Player {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "Id", updatable = false, nullable = false)
private long id;
@Column(name = "Name")
private String name;
@ManyToMany
@JoinTable(
name = "PlayerTournament_map",
joinColumns = @JoinColumn(name = "Player_Id"),
inverseJoinColumns = @JoinColumn(name = "Tournament_Id"))
Set<Tournament> attendedTournaments;
@OneToMany(mappedBy = "player")
Set<PlayerPlacement> placements;
//getters, constructors - left out for brevity
}
PlayerPlacementKey.java (making an embeddable composite key-class)
@Embeddable
public class PlayerPlacementKey implements Serializable {
@Column(name = "Player_Id")
long playerId;
@Column(name = "Tournament_Id")
long tournamentId;
//getters, setters, constructors, equals, hashcode - left out for brevity
}
PlayerPlacement.java (junction table)
@Entity
@Table(name = "PlayerPlacement_map")
public class PlayerPlacement {
@EmbeddedId
PlayerPlacementKey id;
@ManyToOne
@MapsId("PlayerId")
@JoinColumn(name = "Player_Id")
Player player;
@ManyToOne
@MapsId("TournamentId")
@JoinColumn(name = "Tournament_Id")
Tournament tournament;
int placement;
//constructors - left out for brevity
}
I have repositories for player, tournament and playerplacement. Player and tournament repositories are working fine on their own, and I am able to perform CRUD operations through a @RestController against a MSSQL database.
This is the repository for playerplacement:
@Repository
public interface PlayerPlacementRepository extends JpaRepository<PlayerPlacement, PlayerPlacementKey>
{}
For the junction table, I have made a PlayerPlacementController:
@RestController
public class PlayerPlacementController {
@Autowired
private PlayerPlacementRepository playerPlacementRepository;
@PostMapping("/playerplacement")
public PlayerPlacement addPlayerPlacement(@RequestBody PlayerPlacement playerPlacement) {
return playerPlacementRepository.save(playerPlacement);
}
}
Finally, here comes the problem: When I call the /"playerplacement"
endpoint, I receive the following error:
org.hibernate.id.IdentifierGenerationException: null id generated for:class com.testproject.learning.model.PlayerPlacement
I think I have migrated the database just fine, but just to be safe, here is my migration for the junction table:
CREATE TABLE PlayerPlacement_map (
PlayerId Bigint NOT NULL,
TournamentId Bigint NOT NULL,
Placement int
CONSTRAINT PK_PlayerPlacement NOT NULL IDENTITY(1,1) PRIMARY KEY
(
PlayerId,
TournamentId
)
FOREIGN KEY (PlayerId) REFERENCES Players (Id),
FOREIGN KEY (TournamentId) REFERENCES Tournaments (Id)
);
I haven't been able to figure out what I am doing wrong. I appreciate all help and pointers that I receive - thanks in advance.
EDIT: Progress has been made with help of user Alex V., but I hit a new problem as of right now.
I make the following JSON call:
{
"player":
{
"name":"John Winther"
},
"tournament":
{
"name":"Spring Boot Cup 2020"
},
"placement":3
}
I don't set the id
(composite key) myself - I guess it should be handled by something of the framework? Anyways, when I make this call in debug mode, I get the following debug variables set in the endpoint:
However, it results in the following error:
java.lang.NullPointerException: Cannot invoke "Object.getClass()" because "o" is null
, which probably refers to my equals
-method in PlayerPlacementKey.java
(?):
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof PlayerPlacementKey)) return false;
PlayerPlacementKey that = (PlayerPlacementKey) o;
return playerId == that.playerId &&
tournamentId == that.tournamentId;
}
Hope anybody can push me in the right direction one more time.
Upvotes: 3
Views: 2345
Reputation: 2841
Your entity has playerId
field annotated as @Column(name = "Player_Id")
, but database table contains PlayerId
column instead of Player_Id
. The same situation with tournamentId
, player
, tournament
fields.
@MapsId("TournamentId")
has to contain @EmbededId
class field name tournamentId
instead of TournamentId
. It means the field is mapped by embeded id field tournamentId
.
@ManyToOne
@MapsId("tournamentId")
@JoinColumn(name = "tournamentId")
Tournament tournament;
The same problem here @MapsId("PlayerId")
.
Upvotes: 1