0

I've done considerable research it doesn't work for me for some reason I don't even get into the situation where I get "zombie" instances(like existing in session but not in db anymore - I wish I had this situation) since remove() is not propagated into database. The child record continues to exist inside my db.

Consider two tables: User and Token(FK_User_Id) , relationship one-to-many respectively

Inside DAO final code which doesn't work:

public void deleteToken(Token t) {

    Token b = em.merge(t);

    em.remove(b); // does nothing

    em.flush();
}

Inside controller:

    Object obj;
    Token token;

    obj = tokenService.getByString(urlToken); // query returning detached object
    User user;

    if (obj != null) {

        token = (Token) obj; // Detached, query is "fetch" overriding lazy init, so token contains its user parent

        user = token.getUser(); // simply get the user to which this token belongs

        if ((token.getExpiryDate().compareTo(new GregorianCalendar()
                .getTime())) == 1) {

            userService.activateUser(user); // user gets merged inside and its relevant property is changed and propagated to db successfully

            tokenService.deleteToken(token); // this line calls DAO method described in snippet above - it fails to delete the token, but no error or exception - record simply stays in database

        model.addAttribute("activationSuccess", "true");


        }...

User entity:

public class User {

    public static final String FIND_USER_BY_TOKEN = "findUserByToken";
    public static final String FIND_USER_BY_USERNAME = "findUserByUsername";
    public static final String FIND_USER_BY_EMAIL = "findUserByEmail";

    @Id
    @GeneratedValue
    private Long id;

    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval=true)
    private List<Token> tokens = new ArrayList(); ...

Token entity:

@Entity
@Table(name = "token")
@NamedQueries({
    @NamedQuery(name=Token.FIND_TOKEN_BY_STRING, query="Select t From Token T where t.tokenString=:string")
})
public class Token {

    public static final String FIND_TOKEN_BY_STRING = "findTokenById";
    @Id
    @GeneratedValue
    Long id;

    @ManyToOne(optional=true)
    private User user; ...

Now if I call something like:

    User c = b.getUser();
            em.remove(c);

Inside DAO snippet, it deletes both token and user which is not what I want. Only token must be deleted.

Basically what I am trying to do is to retrieve Token by string property and along with it the user parent which owns this token. Then I retrieve this user from token and change some property for the user(changes propagated into db). All successful so far. Then I want to delete the token as a final operation but in a way that user will not be deleted.

I am on my fifth hour on this please help... I managed to setId to null for the token(propagated into db), but it only gets me the point where token no longer has owner but still persists in database. To delete it I tried to merge the Token inside DAO with null which threw me exception. Then I tried to set Tokens list value to null inside User(which was retrieved from this token) and it also through me exception.

How I am supposed to delete child token entity which I retrieved with its parent but keep parent present in db?

SQL Hibernate log shows no delete query...after passing remove method.

Thanks,

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Aubergine
  • 5,862
  • 19
  • 66
  • 110
  • Weird... some advice: Have you tried to get rid of the relationship from the User? Try that and then ask yourself whether you really need it (probably you need that for em.remove(user)). On the other hand, the token could remain somewhere in the loaded Token list of a managed User? What application server do you use? Also, are you sure you have looked in the correct DB table (there should be the Token table and no intermediate table like Token_User)? – V G Sep 09 '13 at 13:41

1 Answers1

2

If you do not dereference the token from User's list of tokens, cascade persist (you have cascade all set which includes persist) will cause the token to be resurrected at some point. You must clear all references to the child, especially ones marked cascade persist when you want to remove entities. Something like:

if ((token.getExpiryDate().compareTo(new GregorianCalendar()
                .getTime())) == 1) {
    user.getTokens().remove(token);
    userService.activateUser(user); 
    //tokenService.deleteToken(token); //no longer needed due to orphanremoval being true
    model.addAttribute("activationSuccess", "true");
    ...
Chris
  • 20,138
  • 2
  • 29
  • 43
  • Yeah I found your answer in other question and upvoted, currently I changed to Cascade.remove, how can I clear all references to the child to have Cascade.ALL? Simply assign every element in the child list token of the parent user to null? – Aubergine Sep 10 '13 at 01:14
  • Removing the child from collections, and setting references to it to null is required to keep your managed objects in synch with what is or what you want in the database. JPA makes references easy, but they still aren't free and should be used and maintained carefully. – Chris Sep 10 '13 at 13:46