So I want the users to have many notes, and this means that the relation type between users and notes should be OneToMany(meaning one user has many notes). So i have a very strange bug in my application. When create and add the note to the database, and then i also save it in the users it works fine for the first time, however at second try i get the error "Cannot add or update a child row: a foreign key constraint fails". When i add one note to the database it works but when i add another note it gives the same error. I have fixed the bug with the set foreign_key_checks=0 in the database and it works, but it does not work when from my application.
Here are my codes for different classes:
Notes:
@Entity
@Table(name = "notes")
@Getter
@Setter
@ToString
public class Note {
@Id
@Column(name = "note_id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String description;
@Temporal(TemporalType.TIMESTAMP)
private Date createdDate = new Date(System.currentTimeMillis());
}
Users:
@Entity
@Table(name = "users")
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class User {
@Id
@Column(name = "user_id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true)
private String email;
private String nickname;
private Integer age;
@Column(nullable = false)
private String password;
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(
name = "users_roles",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "role_id")
)
private Set<Role> roles = new HashSet<>();
@OneToMany(fetch = FetchType.EAGER)
@JoinColumn(name = "note_id", referencedColumnName = "user_id")
private Set<Note> notes = new HashSet<>();
public void addRole(Role role){
this.roles.add(role);
}
public void addNote(Note note){this.notes.add(note);}
public Set<Note> getNotes(){
return this.notes;
}
}
NoteService:
@Service
public class NoteService {
@Autowired
private NoteRepository noteRepository;
@Autowired
private UserService userService;
public List<Note> getAllNote(){
return noteRepository.findAll();
}
public Note getNote(Long id) throws noteNotFoundException {
Optional<Note> result = noteRepository.findById(id);
if(result.isPresent()){
return result.get();
}
//chveni sheqmnili exeptioni
throw new noteNotFoundException("Could not find any note with given ID: " id);
}
public void save(Note note) {
noteRepository.save(note);
}
public void deleteNoteById(Long id) throws noteNotFoundException {
if(getNote(id)==null){
throw new noteNotFoundException("Could not find any note with given ID: " id);
}
noteRepository.deleteById(id);
}
}
UserService:
@Service
public class UserService {
private final Integer USER_ROLE_ID = 1;
private final Long ADMIN_ID = 3L;
@Autowired
private UserRepository repository;
@Autowired
private RoleService roleService;
public List<User> getAllUser(){
return (List<User>) repository.findAll();
}
public List<User> getAllUsersWithoutAdmin(){
List<User> allUsers = repository.findAll();
User admin = repository.getById(ADMIN_ID);
allUsers.remove(admin);
return allUsers;
};
public void save(User u) {
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
String encodedPassword = encoder.encode(u.getPassword());
u.setPassword(encodedPassword);
try {
u.addRole(roleService.getRole(USER_ROLE_ID));
} catch (userNotFoundException e) {
e.printStackTrace();
}
repository.save(u);
}
public void saveNoteToUser(Note note){
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
User user = repository.findByEmail(authentication.getName());
user.addNote(note);
repository.save(user);
}
public Set<Note> getAllNotes(){
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
User user = repository.findByEmail(authentication.getName());
return user.getNotes();
}
public User getUser(Long id) throws userNotFoundException {
Optional<User> result = repository.findById(id);
if(result.isPresent()){
return result.get();
}
//chveni sheqmnili exeptioni
throw new userNotFoundException("Could not find any user with given ID: " id);
}
public void deleteUserById(Long id) throws userNotFoundException {
//eseigi jer itvli tu 0 ia an null errors abruen
//tu useri arsebobs mere adeleteb
Long count = repository.countById(id);
if(count == null || count==0){
throw new userNotFoundException("Could not find any user with given ID: " id);
}
repository.deleteById(id);
}
}
And Finally Mapping:
@PostMapping("/notes/save")
public String saveNote(Note note, RedirectAttributes ra){
noteService.save(note);
userService.saveNoteToUser(note);
//ra atributi ari roca redirect moxdeba mere ro dawers messijs anu redirectis mere xdeba
ra.addFlashAttribute("message", "The note has been added successfully.");
return "redirect:/notes";
}
In mapping as you can see, firstly i am trying to save a note in the database, and after that i want to add that note to the user itself. However as mentioned above it works only once, however when i want to add another note i get this error:
java.sql.SQLIntegrityConstraintViolationException: Cannot add or update a child row: a foreign key constraint fails (`myfirstappdb`.`notes`, CONSTRAINT `FKb7tumg0c2p1wt2ifjag2gv998` FOREIGN KEY (`note_id`) REFERENCES `users` (`user_id`))
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:117) ~[mysql-connector-java-8.0.27.jar:8.0.27]
at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122) ~[mysql-connector-java-8.0.27.jar:8.0.27]
at com.mysql.cj.jdbc.ClientPreparedStatement.executeInternal(ClientPreparedStatement.java:953) ~[mysql-connector-java-8.0.27.jar:8.0.27]
at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdateInternal(ClientPreparedStatement.java:1098) ~[mysql-connector-java-8.0.27.jar:8.0.27]
at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdateInternal(ClientPreparedStatement.java:1046) ~[mysql-connector-java-8.0.27.jar:8.0.27]
at com.mysql.cj.jdbc.ClientPreparedStatement.executeLargeUpdate(ClientPreparedStatement.java:1371) ~[mysql-connector-java-8.0.27.jar:8.0.27]
at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdate(ClientPreparedStatement.java:1031) ~[mysql-connector-java-8.0.27.jar:8.0.27]
at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61) ~[HikariCP-4.0.3.jar:na]
at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.executeUpdate(HikariProxyPreparedStatement.java) ~[HikariCP-4.0.3.jar:na]
CodePudding user response:
According to the Hibernate Documentation when you have a UniDirectional relation in your entity schema and you have only the side of @OneToMany
, you can't use the annotation @JoinColumn
.
When using a unidirectional @OneToMany association, Hibernate resorts to using a link table between the two joining entities.
You must remove the @JoinColumn
so that hibernate follows the default process of creating a intermediate join table and then it will be able to proceed.