字段为空时,JPA/Hibernate在setters中使用关联对象

字段为空时,JPA/Hibernate在setters中使用关联对象,hibernate,jpa,Hibernate,Jpa,我的代码看起来像这样。为什么我在setChildren()中获得的名称为null public class Node { private String id, name, parentName; private Set<Node> children = new HashSet<Node>(); private Node parent; private Set<String> childrenNames =

我的代码看起来像这样。为什么我在
setChildren()
中获得的名称为
null

    public class Node {
      private String id, name, parentName;
      private Set<Node> children = new HashSet<Node>();
      private Node parent;
      private Set<String> childrenNames = new HashSet<String>();

      @Id @GeneratedValue public String getId() { return id; }

      public String getName() {return name;}

      @ManyToOne @JoinColumn(name="PARENT_ID") public Node getParent(){retrun parent;}

      @OneToMany(cascade=CascadeType.ALL, mappedBy="parent", fetch=FetchType.EAGER)
      public Set<Client> getChildren(){return Collections.unmodifiableSet(children)}

      @Transient public String getParentName() { return parentName;}
      @Transient public Set<String> getChildrenNames() {return childrenNames;}

      // PROBLEM here ------------
      public void setChildren(Set<Node> children) {
        this.children = children;
        for(Node child : children) {
          childrenNames.add(child.getName());  //Adds NULL !!!!!!
        }
      }

      // Other Setters

    }
公共类节点{
私有字符串id、名称、父名称;
private Set children=new HashSet();
私有节点父节点;
private Set childrenNames=new HashSet();
@Id@GeneratedValue公共字符串getId(){return Id;}
公共字符串getName(){return name;}
@ManyToOne@JoinColumn(name=“PARENT_ID”)公共节点getParent(){retrun PARENT;}
@OneToMany(cascade=CascadeType.ALL,mappedBy=“parent”,fetch=FetchType.EAGER)
公共集getChildren(){返回集合。不可修改集(子项)}
@临时公共字符串getParentName(){return parentName;}
@临时公共集getChildrenNames(){return childrenNames;}
//这里有问题------------
公共无效集合子对象(集合子对象){
这个。孩子=孩子;
用于(节点子节点:子节点){
childrenNames.add(child.getName());//添加NULL!!!!!!
}
}
//其他二传手
}

我是n00b,所以请帮帮忙。

如果孩子的名字是空的,那么写的代码会很高兴地将空添加到你的孩子名字集中

这是一个猜测,但我怀疑Node.setChildren()是在Hibernate使用支持SQL查询的数据填充子对象之前被调用的。Hibernate有时只创建设置了@Id值的对象;如果您阅读了关于懒惰代理的文档,您应该会看到这方面的证据。我怀疑Hibernate已经创建了子节点对象,但是还没有用数据填充它们,因为它需要先填充父节点。基本上你的二传手在这个过程中发射得太早了

您可以通过调试代码并在setChildren()和setName()中添加断点来验证这一点。在上面的代码中没有显示setName()方法,但我假设您忽略了它。还要检查断点处的堆栈,以了解在调用setter时Hibernate正在做什么

一种解决方法是将setChildren()中的逻辑添加到getChildren()中,如下所示:

@Transient public Set<String> getChildrenNames() {
    if (children.size() != childrenNames.size() {
        childrenNames.removeAll();
        for(Node child : children) {
            childrenNames.add(child.getName());
        }
    }
    return childrenNames;
}
@Transient public Set getChildrenNames(){
if(childrenNames.size()!=childrenNames.size(){
childrenNames.removeAll();
用于(节点子节点:子节点){
添加(child.getName());
}
}
返回儿童姓名;
}

这只能执行一次,在调用getter时,应该填充子对象中的数据。或者,您可以将填充“childrenNames”的逻辑放入进入非getter/setter方法,并在查询后手动调用它。

子项的名称不为null。事实上,它们是稍后填充的。只有在setter时才返回null。如果调试此代码并在setChildren(…)内设置断点,则所有字段(可能除了@Id)在子节点上,对象设置为null?正确。除
@Id
之外的所有字段都是
null
。有关集合的类似问题,请参阅帮助。有效的方法是我切换到Hibernate的所有字段直接访问,并按照您的建议将逻辑移动到getter。现在可以工作了。