级联持久化:一个父级和多个子级(每个子级都有自己的嵌入ID)



我有一个名为Player的父实体和两个名为bat和ball的子实体。

玩家实体的PK为"playerId",球的复合pk是"playerId(FK("one_answers"ballColor",&蝙蝠的复合pk是"playerId(FK("one_answers"batColor">

注:球员对击球是OneToOne,球员对球是OneToMany(单向(

@Entity
@Table(name = "player")
public class Player implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name = "player_id")
private Long playerId;

@JsonManagedReference
@OneToMany(mappedBy = "player", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@Fetch(value=FetchMode.SELECT)
private List<Ball> balls;
@OneToOne(mappedBy = "player", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JsonManagedReference
private Bat bat;
// getters and setters
}
@Entity
@Table(name = "bat")
public class Bat implements Serializable {
private static final long serialVersionUID = -114046508592L;
@EmbeddedId
private BatEmbeddedId batEmbeddedId = new BatEmbeddedId();
@MapsId("playerId")
@OneToOne
@JoinColumn(name = "bat_id", insertable = false, updatable = false)
@JsonBackReference
private Player player;
// getters and setters for player field
}
@Entity
@Table(name = "ball")
public class Ball implements Serializable {
private static final long serialVersionUID = -1108592L;
@EmbeddedId
private BallEmbeddedId ballEmbeddedId = new BallEmbeddedId();
@MapsId("playerId")
@ManyToOne
@JoinColumn(name = "ball_id", insertable = false, updatable = false)
@JsonBackReference
private Player player;
// getters and setters for player field
}
@Embeddable
public class BatEmbeddedId implements Serializable{
private static final long serialVersionUID = -91976886L;
@Column(name = "player_id")
private Long playerId;

@Column(name = "bat_color")
private String batColor;

public BatEmbeddedId() {}

public BatEmbeddedId(Long playerId, String batColor) {
super();
this.playerId= playerId;
this.batColor= batColor;
}
// getters and setters for all fields
}

@Embeddable
public class BallEmbeddedId implements Serializable{
private static final long serialVersionUID = -91976889990L;
@Column(name = "player_id")
private Long playerId;

@Column(name = "ball_color")
private String ballColor;

public BatEmbeddedId() {}

public BatEmbeddedId(Long playerId, String ballColor) {
super();
this.playerId= playerId;
this.ballColor= ballColor;
}
// getters and setters for all fields
}

public interface PlayerRepository extends JpaRepository<Player, Long>, JpaSpecificationExecutor<Player> {
Player findByPlayerId(Long playerId);
}
@Service
public class myService{
@Autowired
PlayerRepository playerRepository;
// Save logic for player
Player player = new Player();

BatEmbeddedId batEmId = new BatEmbeddedId();
//note i can't set the playerId here coz its not yet generated
batEmId.setBatColor("white");
Bat bat = new Bat();
bat.setBatEmbeddedId(batEmId);
bat.setPlayer(player);
player.setBat(bat); // added bat to player object
List<Ball> balls = new ArrayList<>();
BallEmbeddedId ballEmId1 = new BallEmbeddedId();
BallEmbeddedId ballEmId2 = new BallEmbeddedId();
ballEmId1.setBallColor("white");
ballEmId2.setBallColor("red");
Ball b1 = new Ball();
Ball b2 = new Ball();
b1.setBallEmbeddedId(ballEmId1);
b2.setBallEmbeddedId(ballEmId2);
balls.add(b1);
balls.add(b2);
//adding balls to player object
player.setBalls(balls);
//finally calling a save
playerRepository.save(player); 
}

运行以上代码后,我得到以下错误:org.springframework.dao.DataIntegrityViolationException: A different object with the same identifier value was already associated with the session

我是否在这些相关实体的保存逻辑中做错了什么?

非常感谢!!!

尝试如下更正映射:

@Entity
@Table(name = "bat")
public class Bat implements Serializable {
// ...
@EmbeddedId
private BatEmbeddedId batEmbeddedId = new BatEmbeddedId();
@MapsId("playerId")
@OneToOne
@JoinColumn(name = "player_id")
@JsonBackReference
private Player player;
// getters and setters for player field
}
@Embeddable
public class BatEmbeddedId implements Serializable{
// ...
// @Column annotation will be ignored as you use @MapsId("playerId") 
// The column name will be taken from the @JoinColumn(name = "player_id") annotation
// @Column(name = "player_id")
private Long playerId;

@Column(name = "bat_color")
private String batColor;

public BatEmbeddedId() {}

public BatEmbeddedId(Long playerId, String batColor) {
// super() is redundant 
// super();
this.playerId= playerId;
this.batColor= batColor;
}
// getters and setters for all fields

// The primary key class must define equals and hashCode methods,
// consistent with equality for the underlying database types to which the primary key is mapped.
// see https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#identifiers-composite

@Override
public boolean equals(Object o) {
if ( this == o ) return true;
if ( o == null || getClass() != o.getClass() ) return false;
BatEmbeddedId pk = (BatEmbeddedId) o;
return Objects.equals( playerId, pk.playerId ) &&
Objects.equals( batColor, pk.batColor );
}
@Override
public int hashCode() {
return Objects.hash( playerId, batColor );
}
}

@Entity
@Table(name = "ball")
public class Ball implements Serializable {
// ...
@EmbeddedId
private BallEmbeddedId ballEmbeddedId = new BallEmbeddedId();
@MapsId("playerId")
@ManyToOne
@JoinColumn(name = "player_id")
@JsonBackReference
private Player player;
// getters and setters for player field
}

@Embeddable
public class BallEmbeddedId implements Serializable{
// ...
// @Column(name = "player_id")
private Long playerId;

@Column(name = "ball_color")
private String ballColor;

public BatEmbeddedId() {}

public BatEmbeddedId(Long playerId, String ballColor) {
this.playerId= playerId;
this.ballColor= ballColor;
}
// getters and setters for all fields
// equals and hashCode 
}

您是否已经尝试从玩家协会中删除insertable = false, updatable = false@MapsId应该能够在没有这种情况下进行处理。此外,您应该将可嵌入类型中的列标记为nullable = false

如果问题仍然存在,你确定你没有在同一事务中做任何其他事情吗?

您使用的是最新的Hibernate 5.4.29版本吗?如果是,并且您仍然有问题,请在问题跟踪器中创建一个问题(https://hibernate.atlassian.net)带有测试用例(https://github.com/hibernate/hibernate-test-case-templates/blob/master/orm/hibernate-orm-5/src/test/java/org/hibernate/bugs/JPAUnitTestCase.java)它再现了这个问题。

最新更新