3

我有一个实体 ( Layer),它映射了其他实体 ( Member) 的列表。此列表可能没有条目 / 是null。然而,当我查询实体时,我NOT NULL check constraint从数据库中得到一个错误。它似乎已连接到 ,NamedQueries因为如果我通过id.

@Entity
@NamedQueries({
    @NamedQuery(name="getChildLayers",-
                query = "SELECT la
                         FROM Layer la
                         WHERE la.parent = :parent AND la.deletedDate IS NULL")})
public class Layer extends CommonModel {
    /*... other field */
    @ManyToOne(fetch = FetchType.LAZY, targetEntity = Layer.class, optional = true)
    private Layer parent;

    @ManyToMany(fetch = FetchType.LAZY, targetEntity = MyUser.class)
    private List<MyUser> members;
    public List<MyUser> getMembers() {
        return members;
    }
    public void setMembers(List<MyUser> members) {
        this.members = members;
    }
    /*... other getters and setters */
}

我收到此错误:integrity constraint violation: NOT NULL check constraint; SYS_CT_10298 table: LAYER_MYUSER column: MEMBERS_ID

不过,我可以创建条目。

当我运行我的测试时,所有读取实体的测试都失败(但创建工作)。如果我在创建方法中添加以下行:

  layer.setMembers(new ArrayList<MyUser>());

然后测试成员交替的方法起作用(意思是,我可以通过在列表中添加和删除元素来创建Layer和更改它)。members

在我看来,只要Member没有Layer.

我确实尝试添加@JoinColumn(nullable=true)到该字段,但它没有改变。

我导入javax.persistence课程。

关于如何访问变量的示例(在 中LayerService

// this method works as expected
public Layer getById(Long id) {
    Session s = sessionFactory.getCurrentSession();
    return (Layer)s.get(Layer.class, id);
}

// this does not.
public List<Layer> getChildren(Layer layer) {
    Query childrenQuery = sessionFactory.getCurrentSession().getNamedQuery("getChildLayers");
    childrenQuery.setParameter("parent", layer);

    return (List<Layer>) childrenQuery.list();
}

Jason Cs 回答后代码更改:

Layer

...
private final List<OCWUser> members = new ArrayList<>();
...
public void setMembers(List<OCWUser> members) {
    this.members.clear();
    this.members.addAll(members);
}

问题依然存在。

4

2 回答 2

4

可以这么简单。我忘了添加@JoinTable

@JoinTable(name = "LAYER_USER", joinColumns = @JoinColumn(nullable = true))
于 2013-11-05T13:26:25.280 回答
2

需要注意的一件重要事情是,除非您在致电之前知道您正在这样做,否则您不应该this.members用另一个列表替换。相反,您需要清除然后将所有指定的元素添加到它。原因是 Hibernate 在 [反] 序列化实体时可以并且将使用其自己的代理/检测的集合类,而在覆盖集合类时你会把它吹走。您应该声明为并始终将其初始化为非空 empty 。setMemberspersist()this.membersmembersfinalList

参见例如(3.6但仍然相关):http ://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/collections.html#collections-persistent ,特别是:

请注意示例 7.2,“使用 @OneToMany 和 @JoinColumn 的集合映射”中,实例变量部分是如何使用 HashSet 实例初始化的。这是初始化新实例化(非持久)实例的集合值属性的最佳方式。当你使实例持久化时,通过调用persist(),Hibernate 实际上会用Hibernate 自己的Set 实现的实例替换HashSet。

只要你以这种方式搞乱集合字段,任何数量的奇怪事情都可能发生。


此外,一般来说,在以这种方式访问​​集合时,您要小心声明不变量等,因为很容易创建两个Layers在内部引用同一集合的集合,以便对一个的操作影响另一个,或者传入集合上的外部操作会影响层,例如,以下代码的行为可能不像您希望的那样:

List<MyUser> u = new ArrayList<MyUser>();
Layer a = new Layer();
Layer b = new Layer();

u.add(...);
a.setMembers(u);
b.setMembers(u);
u.clear();

此外,当您persist()在那里的其中一个层,并且 Hibernate 用它自己的集合类覆盖该字段时,行为会随着对象不再引用同一个集合而改变:

// not only did u.clear() [possibly undesirably] affect a and b above, but:
session.persist(a);
u.add(...); // ... now it only affects b.
于 2013-11-05T12:46:12.837 回答