使用命名查询时,ManyToMany NOT NULL检查约束

时间:2013-11-05 12:35:51

标签: java hibernate jpa many-to-many hsqldb

我有一个实体(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);
}

问题仍然存在。

2 个答案:

答案 0 :(得分:4)

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

@JoinTable(name = "LAYER_USER", joinColumns = @JoinColumn(nullable = true))

答案 1 :(得分:2)

要注意的一件重要事情是,除非您在致电this.members之前知道自己正在执行此操作,否则不应将setMembers替换为persist()中的其他列表。相反,您需要清除this.members,然后将所有指定的元素添加到其中。原因是Hibernate可以并且将在[de]序列化实体时使用自己的代理/检测集合类,并在覆盖集合类时将其吹掉。您应将members声明为final并始终将其初始化为非空的空List

参见例如(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自己的实例替换HashSet   Set的实现。

只要您以这种方式搞乱收集字段,就会发生任何奇怪的事情。

<小时/> 此外,一般情况下,您希望在以这种方式访问​​集合时要小心说明不变量,因为很容易,例如,创建两个内部引用相同集合的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.