EDIT: Solved, but I don't know what was different now. If anyone can explain what happened differently, I'd appreciate it.
The way it's working now is by merging only the Parent, not the Child at all, like so:
Parent parent = child.getParent();
parent.getChildren().add(child);
getJpaTemplate().merge(parent);
Now, it seems to be working, but I don't quite get why this would work while my previous attempt didn't.
Original attempt/question:
I'm having an issue with Spring JPA and IndirectSets. I have two entities, Parent and Child, defined below. I have a Spring form in which I'm trying to create a new Child and link it to an existing Parent, then have everything reflected in the database and in the web interface. What's happening is that it gets put into the database, but the UI doesn't seem to agree.
The two entities that are linked to each other in a OneToMany relationship like so:
@Entity
@Table(name = "parent", catalog = "myschema", uniqueConstraints = @UniqueConstraint(columnNames = "ChildLinkID"))
public class Parent {
private Integer id;
private String childLinkID;
private Set<Child> children = new HashSet<Child>(0);
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "id", unique = true, nullable = false)
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
@Column(name = "ChildLinkID", unique = true, nullable = false, length = 6)
public String getChildLinkID() {
return this.childLinkID;
}
public void setChildLinkID(String childLinkID) {
this.childLinkID = childLinkID;
}
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "parent")
public Set<Child> getChildren() {
return this.children;
}
public void setChildren(Set<Child> children) {
this.children = children;
}
}
@Entity
@Table(name = "child", catalog = "myschema")
public class Child extends
private Integer id;
private Parent parent;
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "id", unique = true, nullable = false)
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "ChildLinkID", referencedColumnName = "ChildLinkID", nullable = false)
public Parent getParent() {
return this.parent;
}
public void setParent(Parent parent) {
this.parent = parent;
}
}
And of course, assorted simple properties on each of them. Now, the problem is that when I edit those simple properties from my Spring interface, everything works beautifully. I can persist new entities of these types and they'll appear when using the JPATemplate to do a find on, say, all Parents (getJpaTemplate().find("select p from Parent p")) or on individual entities by ID or another property.
The problem I'm running into is that now, I'm trying to create a new Child linked to an existing Parent through a link from the Parent's page. Here's the important bits of the Controller (note that I've placed the JPA foo in the controller here to make it clearer; the actual JpaDaoSupport is actually in another class, appropriately tiered):
protected Object formBackingObject(HttpServletRequest request) throws Exception {
String parentArg = request.getParameter("parent");
int parentId = Integer.parseInt(parentArg);
Parent parent = getJpaTemplate().find(Parent.class, parentId);
Child child = new Child();
child.setParent(parent);
NewChildCommand command = new NewChildCommand();
command.setChild(child);
return command;
}
protected ModelAndView onSubmit(Object cmd) throws Exception {
NewChildCommand command = (NewChildCommand)cmd;
Child child = command.getChild();
child.getParent().getChildren().add(child);
getJpaTemplate().merge(child);
return new ModelAndView(new RedirectView(getSuccessView()));
}
Like I said, I can run through the form and fill in the new values for the Child -- the Parent's details aren't even displayed. When it gets back to the controller, it goes through and saves it to the underlying database, but the interface never reflects it. Once I restart the app, it's all there and populated appropriately.
What can I do to clear this up? I've tried to call extra merges, tried refreshes (which gave a transaction exception), everything short of just writing my own database access code. I've made sure that every class has an appropriate equals() and hashCode(), have full JPA debugging on to see that it's making appropriate SQL calls (it doesn't seem to make any new calls to the Child table) and stepped through in the debugger (it's all in IndirectSets, as expected, and between saving and displaying the Parent the object takes on a new memory address). What's my next step?