【问题标题】:When hibernate will update related entities?hibernate什么时候会更新相关实体?
【发布时间】:2018-03-19 22:16:19
【问题描述】:

我正在使用 Spring Data JPA 并拥有 PlayerScore 实体。 PlayerScore 具有 @OneToMany 关系(以及从 ScorePlayer@ManyToOne)。我将Player 保存到数据库中而不指定相关分数。之后,我将Score 保存到数据库中,将指向Player 的链接传递给构造函数。然后在同一个函数中,我读取了Player 条目,但它还没有指向Score 条目的链接。
我的OneToMany 关系中有FetchType.LAZY,所以我需要在该函数中使用注释@Transactional。我还尝试在内部事务中保存玩家和分数(使用propogation=Propogation.REQUIRES_NEW)并在外部事务中读取玩家条目,但这没有帮助。我还尝试添加CascadeType.ALL - 没有效果。我使用了来自JpaRepositoryflush() 方法 - 也没有帮助。
但是,如果我将播放器和得分保存在一个控制器方法中(使用 spring MVC),然后读入另一个,播放器将有一个指向得分的链接。
编辑:添加代码,得分代码是 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());
}

【问题讨论】:

  • 请发布PlayerScore 类的代码以及您尝试持久化它们的方式。将其作为散文阅读时,真的很难理解发生了什么。此外,所需的确切注释等重要细节也很容易丢失。
  • 好的,我添加了代码
  • 解决的关键是重新读入数据。该链接仅在您加载 player 时由 JPA 填充,而不是神奇地...
  • 什么意思?我应该在同一个函数中再次从存储库中读取?好像也没有用

标签: java database hibernate spring-data-jpa hibernate-onetomany


【解决方案1】:

一旦这些更改被刷新,保存分数应该会更新与数据库中玩家的关系。

但是使用已经包含 Player 的会话重新加载 Player 将启动旧的 Player 实例,而不会使用数据库中的数据对其进行更新。

因此,为了查看更改后的Player,您需要刷新会话并从会话中删除Player,然后重新加载它。或者,您可以关闭会话。

您似乎试图通过单独的事务方法来实现这一点。 这原则上应该有效,但我几乎 100% 确定,您有一个事务在 save 和 load 方法上运行。 您可以启用事务日志记录以便进一步调查:Spring hibernate Transaction Logging

您真正想要做的可能是通过编写自定义设置器来保持两个 java 对象同步。

【讨论】:

  • 为什么跨两个操作运行的事务不允许刷新更改?我想,如果交易结束,所有的变化都会被记录下来。我还尝试编写事务写入方法、事务读取方法,并在测试(非事务)方法中调用这两种方法。但我有例外,说“......不能懒惰地初始化一个集合......”。但我正在用事务方法阅读这个集合......
  • 更改会被刷新,但只要您处于事务/会话中,对象就不会被重新加载。您的事务设置似乎有问题,因为它应该在使用两个单独的事务时工作。我建议激活日志记录以确认您确实使用了一个事务并创建一个关于如何解决该问题的新问题。
  • 由于某些我不知道的原因,如果我从非事务方法调用一些事务方法(来自同一类),实际上 spring 根本不会创建事务
  • 好的,现在我发现由于Spring AOP的限制我不能这样做
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-29
  • 2019-04-22
  • 1970-01-01
相关资源
最近更新 更多