Спящий режим один в один для двух столбцов (два поля одного типа в классе Entity) - PullRequest
0 голосов
/ 08 февраля 2020

Предположим, у меня есть игра Крестики-нолики (ti c ta c toe):

игры :

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;
...

Однако Я не уверен, что поместить в @OneToOne(mappedBy = ?) в Player классе.

Самый близкий ответ, который я нашел, это:

{ ссылка }

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 игру. Игрок в основном является идентификатором пользователя, идентификатором игры и альянсом (крестики или нолики), и после того, как игра окончена, на этого же игрока больше никогда не ссылаются из новых игр. Может быть, я должен сделать это так:

Игрок :

@OneToOne(mappedBy = "crossesPlayer")
private Game crossesGame;

@OneToOne(mappedBy = "noughtsPlayer")
private Game noughtsGame;

но тогда игрок может только быть либо нолями, либо пересекает игрока , так что одно из этих полей всегда будет нулевым?

Ответы [ 3 ]

2 голосов
/ 11 февраля 2020

@ parsecer ... Для меня ваша проблема заключается в неправильном представлении о вашей модели данных; цитирую вас:

Игрок - это, в основном, идентификатор пользователя, идентификатор игры и альянс (крестики или нолики), и после того, как игра окончена, на этого игрока больше никогда не ссылаются из любых новых игр

Это означает, что «Игра» является сильной сущностью и не должна иметь ссылок (т. Е. Внешних ключей) на игроков (т. Е. Таблица «игры» не должна содержать столбцы: crosses_player_id, noughts_player_id) ... it также означает, что «Игрок» является слабой сущностью, которая существует только в контексте «Игры» (так что здесь нормально иметь чужой столбец для игры) ...

Принимая все это, ваша модель становится чище, а также ваше отображение, например:

@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);
    }
}
0 голосов
/ 11 февраля 2020

В вашей модели вы хотите соотношение 1-1 между вашей игрой и вашим игроком. Но два игрока будут совместно использовать игру, поэтому на один игровой идентификатор можно сослаться несколько раз с игрового стола, но на одного игрока можно ссылаться только один раз с игрового стола.

Единственное, что вы ошиблись, это использование атрибута 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;
0 голосов
/ 11 февраля 2020
 @ManyToOne(optional=false) 
 @JoinColumn(name="game_id")
 private Game game;

OneToOne от Game on the Player уже создаст необходимые отношения между этими объектами. С первичным ключом ManyToOne с присоединением к играм вы сможете увидеть, в какую игру участвует игрок (с точки зрения игроков на данных). С этим логом данных c максимум два игрока могут участвовать в игре на уровне данных. Однако вы все равно можете создать Игрока и назначить ему игру, в которую он не будет играть.

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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...