hibernate什么时候会更新相关实体?
When hibernate will update related entities?
我正在使用 spring 数据 JPA 并且有 Player
、Score
个实体。 Player
与 Score
有 @OneToMany
关系(并且 @ManyToOne
从 Score
到 Player
)。我将 Player
保存到数据库,但没有指定相关分数。之后,我将 Score
保存到数据库,将 link 传递给构造函数 Player
。然后在同一个函数中,我读取了 Player
条目,但是它还没有 link 到 Score
条目。
我的 OneToMany
关系中有 FetchType.LAZY
,所以我需要在该函数中使用注释 @Transactional
。我还尝试在内部事务中保存玩家和得分(使用 propogation=Propogation.REQUIRES_NEW
)并在外部事务中读取玩家条目,但这没有帮助。我也尝试添加 CascadeType.ALL
- 没有效果。我使用了 JpaRepository
中的 flush()
方法 - 也没有帮助。
但是,如果我将在一个控制器方法(使用 spring MVC)中保存播放器和分数,并在另一个控制器方法中读取,则播放器将有一个 link 的分数。
已编辑:添加代码,代码中的得分为GamePlayerScore。所有 类 都有 lombok 注释 @Getter
和 @ToString
.
玩家
@Temporal(TemporalType.DATE)
@NonNull
@Column(updatable = false)
private Date createdAt;
@Setter
@NonNull
@Column(unique = true)
private String nickname;
@OneToMany(mappedBy = "winner", cascade = CascadeType.REFRESH)
private Set<GameStatistic> gamesWon;
@OneToOne(cascade = {CascadeType.PERSIST, CascadeType.REFRESH}, optional = false)
@JoinColumn
private PlayerStatistic stat;
@OneToMany(mappedBy = "player", cascade = CascadeType.REFRESH)
private Set<GamePlayerScore> scores;
@ManyToOne
@JoinColumn
private Game currentGame;
public Player(String nickname, Date createdAt) {
this.nickname = nickname;
this.createdAt = createdAt;
stat = new PlayerStatistic(0, 0, 0, this);
}
GamePlayerScore
@ManyToOne(optional = false)
@JoinColumn
@NonNull
@JsonIgnore
private GameStatistic game;
@ManyToOne(optional = false)
@JoinColumn
@NonNull
@JsonIgnore
private Player player;
@NonNull
private Integer score;
@PrePersist
private void updatePlayerStat() {
player.getStat().incrementTotalGames();
player.getStat().addTotalScore(score);
}
游戏统计
@Column(updatable = false)
@NonNull
private Integer durationMinutes;
@Temporal(TemporalType.TIMESTAMP)
@NonNull
private Date startedAt;
@ManyToOne(optional = false)
@JoinColumn
@NonNull
@JsonIgnore
private Player winner;
@OneToMany(mappedBy = "game")
private Set<GamePlayerScore> scores;
@PrePersist
private void update() {
winner.getStat().incrementTotalWins();
}
以及我如何存储条目
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void prepossess() {
Date date = new Date();
Player john = new Player("john", date);
GameStatistic game = new GameStatistic(35, new Date(), john);
GamePlayerScore johnScore = new GamePlayerScore(game, john, 100);
playerRepository.save(player);
gameRepository.save(game);
scoreRepository.save(johnScore);
}
以及我如何阅读条目
@Transactional
public void insertAndRead() {
preprocess();
Player player = playerRepository.findByNickname("john");
System.out.println(player.getScores());
}
一旦这些更改被刷新,保存分数应该会更新与数据库中玩家的关系。
但是使用已经包含 Player
的会话重新加载 Player
将启动旧的 Player
实例,而不使用数据库中的数据更新它。
因此,为了查看更改后的 Player
,您需要刷新会话并从会话中删除 Player
,然后重新加载它。或者,您可以关闭会话。
您似乎试图通过单独的交易方法来实现这一点。
这在原则上应该有效,但我几乎 100% 肯定,您在保存和加载方法中都有一个事务 运行。
您可以启用事务日志记录以便进一步调查:Spring hibernate Transaction Logging
你真正想做的可能是通过编写自定义 setter 使两个 java 对象保持同步。
我正在使用 spring 数据 JPA 并且有 Player
、Score
个实体。 Player
与 Score
有 @OneToMany
关系(并且 @ManyToOne
从 Score
到 Player
)。我将 Player
保存到数据库,但没有指定相关分数。之后,我将 Score
保存到数据库,将 link 传递给构造函数 Player
。然后在同一个函数中,我读取了 Player
条目,但是它还没有 link 到 Score
条目。
我的 OneToMany
关系中有 FetchType.LAZY
,所以我需要在该函数中使用注释 @Transactional
。我还尝试在内部事务中保存玩家和得分(使用 propogation=Propogation.REQUIRES_NEW
)并在外部事务中读取玩家条目,但这没有帮助。我也尝试添加 CascadeType.ALL
- 没有效果。我使用了 JpaRepository
中的 flush()
方法 - 也没有帮助。
但是,如果我将在一个控制器方法(使用 spring MVC)中保存播放器和分数,并在另一个控制器方法中读取,则播放器将有一个 link 的分数。
已编辑:添加代码,代码中的得分为GamePlayerScore。所有 类 都有 lombok 注释 @Getter
和 @ToString
.
玩家
@Temporal(TemporalType.DATE)
@NonNull
@Column(updatable = false)
private Date createdAt;
@Setter
@NonNull
@Column(unique = true)
private String nickname;
@OneToMany(mappedBy = "winner", cascade = CascadeType.REFRESH)
private Set<GameStatistic> gamesWon;
@OneToOne(cascade = {CascadeType.PERSIST, CascadeType.REFRESH}, optional = false)
@JoinColumn
private PlayerStatistic stat;
@OneToMany(mappedBy = "player", cascade = CascadeType.REFRESH)
private Set<GamePlayerScore> scores;
@ManyToOne
@JoinColumn
private Game currentGame;
public Player(String nickname, Date createdAt) {
this.nickname = nickname;
this.createdAt = createdAt;
stat = new PlayerStatistic(0, 0, 0, this);
}
GamePlayerScore
@ManyToOne(optional = false)
@JoinColumn
@NonNull
@JsonIgnore
private GameStatistic game;
@ManyToOne(optional = false)
@JoinColumn
@NonNull
@JsonIgnore
private Player player;
@NonNull
private Integer score;
@PrePersist
private void updatePlayerStat() {
player.getStat().incrementTotalGames();
player.getStat().addTotalScore(score);
}
游戏统计
@Column(updatable = false)
@NonNull
private Integer durationMinutes;
@Temporal(TemporalType.TIMESTAMP)
@NonNull
private Date startedAt;
@ManyToOne(optional = false)
@JoinColumn
@NonNull
@JsonIgnore
private Player winner;
@OneToMany(mappedBy = "game")
private Set<GamePlayerScore> scores;
@PrePersist
private void update() {
winner.getStat().incrementTotalWins();
}
以及我如何存储条目
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void prepossess() {
Date date = new Date();
Player john = new Player("john", date);
GameStatistic game = new GameStatistic(35, new Date(), john);
GamePlayerScore johnScore = new GamePlayerScore(game, john, 100);
playerRepository.save(player);
gameRepository.save(game);
scoreRepository.save(johnScore);
}
以及我如何阅读条目
@Transactional
public void insertAndRead() {
preprocess();
Player player = playerRepository.findByNickname("john");
System.out.println(player.getScores());
}
一旦这些更改被刷新,保存分数应该会更新与数据库中玩家的关系。
但是使用已经包含 Player
的会话重新加载 Player
将启动旧的 Player
实例,而不使用数据库中的数据更新它。
因此,为了查看更改后的 Player
,您需要刷新会话并从会话中删除 Player
,然后重新加载它。或者,您可以关闭会话。
您似乎试图通过单独的交易方法来实现这一点。 这在原则上应该有效,但我几乎 100% 肯定,您在保存和加载方法中都有一个事务 运行。 您可以启用事务日志记录以便进一步调查:Spring hibernate Transaction Logging
你真正想做的可能是通过编写自定义 setter 使两个 java 对象保持同步。