Spring Hibernate:使用组合主键存储 n:m table 值时出现 EntityExistsException
Spring Hibernate: EntityExistsException when storing n:m table value with combiend Priamry Key
所以我的第二个 post。这次我在做我的一个激情项目,结果比我想象的要复杂得多,我再次需要一些帮助。
我有两个实体:Gamestate 和 User。
用户应该能够加入多个游戏(/gamestates)。游戏(/游戏状态)应该有很多人加入。因此,它表示为 N:M 关系。
根据谁加入以及他们加入的时间,他们应该具有不同的角色,从而赋予他们在应用程序中的不同权利。这意味着我需要与自定义字段的 N:M 关系,因此我必须自己对关系 table 进行建模。我就到此为止了。
抽象模型:
@EqualsAndHashCode
@Getter
@Setter
@ToString
public abstract class AbstractModel {
@Id
@GeneratedValue
protected Long id;
@NotNull
protected String identifier;
}
用户
@Getter
@Setter
@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class User extends AbstractModel{
private String nickName;
private UserRole role;
@ToString.Exclude
@EqualsAndHashCode.Exclude
@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "user", orphanRemoval = true)
private LoginInformation loginInformation;
@ToString.Exclude
@EqualsAndHashCode.Exclude
@OneToMany(cascade = {CascadeType.PERSIST}, fetch = FetchType.LAZY, mappedBy = "gameState")
private List<UserGameState> userGameStates = new ArrayList<>();
//DTO Constructor
public User(UserDTO userDTO){
this.identifier = Optional.ofNullable(userDTO.getIdentifier())
.orElse(UUID.randomUUID().toString());
this.nickName = userDTO.getNickName() == null ? "": userDTO.getNickName();
this.role = UserRole.valueOf(userDTO.getRole());
this.loginInformation = null;
if(userDTO.getLoginInformation() != null) {
setLoginInformation(new LoginInformation(userDTO.getLoginInformation()));
} else {
setLoginInformation(new LoginInformation());
}
(userDTO.getUserGameStates() == null ? new ArrayList<GameStateDTO>() : userDTO.getUserGameStates())
.stream()
.map(x -> new UserGameState((UserGameStateDTO) x))
.forEach(this::addUserGameState);
}
游戏状态
@Getter
@Setter
@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class GameState extends AbstractModel{
private String name;
private String description;
private String image;
@ToString.Exclude
@EqualsAndHashCode.Exclude
@OneToMany(cascade = {CascadeType.PERSIST}, fetch = FetchType.LAZY, mappedBy = "user")
private List<UserGameState> userGameStates = new ArrayList<>();
//DTO Constructor
public GameState(GameStateDTO gameStateDTO){
this.identifier = Optional.ofNullable(gameStateDTO.getIdentifier())
.orElse(UUID.randomUUID().toString());
this.name = gameStateDTO.getName() == null ? "": gameStateDTO.getName();
this.description = gameStateDTO.getDescription() == null ? "": gameStateDTO.getDescription();
this.image = gameStateDTO.getImage() == null ? "": gameStateDTO.getImage();
(gameStateDTO.getUserGameStates() == null ? new ArrayList<UserDTO>() : gameStateDTO.getUserGameStates())
.stream()
.map(x -> new UserGameState((UserGameStateDTO) x))
.forEach(this::addUserGameState);
}
//----------------------1:1 Relationship Methods----------------------
//----------------------1:N Relationship Methods----------------------
public void addUserGameState(UserGameState userGameState) {
if (userGameStates.contains(userGameState)) {
return;
}
userGameStates.add(userGameState);
userGameState.setGameState(this);
}
public void removeUserGameState(UserGameState userGameState) {
if (!userGameStates.contains(userGameState)) {
return;
}
userGameState.setGameState(null);
userGameStates.remove(userGameState);
}
//----------------------N:1 Relationship Methods----------------------
//----------------------N:M Relationship Methods----------------------
}
UserGameSatet(自定义 N:M Table)
@Getter
@Setter
@Entity
@Builder
@ToString
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
public class UserGameState{
@EmbeddedId
private User_GameState_PK id;
@ManyToOne(cascade = {CascadeType.PERSIST}, fetch = FetchType.LAZY)
@MapsId("user_id")
@JoinColumn(name = "USER_ID", insertable = false, updatable = false)
private User user;
@ManyToOne(cascade = {CascadeType.PERSIST}, fetch = FetchType.LAZY)
@MapsId("gameState_id")
@JoinColumn(name = "GAMESTATE_ID", insertable = false, updatable = false)
private GameState gameState;
//add Role later
public UserGameState(User u, GameState gs) {
// create primary key
this.id = new User_GameState_PK(u.getId(), gs.getId());
// initialize attributes
setUser(u);
setGameState(gs);
}
public UserGameState(UserGameStateDTO userGameStateDTO){
//this.id =
this.user = null;
this.gameState = null;
}
//----------------------1:1 Relationship Methods----------------------
//----------------------1:N Relationship Methods----------------------
//----------------------N:1 Relationship Methods----------------------
public void setUser(User user) {
if (Objects.equals(this.user, user)) {
return;
}
User oldUser = this.user;
this.user = user;
if (oldUser != null) {
oldUser.removeUserGameState(this);
}
if (user != null) {
user.addUserGameState(this);
}
}
public void setGameState(GameState gameState) {
if (Objects.equals(this.gameState, gameState)) {
return;
}
GameState oldGameState = this.gameState;
this.gameState = gameState;
if (oldGameState != null) {
oldGameState.removeUserGameState(this);
}
if (oldGameState != null) {
oldGameState.addUserGameState(this);
}
}
//----------------------N:M Relationship Methods----------------------
}
User_GameState_PK(组合键)
@Embeddable
@Builder
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
public class User_GameState_PK implements Serializable {
@Column(name = "USER_ID")
private Long user_id;
@Column(name = "GAMESTATE_ID")
private Long gameState_id;
public User_GameState_PK(long user_id, long gameState_id){
this.user_id = user_id;
this.gameState_id = gameState_id;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass())
return false;
User_GameState_PK that = (User_GameState_PK) o;
return Objects.equals(user_id, that.user_id) &&
Objects.equals(gameState_id, that.gameState_id);
}
@Override
public int hashCode() {
return Objects.hash(user_id, gameState_id);
}
}
在我的服务中保存连接的方法
(GameState 和 User 都已经实例化,该方法获取两个对象的标识符,从数据库中检索它们并添加它们之间的关系。)
public Optional<GameStateDTO> addUserToGameState(String identifierGS, String identifierU) {
GameState gameState = gameStateRepo.findByIdentifier(identifierGS)
.orElseThrow(() -> new IllegalArgumentException("GameState ID has no according GameState."));
User user = userRepo.findByIdentifier(identifierU)
.orElseThrow(() -> new IllegalArgumentException("User ID has no according User."));
//Custom N:M Connection Part
UserGameState connection = new UserGameState(user, gameState);
userGameStateRepo.save(connection);
return Optional.of(gameState)
.map(m -> convertModelIntoDTO(m));
}
我设法设置了 N:M table 及其组合键。我使用简单的 CRUD 路由对其进行了测试,它们有效。
接下来我尝试设置一些路线,以便人们可以实际加入游戏 (/gamestate),此时它会在保存时抛出以下异常。
javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session : [com.Astralis.backend.model.UserGameState#User_GameState_PK(user_id=1, gameState_id=7)]
在 Whosebug 上阅读了一些 post 之后,我尝试将 Cascadetype 更改为 .MERGE,这导致了这个异常。
javax.persistence.EntityNotFoundException: ...
真的我在这里迷路了,感觉如果我使用.PERSIST,Hibernate 抱怨它在保存关系时复制了自己。如果我将其更改为 .MERGE,它会抱怨该值一开始就不存在。
我非常感谢任何让我更接近解决方案的面包屑,因为事实证明这对项目来说是一个巨大的障碍,我已经尝试了我能想到的一切。
于是又找了几天终于解决了
为此,我首先使用指南中的数据结构和我项目的 service/controller 结构重新制作了指南的项目。测试它是否可行,正如它所做的那样,我刚刚开始将模型相互比较并尝试所有不同的可能性,以找出真正导致问题的原因。
我有六个复制和粘贴(有点)错误导致 Hibernate 错误地将 table 列相互关联。这些是:
- 在用户中:
...
@ToString.Exclude
@EqualsAndHashCode.Exclude
@OneToMany(
cascade = {CascadeType.PERSIST},
fetch = FetchType.LAZY,
mappedBy = "user",// changed from gameState to user
orphanRemoval = true
)
private List<UserGameState> userGameStates = new ArrayList<>();
...
- 在 GameState 中相反:
...
@ToString.Exclude
@EqualsAndHashCode.Exclude
@OneToMany(cascade = {CascadeType.PERSIST},
fetch = FetchType.LAZY,
mappedBy = "gameState",// changed from user to gameState
orphanRemoval = true)
private List<UserGameState> userGameStates = new ArrayList<>();
...
3&4。 JoinColumn 注释是不必要的,似乎我将多个指南合并到一个项目中。这导致了更多问题:
...
@ManyToOne(
cascade = {CascadeType.PERSIST},
fetch = FetchType.LAZY)
@MapsId("user_id")
//@JoinColumn(name = "USER_ID", insertable = false, updatable = false) //this one removed
private User user;
@ManyToOne(
cascade = {CascadeType.PERSIST},
fetch = FetchType.LAZY)
@MapsId("gameState_id")
//@JoinColumn(name = "GAMESTATE_ID", insertable = false, updatable = false) //this one removed
private GameState gameState;
...
5&6。在 UserGameState 的“continuity keeper”方法中有两个小的复制和粘贴错误:
...
public void setGameState(GameState gameState) {
if (Objects.equals(this.gameState, gameState)) {
return;
}
GameState oldGameState = this.gameState;
this.gameState = gameState;
if (oldGameState != null) {
oldGameState.removeUserGameState(this);
}
//I copied the previous if block, and replaced the remove... with add...
//But I didn't change the oldGameState to gameState.
//This didn't throw any errors, and actually it still created the relations properly, but I am pretty sure it would cause issues further down the line.
if (gameState != null) {
gameState.addUserGameState(this);
}
}
...
那么现在它是如何工作的:
和以前一样,当调用具有连接的 GameState 和 User 的标识符的路由时,将调用服务“addUserToGameState”,获取具有给定标识符的模型。
...
public Optional<GameStateDTO> addUserToGameState(String identifierGS, String identifierU) {
GameState gameState = gameStateRepo.findByIdentifier(identifierGS)
.orElseThrow(() -> new IllegalArgumentException("GameState ID has no according GameState."));
User user = userRepo.findByIdentifier(identifierU)
.orElseThrow(() -> new IllegalArgumentException("User ID has no according User."));
//Custom N:M Connection Part
UserGameState connection = new UserGameState(user, gameState);
return Optional.of(gameState)
.map(m -> convertModelIntoDTO(m));
}
...
之后调用 UserGameState 构造器,它设置和创建组合键并调用相关 User/GameState 字段的 setter 方法。
...
public UserGameState(User u, GameState gs) {
// create primary key
this.id = new User_GameState_PK(u.getId(), gs.getId());
// initialize attributes
setUser(u);
setGameState(gs);
}
...
我写 setter 的方式是,它们同时检查添加的模型是否存在关系一致性问题,并根据它们是否被新编辑或替换来调整它们的字段。
...
public void setUser(User user) {
if (Objects.equals(this.user, user)) {
return;
}
User oldUser = this.user;
this.user = user;
if (oldUser != null) {
oldUser.removeUserGameState(this);
}
if (user != null) {
user.addUserGameState(this);
}
}
public void setGameState(GameState gameState) {
if (Objects.equals(this.gameState, gameState)) {
return;
}
GameState oldGameState = this.gameState;
this.gameState = gameState;
if (oldGameState != null) {
oldGameState.removeUserGameState(this);
}
if (gameState != null) {//copy paste error
gameState.addUserGameState(this);
}
}
...
所以我的第二个 post。这次我在做我的一个激情项目,结果比我想象的要复杂得多,我再次需要一些帮助。
我有两个实体:Gamestate 和 User。
用户应该能够加入多个游戏(/gamestates)。游戏(/游戏状态)应该有很多人加入。因此,它表示为 N:M 关系。
根据谁加入以及他们加入的时间,他们应该具有不同的角色,从而赋予他们在应用程序中的不同权利。这意味着我需要与自定义字段的 N:M 关系,因此我必须自己对关系 table 进行建模。我就到此为止了。
抽象模型:
@EqualsAndHashCode
@Getter
@Setter
@ToString
public abstract class AbstractModel {
@Id
@GeneratedValue
protected Long id;
@NotNull
protected String identifier;
}
用户
@Getter
@Setter
@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class User extends AbstractModel{
private String nickName;
private UserRole role;
@ToString.Exclude
@EqualsAndHashCode.Exclude
@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "user", orphanRemoval = true)
private LoginInformation loginInformation;
@ToString.Exclude
@EqualsAndHashCode.Exclude
@OneToMany(cascade = {CascadeType.PERSIST}, fetch = FetchType.LAZY, mappedBy = "gameState")
private List<UserGameState> userGameStates = new ArrayList<>();
//DTO Constructor
public User(UserDTO userDTO){
this.identifier = Optional.ofNullable(userDTO.getIdentifier())
.orElse(UUID.randomUUID().toString());
this.nickName = userDTO.getNickName() == null ? "": userDTO.getNickName();
this.role = UserRole.valueOf(userDTO.getRole());
this.loginInformation = null;
if(userDTO.getLoginInformation() != null) {
setLoginInformation(new LoginInformation(userDTO.getLoginInformation()));
} else {
setLoginInformation(new LoginInformation());
}
(userDTO.getUserGameStates() == null ? new ArrayList<GameStateDTO>() : userDTO.getUserGameStates())
.stream()
.map(x -> new UserGameState((UserGameStateDTO) x))
.forEach(this::addUserGameState);
}
游戏状态
@Getter
@Setter
@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class GameState extends AbstractModel{
private String name;
private String description;
private String image;
@ToString.Exclude
@EqualsAndHashCode.Exclude
@OneToMany(cascade = {CascadeType.PERSIST}, fetch = FetchType.LAZY, mappedBy = "user")
private List<UserGameState> userGameStates = new ArrayList<>();
//DTO Constructor
public GameState(GameStateDTO gameStateDTO){
this.identifier = Optional.ofNullable(gameStateDTO.getIdentifier())
.orElse(UUID.randomUUID().toString());
this.name = gameStateDTO.getName() == null ? "": gameStateDTO.getName();
this.description = gameStateDTO.getDescription() == null ? "": gameStateDTO.getDescription();
this.image = gameStateDTO.getImage() == null ? "": gameStateDTO.getImage();
(gameStateDTO.getUserGameStates() == null ? new ArrayList<UserDTO>() : gameStateDTO.getUserGameStates())
.stream()
.map(x -> new UserGameState((UserGameStateDTO) x))
.forEach(this::addUserGameState);
}
//----------------------1:1 Relationship Methods----------------------
//----------------------1:N Relationship Methods----------------------
public void addUserGameState(UserGameState userGameState) {
if (userGameStates.contains(userGameState)) {
return;
}
userGameStates.add(userGameState);
userGameState.setGameState(this);
}
public void removeUserGameState(UserGameState userGameState) {
if (!userGameStates.contains(userGameState)) {
return;
}
userGameState.setGameState(null);
userGameStates.remove(userGameState);
}
//----------------------N:1 Relationship Methods----------------------
//----------------------N:M Relationship Methods----------------------
}
UserGameSatet(自定义 N:M Table)
@Getter
@Setter
@Entity
@Builder
@ToString
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
public class UserGameState{
@EmbeddedId
private User_GameState_PK id;
@ManyToOne(cascade = {CascadeType.PERSIST}, fetch = FetchType.LAZY)
@MapsId("user_id")
@JoinColumn(name = "USER_ID", insertable = false, updatable = false)
private User user;
@ManyToOne(cascade = {CascadeType.PERSIST}, fetch = FetchType.LAZY)
@MapsId("gameState_id")
@JoinColumn(name = "GAMESTATE_ID", insertable = false, updatable = false)
private GameState gameState;
//add Role later
public UserGameState(User u, GameState gs) {
// create primary key
this.id = new User_GameState_PK(u.getId(), gs.getId());
// initialize attributes
setUser(u);
setGameState(gs);
}
public UserGameState(UserGameStateDTO userGameStateDTO){
//this.id =
this.user = null;
this.gameState = null;
}
//----------------------1:1 Relationship Methods----------------------
//----------------------1:N Relationship Methods----------------------
//----------------------N:1 Relationship Methods----------------------
public void setUser(User user) {
if (Objects.equals(this.user, user)) {
return;
}
User oldUser = this.user;
this.user = user;
if (oldUser != null) {
oldUser.removeUserGameState(this);
}
if (user != null) {
user.addUserGameState(this);
}
}
public void setGameState(GameState gameState) {
if (Objects.equals(this.gameState, gameState)) {
return;
}
GameState oldGameState = this.gameState;
this.gameState = gameState;
if (oldGameState != null) {
oldGameState.removeUserGameState(this);
}
if (oldGameState != null) {
oldGameState.addUserGameState(this);
}
}
//----------------------N:M Relationship Methods----------------------
}
User_GameState_PK(组合键)
@Embeddable
@Builder
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
public class User_GameState_PK implements Serializable {
@Column(name = "USER_ID")
private Long user_id;
@Column(name = "GAMESTATE_ID")
private Long gameState_id;
public User_GameState_PK(long user_id, long gameState_id){
this.user_id = user_id;
this.gameState_id = gameState_id;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass())
return false;
User_GameState_PK that = (User_GameState_PK) o;
return Objects.equals(user_id, that.user_id) &&
Objects.equals(gameState_id, that.gameState_id);
}
@Override
public int hashCode() {
return Objects.hash(user_id, gameState_id);
}
}
在我的服务中保存连接的方法
(GameState 和 User 都已经实例化,该方法获取两个对象的标识符,从数据库中检索它们并添加它们之间的关系。)
public Optional<GameStateDTO> addUserToGameState(String identifierGS, String identifierU) {
GameState gameState = gameStateRepo.findByIdentifier(identifierGS)
.orElseThrow(() -> new IllegalArgumentException("GameState ID has no according GameState."));
User user = userRepo.findByIdentifier(identifierU)
.orElseThrow(() -> new IllegalArgumentException("User ID has no according User."));
//Custom N:M Connection Part
UserGameState connection = new UserGameState(user, gameState);
userGameStateRepo.save(connection);
return Optional.of(gameState)
.map(m -> convertModelIntoDTO(m));
}
我设法设置了 N:M table 及其组合键。我使用简单的 CRUD 路由对其进行了测试,它们有效。
接下来我尝试设置一些路线,以便人们可以实际加入游戏 (/gamestate),此时它会在保存时抛出以下异常。
javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session : [com.Astralis.backend.model.UserGameState#User_GameState_PK(user_id=1, gameState_id=7)]
在 Whosebug 上阅读了一些 post 之后,我尝试将 Cascadetype 更改为 .MERGE,这导致了这个异常。
javax.persistence.EntityNotFoundException: ...
真的我在这里迷路了,感觉如果我使用.PERSIST,Hibernate 抱怨它在保存关系时复制了自己。如果我将其更改为 .MERGE,它会抱怨该值一开始就不存在。
我非常感谢任何让我更接近解决方案的面包屑,因为事实证明这对项目来说是一个巨大的障碍,我已经尝试了我能想到的一切。
于是又找了几天终于解决了
为此,我首先使用指南中的数据结构和我项目的 service/controller 结构重新制作了指南的项目。测试它是否可行,正如它所做的那样,我刚刚开始将模型相互比较并尝试所有不同的可能性,以找出真正导致问题的原因。
我有六个复制和粘贴(有点)错误导致 Hibernate 错误地将 table 列相互关联。这些是:
- 在用户中:
...
@ToString.Exclude
@EqualsAndHashCode.Exclude
@OneToMany(
cascade = {CascadeType.PERSIST},
fetch = FetchType.LAZY,
mappedBy = "user",// changed from gameState to user
orphanRemoval = true
)
private List<UserGameState> userGameStates = new ArrayList<>();
...
- 在 GameState 中相反:
...
@ToString.Exclude
@EqualsAndHashCode.Exclude
@OneToMany(cascade = {CascadeType.PERSIST},
fetch = FetchType.LAZY,
mappedBy = "gameState",// changed from user to gameState
orphanRemoval = true)
private List<UserGameState> userGameStates = new ArrayList<>();
...
3&4。 JoinColumn 注释是不必要的,似乎我将多个指南合并到一个项目中。这导致了更多问题:
...
@ManyToOne(
cascade = {CascadeType.PERSIST},
fetch = FetchType.LAZY)
@MapsId("user_id")
//@JoinColumn(name = "USER_ID", insertable = false, updatable = false) //this one removed
private User user;
@ManyToOne(
cascade = {CascadeType.PERSIST},
fetch = FetchType.LAZY)
@MapsId("gameState_id")
//@JoinColumn(name = "GAMESTATE_ID", insertable = false, updatable = false) //this one removed
private GameState gameState;
...
5&6。在 UserGameState 的“continuity keeper”方法中有两个小的复制和粘贴错误:
...
public void setGameState(GameState gameState) {
if (Objects.equals(this.gameState, gameState)) {
return;
}
GameState oldGameState = this.gameState;
this.gameState = gameState;
if (oldGameState != null) {
oldGameState.removeUserGameState(this);
}
//I copied the previous if block, and replaced the remove... with add...
//But I didn't change the oldGameState to gameState.
//This didn't throw any errors, and actually it still created the relations properly, but I am pretty sure it would cause issues further down the line.
if (gameState != null) {
gameState.addUserGameState(this);
}
}
...
那么现在它是如何工作的: 和以前一样,当调用具有连接的 GameState 和 User 的标识符的路由时,将调用服务“addUserToGameState”,获取具有给定标识符的模型。
...
public Optional<GameStateDTO> addUserToGameState(String identifierGS, String identifierU) {
GameState gameState = gameStateRepo.findByIdentifier(identifierGS)
.orElseThrow(() -> new IllegalArgumentException("GameState ID has no according GameState."));
User user = userRepo.findByIdentifier(identifierU)
.orElseThrow(() -> new IllegalArgumentException("User ID has no according User."));
//Custom N:M Connection Part
UserGameState connection = new UserGameState(user, gameState);
return Optional.of(gameState)
.map(m -> convertModelIntoDTO(m));
}
...
之后调用 UserGameState 构造器,它设置和创建组合键并调用相关 User/GameState 字段的 setter 方法。
...
public UserGameState(User u, GameState gs) {
// create primary key
this.id = new User_GameState_PK(u.getId(), gs.getId());
// initialize attributes
setUser(u);
setGameState(gs);
}
...
我写 setter 的方式是,它们同时检查添加的模型是否存在关系一致性问题,并根据它们是否被新编辑或替换来调整它们的字段。
...
public void setUser(User user) {
if (Objects.equals(this.user, user)) {
return;
}
User oldUser = this.user;
this.user = user;
if (oldUser != null) {
oldUser.removeUserGameState(this);
}
if (user != null) {
user.addUserGameState(this);
}
}
public void setGameState(GameState gameState) {
if (Objects.equals(this.gameState, gameState)) {
return;
}
GameState oldGameState = this.gameState;
this.gameState = gameState;
if (oldGameState != null) {
oldGameState.removeUserGameState(this);
}
if (gameState != null) {//copy paste error
gameState.addUserGameState(this);
}
}
...