I'm using spring data JPA and have Player
, Score
entities. Player
has @OneToMany
relation to Score
(and @ManyToOne
from Score
to Player
). I save Player
to the database without specifying related scores. After that, I save Score
to the database, passing to the contructor a link to the Player
. Then in the same function, I read Player
entry, but it has no link to the Score
entry yet.
I have FetchType.LAZY
in my OneToMany
relation, so I need to use annotation @Transactional
in that function. I also tried to save player and score in the inner transaction(using propogation=Propogation.REQUIRES_NEW
) and read the player entry in an external transaction, but it doesn't help. I also tried to add CascadeType.ALL
- no effect. And i used flush()
method from JpaRepository
- doesn't help too.
But if I will save player and score in one controller method(using spring MVC), and read in another, the player will have a link to the score.
Edited: add code, Score in code is GamePlayerScore. All classes have lombok annotations @Getter
and @ToString
.
Player
@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);
}
GameStatistic
@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();
}
And how I store entries
@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);
}
And how I read entry
@Transactional
public void insertAndRead() {
preprocess();
Player player = playerRepository.findByNickname("john");
System.out.println(player.getScores());
}
Saving the score should update the relationship to the player in the database, once those changes are flushed.
But reloading a Player
with a session that already contains that Player
will bring up the old Player
instance without updating it with data from the database.
So in order to see the changed Player
you need to flush the session and remove the Player
from the session and then reload it. Alternatively, you can just close the session.
You seem to try to achieve this by separate transactional methods. This should work in principle, but I'm almost 100% sure, that you have a transaction running across both the save and the load method. You can enable logging for transactions in order to investigate further: Spring hibernate Transaction Logging
What you really want to do though is probably to keep the two java objects in sync by writing custom setters.