【问题标题】:A class that behaves like @Entity and @Embeddable行为类似于 @Entity 和 @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的复合主键的作用

问候,

【问题讨论】:

    标签: java hibernate jpa


    【解决方案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 一书。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-12
      • 2012-09-25
      • 2014-02-04
      • 2020-06-05
      • 2016-01-27
      • 1970-01-01
      相关资源
      最近更新 更多