-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 1 post ] 
Author Message
 Post subject: how to correctly delete a parent
PostPosted: Tue Nov 04, 2014 7:06 am 
Newbie

Joined: Tue Nov 04, 2014 3:36 am
Posts: 4
Ok, so I'd like to implement a simple forum example. So, I have threads, messages and users, of course and these are the pojos (I omitted the usually getters and simplicity)

**Message**
Code:
    @Entity
    @Table(name = "message")
    public class Message implements java.io.Serializable, RecognizedServerEntities
    {       
       @Id
       @GeneratedValue(strategy = IDENTITY)
       @Column(name = "id", unique = true, nullable = false)
       private Integer id;
       
       @ManyToOne(fetch = FetchType.LAZY)
       @Cascade({ CascadeType.SAVE_UPDATE })
       @JoinColumn(name = "thread", nullable = false)
       private Thread thread;
       
       @ManyToOne(fetch = FetchType.LAZY)
       @Cascade({ CascadeType.SAVE_UPDATE })
       @JoinColumn(name = "author", nullable = true)
       private User user;
       
       @Column(name = "title", nullable = false, length = 31)
       private String title;
       
       @Column(name = "body", nullable = false, columnDefinition = "Text")
       private String body;
       
       @Temporal(TemporalType.TIMESTAMP)
       @Column(name = "last_modified_date", nullable = false, length = 19)
       private Date lastModifiedDate;
       
       @Temporal(TemporalType.TIMESTAMP)
       @Column(name = "created_date", nullable = false, updatable = false, length = 19)
       private Date createdDate;
    }


**User**
Code:
    @Entity
    @Table(name = "user", uniqueConstraints =
    { @UniqueConstraint(columnNames = "email"),
        @UniqueConstraint(columnNames = "nick") })
    public class User implements java.io.Serializable, RecognizedServerEntities
    {
       @Id
       @GeneratedValue(strategy = IDENTITY)
       @Column(name = "id", unique = true, nullable = false)
       private Integer id;
       
       @Column(name = "email", unique = true, nullable = false, length = 31)
       private String email;
       
       @Column(name = "password", nullable = false, length = 31)
       private String password;
       
       @Column(name = "nick", unique = true, nullable = false, length = 31)
       @NaturalId(mutable = false)
       private String nick;
       
       @Temporal(TemporalType.TIMESTAMP)
       @Column(name = "registered_date", nullable = false, updatable = false, length = 19)
       private Date registeredDate;
       
       @OneToMany(fetch = FetchType.LAZY, mappedBy = "user", orphanRemoval = false)
       private Set<Thread> threads = new HashSet<Thread>(0);
       
       @OneToMany(fetch = FetchType.LAZY, mappedBy = "user", orphanRemoval = false)
       private /**transient /**/ Set<Message> messages = new HashSet<Message>(0);
   }

**Thread**
Code:
    @Entity
    @Table(name = "thread")
    public class Thread implements java.io.Serializable, RecognizedServerEntities
    {
       @Id
       @GeneratedValue(strategy = IDENTITY)
       @Column(name = "id", unique = true, nullable = false)
       private Integer id;
       
       @ManyToOne(fetch = FetchType.LAZY)
       @Cascade({CascadeType.SAVE_UPDATE})
       @JoinColumn(name = "parent_thread", nullable = true)
       private Thread parentThread;
       
       @ManyToOne(fetch = FetchType.LAZY)
       @Cascade({CascadeType.SAVE_UPDATE})
       @JoinColumn(name = "author", nullable = true)
       private User user;
       
       @Column(name = "title", nullable = false, length = 63)
       private String title;
       
       @Temporal(TemporalType.TIMESTAMP)
       @Column(name = "last_modified_date", nullable = false, length = 19)
       private Date lastModifiedDate;
       
       @Temporal(TemporalType.TIMESTAMP)
       @Column(name = "created_date", nullable = false, updatable = false, length = 19)
       private Date createdDate;
       
       @OneToMany(fetch = FetchType.LAZY, mappedBy = "thread"/**/, orphanRemoval = true/**/)
       @Cascade({ CascadeType.REMOVE })
       private /**transient /**/ Set<Message> messages = new HashSet<Message>(0);
       
       @OneToMany(fetch = FetchType.LAZY, mappedBy = "parentThread", orphanRemoval = true)
       @Cascade({CascadeType.REMOVE })
       private /**transient /**/ Set<Thread> subThreads = new HashSet<Thread>(0);
    }


I have many doubts on the annotations of course, but these are the relevant choice.
    - When I delete an user, I don't want to delete all his threads and messages, so it make sense to don't use orphan-removal or cascade delete on the @OneToMany associations (ie the messages and threads collections).
    - Also, because the id is automatically generated from the database, I don't think it make sense at all to use the annotation CascadeType.UPDATE (or SAVE_UPDATE) on the collections of all the entity.
    - When we delete a thread, we want that all its subthreads and all its messages to be deleted. So, I use the CascadeType.REMOVE and orphan-removal annotations (don't know if it make sense to use both).
    - On all the @ManyToOne associations, I use the CascadeType.ALL. The idea is that if we delete a message or a subthread, all the parents will be updated. Make sense?
    - All the collections are not transient.
Feel free to propose suggestion on this of course. To complicate a little bit more the whole story, I also have another class, StorageManager<T>, that is used to encapsulate the common code between entities (I can post the code if you think it is useful). Briefly, it implements the "one session per transaction" pattern. So each methodX() of StorageManager<T> class:
  1. invoke `sessionFactory.openSession()` and `session.beginTransaction()`
  2. invoke `session.methodX()`
  3. invoke `transaction.commit()`
  4. invoke `session.clear()` and `session.close`

Btw, given the whole story, this is the question: suppose I have a thread "mThread" started from the user "mUser" with many messages from different users, how can I safely delete "mUser"? Then, how can I safely remove all other users?
I tried different things, but I'm not sure of anything and in most cases I only have exceptions.

So, in the following code, this is what I've done to safely delete "mUser"
Code:
    for (Thread t : mUser.getThreads())
       {
          t.setUser(null);
          storageManagerThread.update(t);
       }
       for (Message m : mUser.getMessages())
       {
          m.setUser(null);
          storageManagerMessage.update(t);
       }
       storageManagerUser.delete(mUser);

Until this point, all the table in the database have the right values. However, I don't know if it is the right way to proceed, because (I think) it leaves dirty collections.
Indeed, when at later point I try to execute some other options (e.g. `update(mThread)` or delete a message from `mThread`) a `NullPointerException` was thrown.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 1 post ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.