Reputation: 65
Im trying to save Team then to save all of his members but im facing this error . I searched a lot for this common problem but i don't realise where the problem is.
Error:
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.hackathon.web.domain.Member#com.hackathon.web.domain.MemberId@dc6]
at org.hibernate.persister.entity.AbstractEntityPersister.check(AbstractEntityPersister.java:2648) ~[hibernate-core-5.4.30.Final.jar:5.4.30.Final]
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3491) ~[hibernate-core-5.4.30.Final.jar:5.4.30.Final]
at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:3354) ~[hibernate-core-5.4.30.Final.jar:5.4.30.Final]
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3768) ~[hibernate-core-5.4.30.Final.jar:5.4.30.Final]
at org.hibernate.action.internal.EntityUpdateAction.execute(EntityUpdateAction.java:201) ~[hibernate-core-5.4.30.Final.jar:5.4.30.Final]
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:604) ~[hibernate-core-5.4.30.Final.jar:5.4.30.Final]
at org.hibernate.engine.spi.ActionQueue.lambda$executeActions$1(ActionQueue.java:478) ~[hibernate-core-5.4.30.Final.jar:5.4.30.Final]
Team class:
public class Team {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="teamid")
private Long teamID;
private String name;
@ToString.Exclude
@OneToMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER,orphanRemoval = true)
@JoinColumn(name="teamid")
private List<Member> members;
@OneToMany(mappedBy = "team",cascade = CascadeType.ALL,fetch = FetchType.EAGER)
@ToString.Exclude
private Set<Mark> marks;
@ManyToOne
@JoinColumn(name = "mentorid",insertable = true, updatable = true)
@ToString.Exclude
private Mentor mentor;
@ManyToOne
@JoinColumn(name = "administratorid",insertable = true, updatable = true)
@ToString.Exclude
private Administrator administrator;
@ManyToOne
@JoinColumn(name = "hackathonid",insertable = true, updatable = true)
@ToString.Exclude
private Hackathon hackathon;}
Member class:
public class Member {
@EmbeddedId
private MemberId id;
private String name;
private String mail;
private String lastName;
private String role;
@ManyToOne(fetch = FetchType.LAZY,cascade = ALL)
@JoinColumn(name="teamid",insertable = false, updatable = false )
private Team team;}
MemberId class:
public class MemberId implements Serializable {
@GeneratedValue(strategy = GenerationType.AUTO)
private Long memberID;
@Column(insertable=true, updatable=true)
private Long teamID;}
Controller:
@PostMapping("/administrator/addTeam")
public String saveTeam(Model model,
@NotNull @Valid
@ModelAttribute Team team,
BindingResult bindingResult,
HttpServletRequest request){
if(team.getAdministrator() == null){
Administrator administrator = (Administrator) request.getSession().getAttribute("user");
team.setAdministrator(administrator);
}
Team teamForSave = new Team(0L,team.getName(),new ArrayList<>(),new HashSet<>(), team.getMentor(), team.getAdministrator(), team.getHackathon());
Team savedTeam = teamService.save(teamForSave);
if(team.getMembers() != null ) {
for (Member m :
team.getMembers()) {
Member member = new Member(new MemberId(0L, savedTeam.getTeamID())
,m.getName()
,m.getMail()
,m.getLastName()
,m.getRole()
,savedTeam);
Member savedMember = memberService.save(member);
}
}
model.addAttribute("team",savedTeam);
return "team";
}
Hibernate logg:
Hibernate: insert into team (administratorid, hackathonid, mentorid, name) values (?, ?, ?, ?)
Hibernate: select member0_.memberid as memberid1_5_2_, member0_.teamid as teamid2_5_2_, member0_.last_name as last_nam3_5_2_, member0_.mail as mail4_5_2_, member0_.name as name5_5_2_, member0_.role as role6_5_2_, team1_.teamid as teamid1_7_0_, team1_.administratorid as administ3_7_0_, team1_.hackathonid as hackatho4_7_0_, team1_.mentorid as mentorid5_7_0_, team1_.name as name2_7_0_, marks2_.teamid as teamid2_4_4_, marks2_.markid as markid1_4_4_, marks2_.markid as markid1_4_1_, marks2_.teamid as teamid2_4_1_, marks2_.comment as comment3_4_1_, marks2_.complexity as complexi4_4_1_, marks2_.design as design5_4_1_, marks2_.efficiency as efficien6_4_1_, marks2_.judgeid as judgeid7_4_1_ from member member0_ left outer join team team1_ on member0_.teamid=team1_.teamid left outer join mark marks2_ on team1_.teamid=marks2_.teamid where member0_.memberid=? and member0_.teamid=?
Hibernate: insert into member (last_name, mail, name, role, memberid, teamid) values (?, ?, ?, ?, ?, ?)
Hibernate: update member set last_name=?, mail=?, name=?, role=? where memberid=? and teamid=?
So, team is saved correctly ,first member also , and then this error occurs. Use case is: Im adding new team on form ,including adding members into table,setting the randomNegativeId for each member for tracking delete from table , setting team id -1 for showing correct form in view. But here in controller im creating new Team and copy values , so i dont know why it doesnt work. I tried to save all at once with cascading but then error is that teamid column is null on members .I really need help with this. Thanks in advance
Upvotes: 0
Views: 1808
Reputation: 1
If you use cascade=cascadeType.All, you shouldn't save team first then save members separately afterward. This causes confusion to the transaction. What you should do is set members to teamForSave first then save it:
List<Member> membersForSave = new ArrayList<>();
if (team.getMembers != null) {
for (Member m : team.getMembers()) {
Member member = new Member(new MemberId(0L, savedTeam.getTeamID())
,m.getName()
,m.getMail()
,m.getLastName()
,m.getRole()
,savedTeam);
membersForSave.add(member);
}
}
Team teamForSave = new Team(0L, team.getName(), membersForSave, new HashSet<>(), team.getMentor(), team.getAdministrator(), team.getHackathon());
You should do the same with marks, too. In case you want to save child entities manually like you're doing in your controller, remove cascadeType.All in your entity (Team)
Upvotes: 0
Reputation: 1527
First off, don't let the controller handle repository interactions, it's a bad practice usually. Instead use a service class. However with that out of the way the class would be something like this
@Service
public class TeamService {
private final TeamRepository teamRepo;
public TeamService(TeamRepository teamRepo) {
this.teamRepo = teamRepo;
}
public Team saveTeam(Team team) {
team.getMembers().forEach(member -> {
member.setMemberId(new MemberId(null, team.getTeamID()));
});
return teamRepo.save(team);
}
}
That's about it. The members will be saved because of the cascade set to ALL which include also PERSIST. It's important to not set ids manually unless it's not an automatic id assigning strategy.
Your controller would become
@PostMapping("/administrator/addTeam")
public String saveTeam(Model model,
@NotNull @Valid
@ModelAttribute Team team,
BindingResult bindingResult,
HttpServletRequest request){
if(team.getAdministrator() == null){
Administrator administrator = (Administrator) request.getSession().getAttribute("user");
team.setAdministrator(administrator);
}
model.addAttribute("team", teamService.saveTeam(team));
return "team";
}
Of course you'd need to inject the TeamService as you where doing for the team repo.
PS: I don't really get if the teamService is actually a Service or a Repositoy. If it's already a service then just replace the code within the .save() method
Upvotes: -1