40

I'm am currently trying to use a Spring Data repository to delete some of my entities. The delete call works without any exceptions/error messages, but the entity is not deleted afterwards.

Those are my entities:

public class Board implements Serializable {

    @Id
    @GeneratedValue(generator = "uuid2")
    @GenericGenerator(name = "uuid2", strategy = "uuid2")
    @Column(columnDefinition = "BINARY(16)")
    private UUID uuid;

    @OneToMany(fetch=FetchType.EAGER, cascade=CascadeType.ALL, orphanRemoval = true, mappedBy = "board")
    private List<Post> posts = new ArrayList<Post>();
}

and

public class Post implements Serializable {

    @Id
    @GeneratedValue
    private long id;

    @ManyToOne(optional = false)
    @JoinColumn(name="board_uuid", updatable = false, nullable = false)
    @JsonBackReference
    private Board board;
}

The repository is as simple as it can be:

@Repository
public interface PostRepository extends CrudRepository<Post, Long> {
}

The delete call is something like

postRepository.delete(50);

Any ideas why this change doesn't reflect in the database?

Edit 1:

I found a workaround, but I still don't understand what the real problem is. It "works" if I delete the Post like this (there are a couple of exceptions because of constraint violations, but still the Post gets deleted):

post.setBoard(null);
postRepo.delete(post);

Edit 2:

When I have a look at the SQL statements executed I can see that hibernate is not even trying to delete. The only thing that happens are those two select statements:

Hibernate: select post0_.id as id1_1_0_, post0_.board_uuid as board_uu6_1_0_, post0_.content as content2_1_0_, post0_.x as x3_1_0_, post0_.y as y4_1_0_, post0_.z as z5_1_0_, board1_.uuid as uuid1_0_1_ from Post post0_ left outer join Board board1_ on post0_.board_uuid=board1_.uuid where post0_.id=?
Hibernate: select posts0_.board_uuid as board_uu6_0_0_, posts0_.id as id1_1_0_, posts0_.id as id1_1_1_, posts0_.board_uuid as board_uu6_1_1_, posts0_.content as content2_1_1_, posts0_.x as x3_1_1_, posts0_.y as y4_1_1_, posts0_.z as z5_1_1_ from Post posts0_ where posts0_.board_uuid=?

Edit 3

Turns out the cascade=CascadeType.ALL on posts seems to be the problem. Without it the delete works fine (but I am missing the cascade of changes to posts now)

Nitek
  • 2,515
  • 2
  • 23
  • 39
  • Did you try postRepository.delete(50L); with a long? – Stefaan Neyts Mar 20 '15 at 17:27
  • Well the actual code is more like public void delete(Post post) { postRepo.delete(post.getId()); } so, yes. – Nitek Mar 20 '15 at 17:34
  • It's weird because few days ago I developed almost the same without issues. Maybe you can have a look in the spring logs by adding a logger and set spring to trace level. – Stefaan Neyts Mar 20 '15 at 17:37
  • Do you have different Options in one of the OneToMany/ManyToOne or JoinColumn annotations? – Nitek Mar 20 '15 at 17:39
  • Lazy loading, no cascade type defined and no orphanRemoval defined. I suppose you annotated both classes with @entity? Does save and load work? – Stefaan Neyts Mar 20 '15 at 17:44
  • updatable = false ... Not sure if this is causing trouble. Did you try yo set this to true? – Stefaan Neyts Mar 20 '15 at 17:46
  • Yes, I did. When I remove the post from the posts list in the board and save the board, then everything works fine, but I'd like to be able to delete a post directly ... – Nitek Mar 20 '15 at 17:47
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/73437/discussion-between-nitek-and-stefaan-neyts). – Nitek Mar 20 '15 at 17:47
  • 1
    I'm having this same issue. Did you find the fix? – cosbor11 Sep 19 '15 at 20:21

6 Answers6

83

The problem seems to be that you are using cascade=CascadeType.ALL, which also includes CascadeType.PERSIST. CascadeType.PERSIST means that the child entity is completely managed by the parent and you cannot delete it directly. In order to delete you just need to remove it from the parent.

You could just add the other CascadeTypes instead of all. e.g CascadeType.REMOVE, if the only thing you would want is to remove the child if the parent is removed.

nbro
  • 15,395
  • 32
  • 113
  • 196
user2936091
  • 846
  • 7
  • 4
  • 9
    do you know if there is a reason why not to throw an exception, I mean I'm trying to delete and is not possible for some constraint, wouldn't bee good if its notified? – cesaregb Dec 28 '16 at 14:28
  • I had similar issue. In my Board I had only one Post. I deleted the Post, and the List in board become empty. But later it did not delete, the List still has the post. What makes difference? No code change. – Kisor Biswal Oct 14 '17 at 20:29
8

Building on the excellent answer from user2936091 above, I just wanted to mention a (related) workaround I stumbled upon today: if the parent entity is not fetched into the Hibernate context, you are able to delete it directly.

In my case this was achieved by setting fetch = FetchType.LAZY on the @ManyToOne relationship. I wanted this change for performance reasons anyway, and noticed that without the parent eagerly fetched Hibernate was free to delete it via the repository method call.

chut
  • 695
  • 4
  • 7
1

cascade=CascadeType.PERSIST is used for updating insert in both side but for delete it will restrict.

@ManyToOne(cascade=CascadeType.PERSIST, fetch = FetchType.LAZY)
//  @JoinColumn(name = "qid")
    @JoinColumn(name = "qid", referencedColumnName = "qid", foreignKey = @ForeignKey(name = "qid"), nullable = false)
    // @JsonIgnore
    @JsonBackReference
    private QueueGroup queueGroup;
Shubham
  • 707
  • 9
  • 7
0

This is because you set mappedBy = "board" on Post class, doing so you tell that the master of Post is Board.

Vyncent
  • 1,185
  • 7
  • 17
  • I have set it in the Board class, so if I understand "mappedBy" correctly I told it that the "board" attribute in Post is the master – Nitek Mar 20 '15 at 18:00
  • http://stackoverflow.com/questions/9108224/can-someone-please-explain-mappedby-in-hibernate nice post to read – Stefaan Neyts Mar 20 '15 at 18:21
0

A simple alternative:

@Repository
public interface PostRepository extends CrudRepository<Post, Long> {

@Modifying(clearAutomatically = true, flushAutomatically = true)
@Query(value = "DELETE FROM post WHERE id = ?1", nativeQuery = true)
void deleteById(long postId);

}
Michel Fortes
  • 819
  • 8
  • 12
0

What @user2936091 said is true but it doesn't solve the problem. I also had the exact same problem like you.

The solution is to remove fetch=FetchType.EAGER from the parent class. Wen fetch type EAGER is used, the instance of board in the post object will have the

 private List<Post> posts = new ArrayList<Post>();

populated because it was eagerly loaded, which means it still has a reference to itself, like @user2936091 mentions.

My workaround to still load related properties eagerly was to create a DBService class and load the objects from there like this

@Service
public class DBService {

    BoardRepository boardRepo;

    @Transactional
    public Board getBoardProcess(Long id) {
       Board board = boardRepo.findById(id).get();
       board.getPosts().size(); // This will trigger the db query so that posts gets loaded
       // It needs to be done within a transactional so that the session is still the same to populate the fields. If it's done from outside of this method. It will not work
    }
}
nissim_dev
  • 323
  • 2
  • 14