web-dev-qa-db-ja.com

JPA 2:外部キーでの複数列の使用

永続性プロバイダーとしてHibernateを使用し、JPA2を使用してエンティティーをモデル化しています。

今、質問が出てきました。あなたが私を助けてくれることを願っています。

私のアプリケーションでは、ゲームを開いて、その中にプレーヤーのグループを作成し、マップ上を歩き回ることができます(タイル(2d))。

最初に私のエンティティの定義:ゲーム:

@Entity
public class Game implements Serializable {
@Id
@SequenceGenerator(name = "gen_gameid", sequenceName = "seq_gameid")
@GeneratedValue(generator="gen_gameid")
private long gameid;

/**
 * Playing Characters.
 */
@OneToMany(mappedBy = "game")
private List<Character> characters;
private int round = 0;

@OneToMany(mappedBy="game")
private List<Tile> tiles;

@OneToMany(mappedBy="game")
private List<Group> group;

タイル(タイルはテンプレートから作成され、1つのゲームにのみ属します):

@Entity @IdClass(TileId.class)
public class Tile implements Serializable{
    private static final long serialVersionUID = 2039974286110729270L;

    @Id
    private int x;

    @Id
    private int y;

    @Id @ManyToOne @JoinColumn(name="gameid")
    private Game game;

    @OneToOne(mappedBy="tile")
    private Character character;
}

キャラクター:

@ManyToOne
@JoinColumn(name="gameid", referencedColumnName = "gameid")
private Game game;

@ManyToOne
@JoinColumns({
    @JoinColumn(name="groupgameid", referencedColumnName = "gameid"),
    @JoinColumn(name="groupTag", referencedColumnName = "grouptag")
})
private Group group;

    @OneToOne
    @JoinColumns({    
      @JoinColumn(name = "x", referencedColumnName = "x"),
      @JoinColumn(name = "y", referencedColumnName = "y"),
      @JoinColumn(name = "tilegameid", referencedColumnName = "gameid")
    })
    private Tile tile;

ご覧のとおり、gameid列の名前をgroupgameidとtilegameidに変更する必要がありました。キャラクターのgameidが一度だけ必要になるので、これはあまりきれいではありません。タイルおよびグループのfk列をinsertable = falseでマークするには、updateable = falseでSQLを生成できますが、この値を変更/設定することはできません。

確かに、グループとタイルに人工pkを導入することはできますが、さらに結合が必要になります。

JPAは、gameid列を他の名前で複数回許可する必要があるという制限がありますか?それとも私のデザインは最適ではありませんか?

フィードバックと感謝を前もって楽しみにしています。あいさつマーカス

PS(編集):瞬間に、モデルが完成するまで、起動時に休止状態でシェマを生成させます。しかし、ここに生成されたシェマがあります(ビットが単純化され、いくつかの重要でないフィールドが切り取られています):

CREATE TABLE Character
(
  charid bigint NOT NULL,
  gameid bigint,
  grouptag character varying(255),
  x integer,
  y integer,
  CONSTRAINT hero_pkey PRIMARY KEY (charid),
  CONSTRAINT fkd4addb09308bc3b822441a FOREIGN KEY (gameid)
  REFERENCES game (gameid) MATCH SIMPLE
  ON UPDATE NO ACTION ON DELETE NO ACTION,
  CONSTRAINT fkd4addb093091cb6522441a FOREIGN KEY (gameid, x, y)
  REFERENCES tile (gameid, x, y) MATCH SIMPLE
  ON UPDATE NO ACTION ON DELETE NO ACTION,
  CONSTRAINT fkd4addb09c018f3ae22441a FOREIGN KEY (gameid, grouptag)
  REFERENCES gamegroup (gameid, grouptag) MATCH SIMPLE
  ON UPDATE NO ACTION ON DELETE NO ACTION
) 

CREATE TABLE tile (
  x integer NOT NULL,
  y integer NOT NULL,
  gameid bigint NOT NULL,
  CONSTRAINT tile_pkey PRIMARY KEY (gameid, x, y),
  CONSTRAINT fk27c6ce308bc3b8 FOREIGN KEY (gameid)
  REFERENCES game (gameid) MATCH SIMPLE
  ON UPDATE NO ACTION ON DELETE NO ACTION)

CREATE TABLE gamegroup
(
  grouptag character varying(255) NOT NULL,
  gameid bigint NOT NULL,
     CONSTRAINT gamegroup_pkey PRIMARY KEY (gameid, grouptag),
  CONSTRAINT fk3c1c51cd308bc3b8 FOREIGN KEY (gameid)
  REFERENCES game (gameid) MATCH SIMPLE
  ON UPDATE NO ACTION ON DELETE NO ACTION
)

PS 2:私はすでに, insertable = false, updatable = falseで遊んでいました。たとえば、グループJoinColumnsを次のように変更すると、次のようになります。

@ManyToOne
@JoinColumns({
    @JoinColumn(name="gameid", referencedColumnName = "gameid", insertable = false, updatable = false ),
    @JoinColumn(name="groupTag", referencedColumnName = "grouptag")
})
private Group group;

混合が許可されていないというエラーが表示されます:原因:org.hibernate.AnnotationException:プロパティで挿入可能な列と挿入不可能な列を混合することは許可されていません:net.hq.model.Charactergroup

両方をinsertable = falseにすると、グループタグを設定できなくなります。 groupTagは挿入後も空のままで、gameidが設定されています。 :-/

グループにキャラクターを追加する方法:

// Create game
Game game = new Game();
game.addCharacter(max);
em.persist(game);

// Group
Group heroGroup = new Group(game, "HEROES");
heroGroup.addCharacter(max);
em.persist(game);

グループクラスのメソッド:

public void addCharacter(Character character){
    if(this.characters == null)
        this.characters = new ArrayList<Character>();

    this.characters.add(character);
    character.setGroup(this);
}
15
mkuff

あなたはこれをする必要があります:

@ManyToOne
@JoinColumns({
    @JoinColumn(name="gameid", referencedColumnName = "gameid", insertable = false, updatable = false ),
    @JoinColumn(name="groupTag", referencedColumnName = "grouptag", insertable = false, updatable = false)
})
private Group group;
8
Sanama Na

これらのinsertable = false, updateable = false@JoinColumnを使用できませんか?

私の知る限り、gameidプロパティを設定することで、gameを一度初期化できます。その後、TilesとGroupsは同じGameに属するため、変更する必要はありません。

3
axtavt