JPA IndirectSet 更改未反映在 Spring 前端中

发布于 2024-08-31 07:23:12 字数 3569 浏览 9 评论 0原文

编辑:解决了,但我不知道现在有什么不同。如果有人能以不同的方式解释发生了什么,我将不胜感激。

它现在的工作方式是仅合并父级,根本不合并子级,如下所示:

Parent parent = child.getParent();
parent.getChildren().add(child);
getJpaTemplate().merge(parent);

现在,它似乎有效,但我不太明白为什么这会起作用,而我之前的尝试却不起作用。

最初的尝试/问题:

我遇到了 Spring JPA 和 IndirectSets 的问题。我有两个实体,父实体和子实体,定义如下。我有一个 Spring 表单,我试图在其中创建一个新的子项并将其链接到现有的父项,然后将所有内容反映在数据库和 Web 界面中。发生的情况是它被放入数据库中,但用户界面似乎不同意。

以 OneToMany 关系相互链接的两个实体如下所示:

@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;
    }
}

当然,每个实体都有各种简单的属性。现在的问题是,当我从 Spring 界面编辑这些简单属性时,一切都运行良好。我可以保留这些类型的新实体,并且当使用 JPATemplate 查找所有父级 (getJpaTemplate().find("select p from Parent p")) 或按 ID 或单个实体进行查找时,它们会出现另一个财产。

我遇到的问题是,现在,我试图创建一个通过父级页面的链接链接到现有父级的新子级。这是控制器的重要部分(请注意,我已将 JPA foo 放在控制器中以使其更清晰;实际的 JpaDaoSupport 实际上位于另一个类中,适当分层):

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()));
}

就像我说的,我可以运行表单并填写子项的新值——父项的详细信息甚至不会显示。当它返回到控制器时,它会遍历并将其保存到底层数据库,但界面永远不会反映它。一旦我重新启动应用程序,它就全部存在并正确填充。

我可以做什么来解决这个问题?我尝试过调用额外的合并,尝试过刷新(这给出了事务异常),除了编写我自己的数据库访问代码之外的一切。我已确保每个类都有适当的 equals() 和 hashCode(),进行完整的 JPA 调试以查看它是否正在进行适当的 SQL 调用(它似乎没有对 Child 表进行任何新调用)并逐步执行在调试器中(正如预期的那样,所有内容都在 IndirectSets 中,并且在保存和显示 Parent 之间,对象采用新的内存地址)。我的下一步是什么?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

寄人书 2024-09-07 07:23:12

不确定这与您的问题有关,但是为什么您的Parent类中有一个childLinkID属性?这看起来真的很奇怪,特别是因为您正在使用相应的列进行连接。我想知道这个映射如何工作,我会解决这个问题。您可以尝试不使用(也删除referencedColumnName,您不需要这个)吗?

顺便说一句,为什么不在同一个方法中设置 ParentChild 之间链接的两端呢?在我看来,这使得代码难以阅读和维护。


您在我回答时编辑了问题,并将级联从 Parent 更改为 Child,但这看起来像是解决实际问题的方法,我不确定您是否会这样做。 t 面临其他问题。请尝试按照我的建议修复您的映射。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文