jeudi 4 juin 2020

Hibernate Cascade - Hiberate saves parent before child

I want to save a new UserRegistration instance in my database using Spring Data JPA. In the database I have the tables "users" and "authorities". Every time I try to save the instance I get the error "Cannot add or update a child row: a foreign key constraint fails". This is because the table "authorities" holds a foreign key referencing "users". The error is caused because Hibernate first tries to store the authority instance, before inserting the UserRegistration instance:

Hibernate: 
    insert 
    into
        users
        (email, enabled, password, profilePicture, username) 
    values
        (?, ?, ?, ?, ?)
Hibernate: 
    insert 
    into
        authorities
        (authority, username, userid) 
    values
        (?, ?, ?)

UserRegistration.java:

package main.java.de.ostfalia.seprojekt.database.models;

import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.OneToOne;
import javax.persistence.Table;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonManagedReference;

import main.java.de.ostfalia.seprojekt.database.dto.UserRegistrationDTO;

@Entity
@Table(name = "users")
public class UserRegistration {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    private String username;
    @JsonIgnore
    private String password;
    @JsonIgnore
    private boolean enabled;
    private Integer profilePicture;
    private String email;
    @OneToOne(fetch = FetchType.LAZY, optional = false, mappedBy = "user", cascade = CascadeType.ALL)
    private Authority authority;
    @JsonManagedReference
    @ManyToMany
    @JoinTable(name = "userhasfavorite", joinColumns = @JoinColumn(name = "userid"), inverseJoinColumns = @JoinColumn(name = "channelid"))
    private List<Channel> favorites;

    public UserRegistration() {
    }

    public UserRegistration(UserRegistrationDTO dto) {
        this.email = dto.getEmail();
        this.username = dto.getUsername();
        this.password = dto.getPassword();
        this.enabled = dto.isEnabled();
        this.profilePicture = dto.getProfilePicture();
        this.authority = new Authority(id, username, dto.getRole(), this);
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public boolean getEnabled() {
        return enabled;
    }

    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }

    public Integer getProfilePicture() {
        return profilePicture;
    }

    public void setProfilePicture(Integer profilePicture) {
        this.profilePicture = profilePicture;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Authority getAuthority() {
        return authority;
    }

    public void setAuthority(Authority authority) {
        this.authority = authority;
    }

    public List<Channel> getFavorites() {
        return favorites;
    }

    public void setFavorites(List<Channel> favorites) {
        this.favorites = favorites;
    }
}

Authority.java:

package main.java.de.ostfalia.seprojekt.database.models;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import javax.persistence.Table;

@Entity
@Table(name = "authorities")
public class Authority {

    @Id
    @Column(name = "userid")
    private int userID;
    private String username;
    private String authority;
    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "userid")
    private UserRegistration user;

    public Authority() {
    }

    public Authority(int userID, String username, String authority, UserRegistration user) {
        this.userID = userID;
        this.username = username;
        this.authority = authority;
        this.user = user;
    }

    public int getUserID() {
        return userID;
    }

    public void setUserID(int userID) {
        this.userID = userID;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getAuthority() {
        return authority;
    }

    public void setAuthority(String authority) {
        this.authority = authority;
    }
}

Code that saves the entity:

@PostMapping
public boolean saveUser(@RequestBody UserRegistrationDTO userToSave) {
    if (roleExists(userToSave.getRole())) {
        UserRegistration user = new UserRegistration(userToSave);
        uRepo.saveAndFlush(user);
        return true;
    }
    return false;
}

My question now is: How can I save the UserRegistration instance, before inserting the authority instance using CascadeTypes?

Thank you for reading this question. I'm looking forward to get this problem fixed.




Aucun commentaire:

Enregistrer un commentaire