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):
开发者_Python百科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?
I'm not sure this has something to do with your problem but why do you have a childLinkID
property in your Parent
class? This seems really weird, especially since you're using the corresponding column for the join. I wonder how things can work with this mapping and I would fix this. Can you try without (also remove the referencedColumnName
, you don't need this)?
And by the way, why don't you set both sides of the link between Parent
and Child
in the same method? This makes the code hard to read and to maintain IMO.
You edited the question while I was answering and changed the cascading from Parent
to Child
but this looks like a work around of the real issue and I'm not sure you won't face other problems. Please try to fix your mapping as I suggested.
精彩评论