Search code examples
javahibernatejpahibernate-mappingcascade

Hibernate - how to use cascade in relations correctly


I'm using hibernate in my spring mvc application and have a question about cascade. I see to many similar questions about it, but none of them can answer my question. Suppose I have User and UserPosition objects. User has a collection of UserPosition and also has one UserPosition as the default position. Structure is look like this:

User:

@OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
private Collection<UserPosition> userPositionCollection;

public Collection<UserPosition> getUserPositionCollection() {
    return userPositionCollection;
}

public void setUserPositionCollection(Collection<UserPosition> collection) {
    this.userPositionCollection = collection;
}


 @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinColumn(name = "default_User_Position_ID", referencedColumnName = "id")
private UserPosition defaultUserPosition;

public UserPosition getDefaultUserPosition() {
    return defaultUserPosition;
}

public void setDefaultUserPosition(UserPosition defaultUserPosition) {
    this.defaultUserPosition = defaultUserPosition;
}

UserPosition:

@JoinColumn(name = "user_id", referencedColumnName = "id")
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
private User user;

public User getUser() {
    return user;
}

public void setUser(User user) {
    this.user = user;
}

@OneToOne(fetch = FetchType.LAZY, mappedBy = "defaultUserPosition", cascade = CascadeType.PERSIST)
private User defaultUserPosition;

public User getDefaultUserPosition() {
    return defaultUserPosition;
}

public void setDefaultUserPosition(User defaultUserPosition) {
    this.defaultUserPosition = defaultUserPosition;
}

Now, my question is what's the best practice to use cascade for saving related objects?. In fact, I confused with these three solutions:

Solution 1:

User user = new User();
//some setters and getters 
UserPosition userPosition = new UserPosition();
//some setters and getters 
List<UserPosition> positionList = new ArrayList<>();
positionList.add(userPosition);
user.setDefaultUserPosition(userPosition);
user.setUserPositionCollection((Collection<UserPosition>) positionList );
session.persist(user)

Solution 2:

User user = new User();
//some setters and getters 
UserPosition userPosition = new UserPosition();
//some setters and getters 
userPosition.setUser(user);
userPosition.setDefaultUserPosition(user);
session.persist(userPosition)

Solution 3(Combination of two previous solutions):

User user = new User();
//some setters and getters 
UserPosition userPosition = new UserPosition();
//some setters and getters 
List<UserPosition> positionList = new ArrayList<>();
positionList.add(userPosition);
user.setDefaultUserPosition(userPosition);
user.setUserPositionCollection((Collection<UserPosition>) positionList );
userPosition.setUser(user);
userPosition.setDefaultUserPosition(user);
session.persist(user);

This is very important for me, so please help me. Which solution is correct and where should be cascade property? Thank you for your time.


Solution

  • The User is the parent entity and cascading always propagates from Parent to Child entities.

    Therefore, the User associations become:

    @OneToMany(mappedBy = "user", 
        fetch = FetchType.LAZY, 
        cascade = CascadeType.ALL, 
        orphanRemoval = true)
    private Collection<UserPosition> userPositionCollection;
    

    but for the default position, the User becomes the Child of the association:

    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "default_User_Position_ID", referencedColumnName = "id")
    private UserPosition defaultUserPosition;
    

    In the UserPosition class is the other way around:

    @JoinColumn(name = "user_id", referencedColumnName = "id")
    @ManyToOne(fetch = FetchType.LAZY)
    private User user;
    

    and

    @OneToOne(fetch = FetchType.LAZY, 
        mappedBy = "defaultUserPosition", 
        cascade = CascadeType.PERSIST)
    private User defaultUserPosition;
    

    Then you also have to add the following utility methods that always synchronize both sides. These go into the User class:

    public void addUserPosition(UserPosition userPosition) {
        userPositionCollection.add(userPosition);
        userPosition.setUser(this);
    }
    
    public void addDefaultUserPosition(UserPosition userPosition) {
        defaultUserPosition = userPosition;
        userPosition.setDefaultUserPosition(this);
    }
    

    The persisting logic becomes:

    User user = new User();
    //some setters and getters 
    UserPosition userPosition = new UserPosition();
    //some setters and getters 
    
    user.addUserPosition(userPosition);
    user.setDefaultUserPosition(userPosition);
    
    session.persist(user);