行为类似于 @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]]]

最后一行显示toStringTeamPlayer,它显示了数字的分配方式(列表的索引)。其他实体可以引用玩家(通过 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

实现一个行为类似于 nginx 的 Undertow 反向代理

VBA:是不是可以创建一个行为类似于集合的类模块?