简体   繁体   中英

Spring boot problem with OneToMany Relationship

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]

Here is your mistake

@OneToMany(fetch = FetchType.EAGER)
@JoinColumn(name = "note_id", referencedColumnName = "user_id")  <------
private Set<Note> notes = new HashSet<>();

You ask Hibernate to map those 2 entities together by 2 different columns on each side. You can't combine a User and a Note together by checking only user_id from one side and note_id from the other side.

The Uniderectional mapping can however be created automatically by hiberante if you define it as

@OneToMany(fetch = FetchType.EAGER)
@JoinColumn(name = "note_id") 
private Set<Note> notes = new HashSet<>();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM