行为类似于 @Entity 和 @Embeddable 的类
Posted
技术标签:
【中文标题】行为类似于 @Entity 和 @Embeddable 的类【英文标题】:A class that behaves like @Entity and @Embeddable 【发布时间】:2009-06-22 22:37:16 【问题描述】:我在 Team 和 Player 类之间有一个单向的 @OneToMany 关系。我想在你的 Players 中保存一个 Team 对象。玩家的标识符由 Team 外键和列表索引组成,如下所示。我有一个这样的映射,因为我需要同时保存团队和你的玩家。
@Entity
public class Team
@Id
@GeneratedValue
private Integer id;
@CollectionOfElements
@JoinTable(
name="PLAYER",
joinColumns=@JoinColumn(name="TEAM_ID"))
@IndexColumn(name="PLAYER_INDEX")
private List<Player> playerList = new ArrayList<Player>();
@Embeddable
public class Player
// Player's getter's and setter's
所以如果我使用以下内容
Team team = new Team();
team.getPlayerList().add(new Player());
team.getPlayerList().add(new Player());
session.save(team); // It works!
无论您使用@CollectionsOfElements,都会发生这种情况,Player 类需要@Embeddable 注解,而不是@Entity。 JPA 不允许同时使用@Entity 和@Embeddable。 Player 也是一个 @Entity - 它与其他实体有关系。
有人知道我可以通过在 Player 类中使用 CascadeType.PERSIST 和 @Entity 而不是 @Embeddable 来保存团队和玩家(单向关系)吗?
记住COMPOUND主键需要在保存前分配,但是Team的标识符和PlayerList索引位置可以起到Player的复合主键的作用
问候,
【问题讨论】:
【参考方案1】:以下解决方案显示了 Player 的复合键,它由 Team 和该团队中的玩家列表中的位置组成。保存从团队到玩家的级联。
Team.java
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Version;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.hibernate.annotations.IndexColumn;
@Entity
public class Team implements Serializable
@Id @GeneratedValue private Long id;
@Version private int version;
@OneToMany(cascade=CascadeType.ALL, mappedBy="id.team")
@IndexColumn(name="PLAYER_IDX")
private List<Player> players = new ArrayList<Player>();
private String name;
protected Team()
public Team(String name)
this.name = name;
public boolean addPlayer(Player player)
boolean result = players.add(player);
if (result)
player.setPlayerId(new PlayerId(this, players.size() - 1));
return result;
public List<Player> getPlayers()
return players;
@Override
public String toString()
return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).append("name", name).append("players", players).toString();
Player.java
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Version;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
@Entity
public class Player implements Serializable
@Id private PlayerId id;
@Version private int version;
void setPlayerId(PlayerId id)
this.id = id;
@Override
public String toString()
return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).append("number", id.getNumber()).toString();
PlayerId.java
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.ManyToOne;
import org.apache.commons.lang.builder.HashCodeBuilder;
@Embeddable
public class PlayerId implements Serializable
@ManyToOne
private Team team;
@Column(name="PLAYER_IDX", insertable=false, updatable=false)
private int number;
protected PlayerId()
PlayerId(Team team, int number)
this.team = team;
this.number = number;
public int getNumber()
return number;
@Override
public boolean equals(Object obj)
if (obj == null)
return false;
else if (obj == this)
return true;
if (obj instanceof PlayerId)
PlayerId other = (PlayerId) obj;
return other.team.equals(this.team) && other.number == this.number;
return false;
@Override
public int hashCode()
return new HashCodeBuilder().append(team).append(number).toHashCode();
下面的这个测试:
public void testPersistTeamAndPlayers() throws Exception
Team team = new Team("My Team");
team.addPlayer(new Player());
team.addPlayer(new Player());
AnnotationConfiguration configuration = new AnnotationConfiguration();
configuration.addAnnotatedClass(Team.class);
configuration.addAnnotatedClass(Player.class);
configuration.configure();
SessionFactory sessionFactory = configuration.buildSessionFactory();
Session session;
session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
session.save(team);
transaction.commit();
session.close();
session = sessionFactory.openSession();
@SuppressWarnings("unchecked") List<Team> list = session.createCriteria(Team.class).list();
assertEquals(1, list.size());
Team persisted = list.get(0);
System.out.println(persisted);
给出以下日志输出:
12:37:17,796 DEBUG [SchemaExport, SchemaExport.execute]
create table Player (
PLAYER_IDX integer not null,
version integer not null,
team_id bigint,
primary key (PLAYER_IDX, team_id)
)
12:37:17,796 DEBUG [SchemaExport, SchemaExport.execute]
create table Team (
id bigint generated by default as identity (start with 1),
name varchar(255),
version integer not null,
primary key (id)
)
12:37:17,796 DEBUG [SchemaExport, SchemaExport.execute]
alter table Player
add constraint FK8EA38701AA5DECBA
foreign key (team_id)
references Team
12:37:17,812 INFO [SchemaExport, SchemaExport.importScript] Executing import script: /import.sql
12:37:17,812 INFO [SchemaExport, SchemaExport.execute] schema export complete
12:37:17,859 DEBUG [SQL, SQLStatementLogger.logStatement]
insert
into
Team
(id, name, version)
values
(null, ?, ?)
12:37:17,875 DEBUG [SQL, SQLStatementLogger.logStatement]
call identity()
12:37:17,875 DEBUG [SQL, SQLStatementLogger.logStatement]
select
player_.PLAYER_IDX,
player_.team_id,
player_.version as version1_
from
Player player_
where
player_.PLAYER_IDX=?
and player_.team_id=?
12:37:17,890 DEBUG [SQL, SQLStatementLogger.logStatement]
select
player_.PLAYER_IDX,
player_.team_id,
player_.version as version1_
from
Player player_
where
player_.PLAYER_IDX=?
and player_.team_id=?
12:37:17,890 DEBUG [SQL, SQLStatementLogger.logStatement]
insert
into
Player
(version, PLAYER_IDX, team_id)
values
(?, ?, ?)
12:37:17,890 DEBUG [SQL, SQLStatementLogger.logStatement]
insert
into
Player
(version, PLAYER_IDX, team_id)
values
(?, ?, ?)
12:37:17,906 DEBUG [SQL, SQLStatementLogger.logStatement]
select
this_.id as id0_0_,
this_.name as name0_0_,
this_.version as version0_0_
from
Team this_
12:37:17,937 DEBUG [SQL, SQLStatementLogger.logStatement]
select
players0_.team_id as team3_1_,
players0_.PLAYER_IDX as PLAYER1_1_,
players0_.PLAYER_IDX as PLAYER1_1_0_,
players0_.team_id as team3_1_0_,
players0_.version as version1_0_
from
Player players0_
where
players0_.team_id=?
Team[name=My Team,players=[Player[number=0], Player[number=1]]]
最后一行显示toString
的Team
和Player
,它显示了数字的分配方式(列表的索引)。其他实体可以引用玩家(通过 team_id 和 player_idx)。
【讨论】:
恭喜,马丁。很好的答案。【参考方案2】:我觉得你犯了一些错误。
@Embedded 是一种表示由表中选定字段组成的对象/组件的方法。您可以使用它来表示复合键,但您还需要使用@EmbeddedId。
看看您想要实现的目标,我觉得您可以通过更简单的映射实现目标。 (为简洁省略了一些部分)
@Entity
public class Team
@OneToMany(mappedBy="team")
private List<Player> playerList = new ArrayList<Player>();
@Enity
public class Player
@ManyToOne
@JoinColumn(name="TEAM_ID")
private Team team;
如果 Player 是一个连接/链接表,您可以使用 @Embedded 静态类来表示复合键,有关这方面的更多信息,请参阅 Java Persistence with JPA 一书。
【讨论】:
以上是关于行为类似于 @Entity 和 @Embeddable 的类的主要内容,如果未能解决你的问题,请参考以下文章
如何制作点“。”和下划线 '_' 的行为类似于 MongoDB 中 $text 索引中的空格
如何创建一组行为类似于 RadioButtons 的 ToggleButtons?
让 Enter 键的行为类似于 QTableWidget 中的 Tab