techPackets
techPackets

Reputation: 4504

cascade type save update in Hibernate

I am using hibernate with JPA annotations for relationship mapping. I have three entities in my code User Group & User_Group

User & Group are in a ManyToMany relationship.

User_Group is a kinda bridge table but with some additional fields. So here is the modified mapping code.

User

@Entity
@Table(name = "USERS")
public class User {

@OneToMany(mappedBy = "user")
private Set<UserGroup> userGroups
}

Group

@Entity
@Table(name = "GROUPS")
public class Group {
@OneToMany(mappedBy = "group")
private Set<UserGroup> userGroups
}

UserGroup

@Entity
@Table(name = "USERS_GROUPS")
public class UserGroup {

@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "USER_ID")  
private User user;
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "GROUP_ID")
private Group group;
}

When I set the user & group object to the usergroup & save it.

User user = new User("tommy", "ymmot", "[email protected]");
Group group = new Group("Coders");

UserGroup userGroup = new UserGroup();
userGroup.setGroup(group);
userGroup.setUser(user);
userGroup.setActivated(true);
userGroup.setRegisteredDate(new Date());

session.save(userGroup);

Things work fine. With CascadeType.ALL the group object & user object are updated too. But when I delete the userGroup object. The child object are deleted too.

Deletion of child objects is a strict no no.

There is no CascadeType.SAVE-UPDATE in JPA, which just does save or update but no delete. How do I achieve this.

If I remove the CascadeType.ALL from the mapping the child objects don't get updated & I need them to be updated.

Upvotes: 10

Views: 33650

Answers (3)

jumping_monkey
jumping_monkey

Reputation: 7779

It's almost always a code smell when propagating from child to parent entity, it should be the other way round.

From Cascading best practices:

Cascading only makes sense only for Parent – Child associations (the Parent entity state transition being cascaded to its Child entities). Cascading from Child to Parent is not very useful and usually, it’s a mapping code smell.

From Hibernate best practices:

  1. Avoid cascade remove for huge relationships Most developers (myself included) get a little nervous when they see a CascadeType.REMOVE definition for a relationship. It tells Hibernate to also delete the related entities when it deletes this one. There is always the fear that the related entity also uses cascade remove for some of its relationships and that Hibernate might delete more database records than intended. During all the years I’ve worked with Hibernate, this has never happened to me, and I don’t think it’s a real issue. But cascade remove makes it incredibly hard to understand what exactly happens if you delete an entity. And that’s something you should always avoid. If you have a closer look at how Hibernate deletes the related entities, you will find another reason to avoid it. Hibernate performs 2 SQL statements for each related entity: 1 SELECT statement to fetch the entity from the database and 1 DELETE statement to remove it. This might be OK, if there are only 1 or 2 related entities but creates performance issues if there are large numbers of them.

Upvotes: 1

Bevor
Bevor

Reputation: 8605

CascadeType.ALL includes CascadeType.REMOVE too. The solution is to use all CascadeType.* you need except CascadeType.REMOVE, like so:

@ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.REFRESH, CascadeType.MERGE}))

in your UserGroup definitions.

Upvotes: 10

JB Nizet
JB Nizet

Reputation: 691655

SAVE_UPDATE is for save(), update(), and saveOrUpdate(), which are 3 Hibernate-proprietary methods. JPA only has persist() and merge(). So, if you want to use cascading on Hibernate-proprietary methods, you'll need to use Hibernate-proprietary annotations. In this case, Cascade.

Or you could stop using the Hibernate Session, and use the standard JPA API instead.

Upvotes: 18

Related Questions