Example:
The problem: I would like to maintain children by manipulating set of children held in parent entity in detached state. After new children are added and/or some of its existing children are removed, parent entity is updated (merged) and with it set of children is updated as well.@Entity
@Table(name = "PARENT")
public class Parent implements Serializable {
...
@OneToMany(cascade = { CascadeType.PERSIST,
CascadeType.MERGE },
mappedBy = "parent")
private SetgetChildren() {
return children;
}
...
}
@Entity
@Table(name = "CHILD")
public class Child implements Serializable {
...
@ManyToOne@JoinColumn(name = "PARENTID",
nullable = true)private Parent getParent() {
return parent;
}
...
}
Note, that child entity may simply be unassigned from its parent to exist with no parent (nullable = true): by removing child from children list we are not removing child entity from persistent context. This case is more general than one when child is simply removed when unassigned from its parent.
Also note, that child class should implement equals() and hashCode() based on values that uniquely identify each instance. Of course, this is simply JPA best practice.
The following classic solution by the book handles adding new children flawlessly:
But if I decide to remove children from the list then this will not have any effect on database as merge operation on parent entity (update) will happily restore removed children. You can search various JPA forums on this topic, e.g.@Entity
@Table(name = "PARENT")
public class Parent implements Serializable {
...
@OneToMany(cascade = { CascadeType.PERSIST,
CascadeType.MERGE },
mappedBy = "parent")
private SetgetChildren() {
return children;
}
public void addChild(Child theChild) {
theChild.setParent(this);
children.add(theChild);
}
...
}
public class ParentManager {
...
@Transactional(propagation = Propagation.REQUIRED,
readOnly = false)
public void updateParent(Parent parent) {
parentDAO.update(parent);
}
...
}
http://jira.jboss.org/jira/browse/EJBTHREE-941
http://forum.java.sun.com/thread.jspa?threadID=5145294&tstart=210
http://forums.oracle.com/forums/thread.jspa?messageID=1707487
The implementation I propose is not confined to JPA entity classes - it requires coding at higher level of persistent context (where EntityManager is used). This is usually handled at session bean or business manager levels (depending on if I use EJB3 or POJO frameworks):
And Parent manager update:@Entity
@Table(name = "PARENT")
public class Parent implements Serializable {
...
@OneToMany(cascade = { CascadeType.PERSIST,
CascadeType.MERGE },
mappedBy = "parent")
private SetgetChildren() {
return children;
}
@Transient
public SetgetUnassignedChildren() {
return unassignedChildren;
}
public void addChild(Child theChild) {
theChild.setParent(this);
children.add(theChild);
}
public void unassignChild(Child theChild) {
if (children.remove(theChild)) {
theChild.setParent(null);
unassignedChildren.add(theChild);
}
}
...
}
Thus, any change to set of children using addChild() and unassignChild() is guaranteed to propagate to the database via single update to parent entity - problem solved.
public class ParentManager {
...
@Transactional(propagation = Propagation.REQUIRED,
readOnly = false)
public void updateParent(Parent parent) {
if (!parent.getUnassignedChildren().isEmpty()) {
for (Child child : parent.getUnassignedChildren()) {
childManager.update(child);
}
}
parentDAO.update(parent);
}
...
}
No comments:
Post a Comment