JPA 2:外键中的多列用法

2022-09-03 02:10:57

我使用Hibernate作为持久性提供者,并使用JPA 2对我的实体进行建模。

现在出现了一个问题,我希望你能帮助我。

在我的应用程序中,您可以打开一个游戏,在其中创建玩家组并在地图上走动(瓷砖(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;

磁贴(磁贴将从模板创建,并且仅属于一个游戏):

@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(编辑):在启动时,我让shema通过休眠生成,直到我的模型完成。但这里是生成的 shema(稍微简化并切掉了一些不重要的字段):

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:我已经玩过.例如,当我将组 JoinColumns 更改为:, insertable = false, updatable = false

@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

当我使两者都可插入=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);
}

答案 1

您需要执行以下操作:

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

编辑:如注释中所述,是一个可重复的注释(自Java 8以来),不需要包装。这简化了解决方案,以便:@JoinColumn

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

答案 2

您确定不能用于这些吗?insertable = false, updateable = false@JoinColumn

据我所知,您可以通过设置属性来初始化一次,之后您不需要更改它,因为s和s属于同一个。gameidgameTileGroupGame


推荐