[英]Hibernate one to one for two columns (two fields of the same type in an Entity class)
假設我有一個 Noughts and Crosses 游戲(井字游戲):
游戲:
id crosses_player_id noughts_player_id...
球員:
id alignment game_id
由於只有 2 個玩家,我認為沒有必要創建一個單獨的表,所以我這樣做了:
@Entity
@Table(name = "games")
public class Game {
@Id @GeneratedValue
private int id;
@OneToOne(mappedBy = "game")
@JoinColumn(name = "crosses_player_id")
private Player crossesPlayer;
@OneToOne(mappedBy = "game")
@JoinColumn(name = "noughts_player_id")
private Player noughtsPlayer;
private List<Move> moves = new ArrayList<>();
private List<Field> fields = new ArrayList<>();
...
@Entity
@Table(name = "players")
public class Player {
@ManyToOne
@JoinColumn(name="user_id", nullable=false)
private User user;
private enum Alignment {
NOUGHTS, CROSSES
};
private Alignment alignment;
@OneToOne(mappedBy = ?)
private Game game;
...
但是我不確定在Player
類的@OneToOne(mappedBy = ?)
里面放什么。
我找到的最接近的答案是這個:
https://stackoverflow.com/a/13944195/4759176
class TeamPlay {
@ManyToOne
Team homeTeam;
@ManyToOne
Team awayTeam;
}
class Team {
@OneToMany(mappedBy="homeTeam")
Set<TeamPlay> homeTeamPlays;
@OneToMany(mappedBy="awayTeam")
Set<TeamPlay> awayTeamPlays;
}
然而,看起來Team
類可以有很多可能的游戲,而在我的情況下,1 個玩家只有 1 個游戲。 玩家基本上是用戶的 id、游戲的 id 和 allighment(noughts 或 crosses),並且在游戲結束后,任何新游戲都不會再引用同一個玩家。 也許我應該這樣做:
球員:
@OneToOne(mappedBy = "crossesPlayer")
private Game crossesGame;
@OneToOne(mappedBy = "noughtsPlayer")
private Game noughtsGame;
但是,一個球員只能是零或交叉球員,所以這些領域之一總是為空?
@parsecer ...對我來說,你的問題在於對你的數據模型的誤解; 引用你:
玩家基本上是用戶的 id、游戲的 id 和 allighment(noughts 或 crosses),並且在游戲結束后,任何新游戲都不會再次引用同一玩家
這意味着“游戲”是一個強大的實體,不應該有對玩家的引用(即外鍵)(即表“游戲”不應該包含列:crosses_player_id、noughts_player_id)......這也意味着“玩家“是一個弱實體,只存在於“游戲”的上下文中(所以,這里可以有一個外國專欄來游戲)......
假設所有這些,您的模型和映射都會變得更清晰,例如:
@Entity
@Table(name = "players")
public class Player {
/**
* Player's ID.
*/
@Id
private int id;
/**
* Game to which this Player belongs.
*/
@ManyToOne(optional = false)
@JoinColumn(name = "game_id", nullable = false, updatable = false)
private Game game;
/**
* Player's alignment in the game.
*/
@Enumerated(EnumType.STRING)
private Alignment alignment;
}
@Entity
@Table(name = "games")
public class Game {
/**
* Game's Id.
*/
@Id
private int id;
/**
* Players in this game.
*/
@OneToMany(mappedBy = "game", cascade = PERSIST)
@MapKey(name = "alignment")
private Map<Alignment, Player> players = new HashMap<>();
...
public List<Player> getPlayers() {
return Arrays.asList( this.players.values() );
}
public Player getCrossesPlayer() {
return this.players.get(Alignment.CROSSES);
}
public Player getNoughtesPlayer() {
return this.players.get(Alignment.NOUGHTES);
}
}
@ManyToOne(optional=false)
@JoinColumn(name="game_id")
private Game game;
Player 上的 Game 中的 OneToOne 已經在這些實體之間創建了您想要的關系。 使用帶有 Joining 的游戲主鍵的 ManyToOne,您將能夠看到玩家正在參與的游戲(從數據的玩家視圖中)。 使用該數據邏輯,最多兩個玩家可以在數據層上參與游戲。 但是,您仍然可以創建一個 Player 並為他分配一個他不玩的游戲。
Player player1ThatDoesentPlay // = new Player(...);
player1ThatDoesentPlay.setGame(thatGame) // with thatGame could already have the participating players
我建議你創建一個像
Game checkandsetGame(Game game){
if (game.crossesplayer == null | game.noughtsplayer == null && game != null){
return setGame(game);
}
// else {probably want to throw an error here, that indicates that the player couldn't be assigned to a game}
}
一般來說,我建議你過度考慮你的數據模式,也許做一些像@Carlitos Way 建議的事情
在您的模型中,您希望游戲和玩家之間建立 1-1 的關系。 但是兩個玩家將共享一個游戲,因此單個游戲 ID 可能會從玩家表中多次引用,但單個玩家只能從游戲表中引用一次。
您弄錯的一件事是使用了mappedBy
屬性,該屬性將用於關系的非擁有方。 它告訴 JPA '我是從另一個表中的那個外鍵引用的。 請為我取回並在此處參考'。 請注意,您必須維護關系的雙方,即,當您調用game.setPlayer(player)
,您必須確保您也調用player.setGame(game)
。 另請注意,雖然 @OneToOne 關系可能沒問題,但它不可擴展,您最好避免使用它。
您與玩家之間的關系由他們的對齊方式定義。 事實上,您的玩家實體似乎定義了用戶和游戲之間的關系。
我為您的用例提供的解決方案是使用GamePosition
表,該表將允許您強制單個玩家只是單個游戲的一部分。
@Entity
@Table(name = "players")
public class Player {
@ManyToOne
@JoinColumn(name="user_id", nullable=false)
private User user;
}
@Entity
@Table(name = "game")
public class Game {
@Id @GeneratedValue
private int id;
}
@Entity
@Table(name = "game_position")
public class GamePosition {
@OneToOne
@JoinColumn(name="player_id", unique=true)
// Making this column unique will prevent a single player to be part of two games
private Player player;
@ManyToOne
@JoinColumn(name="game_id")
private Game game;
@Enumarated
private Alignement alignement;
}
然后如果你想要雙向關系,你可以使用mappedBy 屬性。 在玩家表中:
@OneToOne(mappedBy="player")
private GamePosition gamePosition;
或在游戲桌上:
@OneToMany(mappedBy="game")
private List<GamePosition> gamePositions;
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.