如果外键在键不可为空时引用空行,如何不崩溃?

时间:2016-01-06 04:57:29

标签: java mysql hibernate

我有这张表"地区":

id |名字| PARENT_ID

1 |不管什么100000

其中parent_id应该自我引用id,这意味着该行在地理上属于100000。

但是由于在开头导入的数据很脏,ID为100000的行不存在。

因此在给定的实体中:

@Entity("regions")
public class Region {
    private int id;
    private String name;
    private Region parent;

    ...

    @ManyToOne()
    @JoinColumn(name = "parent_id")
    public Region getParent() {
        return parent;
    }

    public void setParent(Region parent) {
        this.parent = parent;
    }
}

当我用hibernate做一个列表时:

    Session session = sessionHandler.getSession(); //gets current session
    Transaction tx = session.beginTransaction();
    try {
        return (List<T>)session.createQuery("FROM regions").list();
    }
    catch(HibernateException ex) {
        ex.printStackTrace();
        if (tx!=null) tx.rollback();
        throw ex;
    }finally {
        sessionHandler.close(); 
    }

它将抛出异常:

org.hibernate.ObjectNotFoundException: No row with the given identifier exists: [whatever.entities.Region#6046193]

这表示id 6046193的区域不存在。如前所述,我预计会发生这样的事情。

我的问题,因为我无法将parent_id列编辑为可空,这是一种处理此异常的方法,以便系统忽略异常并保持程序运行吗?

2 个答案:

答案 0 :(得分:1)

您可以尝试将多对一关系的提取类型设置为延迟。

...
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "parent_id")
public Region getParent() {
    return parent;
}
...

我猜hibernate在“全部选择”期间不会抛出错误,你可以在第一次调用getter时处理错误,如果这样做的话。

但我不确定这是否有效,我认为这不是一个非常好的解决方案。我真的认为你应该在导入期间/之后清理你的数据。

如果您没有清理数据,则必须在代码永远中为问题保留解决方法。如果将来有人删除了fetch = FetchType.LAZY,因为他们认为会带来更好的性能会怎样?您的应用程序将以意外的方式中断,因为您的实体无法正确反映数据库中的内容。

您说您无法将parent_id设置为null,因为该列不可为空。但是如何为缺少的ID创建虚拟条目呢?在第一次启动应用程序之前,您可以在导入脏数据后立即执行此操作。

此外,只需将列更改为可空(假设您可以暂时执行此操作)无论如何都无法正常工作。您仍然需要清理数据 - 在这种情况下,当具有引用ID的行不存在时,您必须将所有parent_id设置为null。

答案 1 :(得分:1)

我想说你的数据模型设计存在缺陷。

从关系角度来看,您使用echo $CustomerApprovedCount;来表示不可为空的自引用parent_id的外键。这意味着您在id中放置的任何值都应该具有匹配的行,并且具有相同的parent_id值。这应该通过插入您在帖子中引用的无效行自动创建外键约束违规。

如果该字段必须保留id,您可以创建一个标记行,并且您使用无效nullable=false引用加载的任何旧数据都可以更改为使用标记行parent_id数据模型有效。如果数据模型可以稍微改变,id可以保留遗留引用,并且您的代码可能具有基于标记行的不同逻辑路径。

我假设您可以稍微修改数据模型的另一个想法是考虑使用将遗留行与非遗留行分开的鉴别器。

在旧版模型中,您填充的legacy_parent_id只是一个整数。在非遗留模型中,您可以使legacy_parent_id外键验证关系不可为空。