Hibernate merge loses data
Asked Answered
F

1

6

I am getting a strange problem with Hibernate and merge.

The structure of the classes in question is like this:

Project --> CaseWorkerA --|> CaseWorker --|> User

So basically I have a Project class, which contains a reference to a CaseWorkerA, which is a subclass of CaseWorker, which again is a subclass of User.

In code:

public class Project {
    [...]
    private CaseWorkerA caseWorkerA;

    @ManyToOne(fetch = FetchType.EAGER)
    @Cascade({ org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.PERSIST,
           org.hibernate.annotations.CascadeType.REFRESH, org.hibernate.annotations.CascadeType.MERGE })
    @JoinColumn(name = "CaseWorker_A")
    public CaseWorkerA getCaseWorkerA() {
        return caseWorkerA;
    }
}

Then we have the User-hierarchy:

public class User {
    [...]
}

public class CaseWorker extends User {
    private CaseWorkerStatus status;

    @Enumerated(EnumType.STRING)
    public CaseWorkerStatus getStatus() {
        return status;
    }
    [...]
}

public class CaseWorkerA extends CaseWorker {
    [...]
}

Then, there is a method in a Dao-class, for storing a project:

public class ProjectDao {
    [...]
    public Project saveUpdateProject(final Project project) {
        if (project.getId() == null) {
            getSession(false).save(project);
        } else {
            project = (Project) getSession(false).merge(project);
        }
        getHibernateTemplate().flush();
        return project;
    }
}

Now, the problem is as follows:

The Dao-method recieves a project that exists in the database. This project is connected to a CaseWorkerA, which has a status CaseWorkerStatus.ACTIVE (in both the database and in the incomming object). But after the merge, the status of the caseworker becomes null.

I really don't get how this is possible, since the value is the same in the database, as in the object to be stored, I would expect it to stay the same after the merge.

(There are no triggers in the database for this field..)

(I am going to try to change the dao-method to use saveOrUpdate instead, but even if this will fix the problem I would still very much like to know what caused it in the first place).

Update:

So I fiddled around with the debugger, and found the following: When I queried the session for the CaseWorker in question, it appeared with it's status-field set (actually, the object returned was exactly the one that was connected to the Project).

Doing a saveOrUpdate, and then a get, resulted in a CaseWorker with the status-field set. So it seems to be a problem with the merge method..

Flack answered 31/8, 2012 at 13:16 Comment(4)
Is the status field in CaseWorker or CaseWorkerA? Also are you using some sort of second level cache - such as Ehcache?Frigate
The status field is in CaseWorker. As far as I am aware, we are not using any second level cache. Perhaps I should try to query for the CaseWorkerA through the debugger, using the same session that is used for the merge?Flack
Is your enum CaseWorkerStatus annotated with @Enum? Of is it not an enum?Dippy
It is not, but is that really necessary? Looked into another project, and non of the enums there have an (at)Enum tag. The referencing side has and (at)Enumerated-tag though..Flack
F
0

Managed to figure it out, it turned out that there was a second path from Project to User (lastChangedBy kind of thing), where someone had decided to put cascade for some strange reason. So when the person who last changed the project was a CaseWorker, then the lastChangedBy referenced it as a User, which doesn't know anything about the status, so it was stored a null.

Code:

public class Project {
    private User changedBy;

    @Cascade({ org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.PERSIST,
               org.hibernate.annotations.CascadeType.REFRESH, org.hibernate.annotations.CascadeType.MERGE })
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "changed_by")
    public User getChangedBy() {
        return this.changedBy;
    }
    [...]
}

The user that was set as changedBy was retrieved from the database like this:

public class UserDao {
    public User getUser(final String userName) {
        return (User) getSession(false).createQuery("from User u where u.loginName = :username").setParameter("username", userName).uniqueResult();
    }
}

Appearantly, even if the user one ends up retrieving is-a CaseWorker, the fields pertaining to a CaseWorker does not get retrieved..

Anyways, removed the uneccesary cascade, and it's all good :)

Flack answered 4/9, 2012 at 12:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.