Importante

Você está vendo a versão anterior da nova experiência da Alura que estamos preparando para você. Em breve, ela ganha uma identidade visual novinha totalmente pensada em potencializar seus estudos!

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) ?