11
respostas

Excluir registros de uma entidade com relacionametos

Olá, tenho a seguinte dúvida, possuo entidades e entre elas alguns relacionamentos, a tarefa que preciso executar é excluir um registro da tabela (post), porém por conta dos relacionamentos eu recebo o erro:

ERROR: update or delete on table "post" violates foreign key constraint "fke2l07hc93u2bbjnl80meu3rn4" on table "image"
  Detalhe: Key (id)=(2) is still referenced from table "image".

Eu entendo que como tenho relacionamentos, não posso excluir um post que está com uma image vinculado a ele, gostaria de entender como fazer para que esse registro seja deletado.

É preciso deletar todos os registros referentes a eles das tabelas relacionadas antes de prosseguir ?

Vou compartilhar abaixo as classes para melhor entendimento da situação

11 respostas

Modelo Post

@Entity
@JsonIgnoreProperties({ "hibernateLazyInitializer", "handler" })
public class Post {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String text;
    private LocalDateTime date = LocalDateTime.now();

    @ManyToOne
    private StatusPost status;

    @ManyToOne(fetch = FetchType.LAZY)
    @JsonIgnore
    private User user;

    @OneToMany(mappedBy = "post")
    private List<Image> images = new ArrayList<>();

    @OneToMany(mappedBy = "post")
    private List<Comment> comments = new ArrayList<>();

    public Post() {
    }

    public Post(String text, StatusPost status) {
        this.text = text;
        this.status = status;
    }

    public Long getId() {
        return id;
    }

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

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }

    public LocalDateTime getDate() {
        return date;
    }

    public void setDate(LocalDateTime date) {
        this.date = date;
    }

    public StatusPost getStatus() {
        return status;
    }

    public void setStatus(StatusPost status) {
        this.status = status;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    public List<Comment> getComments() {
        return comments;
    }

    public void setComments(List<Comment> comments) {
        this.comments = comments;
    }

    public List<Image> getImages() {
        return images;
    }

    public void setImages(List<Image> images) {
        this.images = images;
    }

}

PostController

@RestController
@CrossOrigin(origins = "http://localhost:4200", maxAge = 3600)
@RequestMapping("/post")
public class PostController {

    @Autowired
    PostRepository postRepository;

    @Autowired
    StatusRepository statusRepository;

    @GetMapping
    public List<PostDto> getPosts() {
        List<Post> posts = postRepository.findAll();
        return PostDto.converter(posts);
    }

    @GetMapping("/{id}")
    public ResponseEntity<DetailsPostDto> detailer(@PathVariable Long id) {
        Optional<Post> post = postRepository.findById(id);
        if (post.isPresent()) {
            return ResponseEntity.ok(new DetailsPostDto(post.get()));
        }

        return ResponseEntity.notFound().build();
    }

    @PostMapping
    public ResponseEntity<PostDto> createPost(@RequestBody @Valid PostForm dados, UriComponentsBuilder uriBuilder) {
        Post post = dados.convert(postRepository, statusRepository);
        postRepository.save(post);

        URI uri = uriBuilder.path("/post/{id}").buildAndExpand(post.getId()).toUri();
        return ResponseEntity.created(uri).body(new PostDto(post));
    }

    @DeleteMapping("/{id}")
    @Transactional
    public ResponseEntity<?> removePost(@PathVariable Long id) {
        Optional<Post> optional = postRepository.findById(id);
        if (optional.isPresent()) {
            postRepository.deleteById(id);
            return ResponseEntity.ok().build();
        }

        return ResponseEntity.notFound().build();
    }

}

PostDto

public class PostDto {

    private Long id;
    private String text;

    @JsonFormat(pattern = "dd/MM/yyyy HH:mm:ss")
    private LocalDateTime date;
    private StatusPost status;
    private User user;

    public PostDto() {
    }

    public PostDto(Post post) {
        this.id = post.getId();
        this.text = post.getText();
        this.date = post.getDate();
        this.status = post.getStatus();
        this.user = post.getUser();
    }

    public Long getId() {
        return id;
    }

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

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }

    public LocalDateTime getDate() {
        return date;
    }

    public void setDate(LocalDateTime date) {
        this.date = date;
    }

    public StatusPost getStatus() {
        return status;
    }

    public void setStatus(StatusPost status) {
        this.status = status;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    public static List<PostDto> converter(List<Post> posts) {
        return posts.stream().map(PostDto::new).collect(Collectors.toList());
    }

Modelo Image

@Entity
public class Image implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String urlImage;

    @ManyToOne
    private Post post;

    public Image() {

    }

    public Image(String urlImage, Post post) {
        super();
        this.urlImage = urlImage;
        this.post = post;
    }

    public Long getId() {
        return id;
    }

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

    public String getUrlImage() {
        return urlImage;
    }

    public void setUrlImage(String urlImage) {
        this.urlImage = urlImage;
    }

    public Post getPost() {
        return post;
    }

    public void setPost(Post post) {
        this.post = post;
    }

}

ImageController

@RestController
@CrossOrigin(origins = "http://localhost:4200", maxAge = 3600)
@RequestMapping("/images")
public class ImageController {

    @Autowired
    ImageRepository imageRepository;

    @Autowired
    PostRepository postRepository;

    @GetMapping
    public List<ImageDto> getAllImages() {
        List<Image> images = imageRepository.findAll();
        return ImageDto.convert(images);
    }

    @PostMapping
    public ResponseEntity<ImageDto> saveImage(@RequestBody @Valid ImageForm values, UriComponentsBuilder uriBuilder) {
        Image images = values.convert(imageRepository, postRepository);
        imageRepository.save(images);

        URI uri = uriBuilder.path("/images/{id}").buildAndExpand(images.getId()).toUri();
        return ResponseEntity.created(uri).body(new ImageDto(images));
    }

    @DeleteMapping("/{id}")
    @Transactional
    public ResponseEntity<?> removeImage(Long id) {
        Optional<Image> optional = imageRepository.findByPost(id);
        if (optional.isPresent()) {
            imageRepository.deleteById(id);
            return ResponseEntity.ok().build();
        }

        return ResponseEntity.notFound().build();
    }

}

ImageDto

public class ImageDto {

    private Long id;
    private String urlImage;

    public ImageDto() {

    }

    public ImageDto(Image images) {
        this.id = images.getId();
        this.urlImage = images.getUrlImage();
    }

    public Long getId() {
        return id;
    }

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

    public String getUrlImage() {
        return urlImage;
    }

    public void setUrlImage(String urlImage) {
        this.urlImage = urlImage;
    }

    public static List<ImageDto> convert(List<Image> images) {
        return images.stream().map(ImageDto::new).collect(Collectors.toList());
    }

}

Oi Alexandre,

Exatamente, como sua entidade tem relacionamento com outras no banco de dados, você não poder excluir.

Acho que no seu caso a ideia seria ao excluir o post, também excluir juntamente todas as images e comments que estiverem relacionadas com ele, certo?

Neste caso, basta utilizar o cascade para que a operação de delete seja cascateada para os relacionamentos:

@OneToMany(mappedBy = "post", cascade = CascadeType.REMOVE)
private List<Image> images = new ArrayList<>();

@OneToMany(mappedBy = "post", cascade = CascadeType.REMOVE)
private List<Comment> comments = new ArrayList<>();

Olá Rodrigo, deu certo essa sua dica, possibilitou a exclusão agora.

Gostaria de entender melhor, sobre o cadastro de um registro nessa entidade (post), como ela possui vários relacionamentos, qual seria a forma mais a se seguir.

Preciso cadastrar um post com imagens e links, criei então duas classes que se relacionam (Image e Link), seguindo esse raciocionio, existe alguma maneira de realizar esses cadastros (post + imagens + links) dentro do mesmo método ? Usando um DTO com esse modelo ?

Ou o fluxo correto seria: 1º cadastra o post 2º cadastra a imagem (chamando o método get no ImageController) 3º cadastra o link (chamando o método get no LinkController)

Link do projeto no git -> https://github.com/AlexandreAmpudiaFaria/Framework-api

Oi Alexandre,

Vai depender de como será feito esse cadastro no sistema.

Eu acredito que seja tudo cadastrado de uma vez(dados do post enviados juntamente com as imagens e links), então você pode ter um DTO que recebe todas as informações juntas e usar o PostRepository para salvar o post e alterar o cascade para ser realizado também no cadastro:

@OneToMany(mappedBy = "post", cascade = {CascadeType.REMOVE, CascadeType.PERSIST})
private List<Image> images = new ArrayList<>();

@OneToMany(mappedBy = "post", cascade = {CascadeType.REMOVE, CascadeType.PERSIST})
private List<Comment> comments = new ArrayList<>();

Bacana Rodrigo, acho que consegui compreender.

Aqui na Alura temos algum curso que apresente este cenário ? cadastro com (CascadeType.PERSIST) ?