检查hibernate映射类中的不变量

时间:2009-10-26 11:58:49

标签: java hibernate orm

使用hibernate的一个挑战是,manged类必须有一个默认构造函数。问题是没有明确的点来初始化类并且可以检查不变量。

如果一个类具有依赖于多个属性的不变量,则类设计变得复杂。让我们从假设的绿地设计开始:

public class A { 
    private int x; 
    private int y; 

    public A(int x, int y) { 
        this.x = x; 
        this.y = y; 
        checkInvariants(this.x, this.y); 
    } 

    private void checkInvariants(int x, int y) { 
        if (x + y « 0) throw new IllegalArgumentException(); 
    } 
}

这是不符合hibernate要求的基本实现。在构造函数中检查不变量。 (checkInvariants()方法的内容无关紧要,它只是为了说明类不变量可以依赖于多一个属性。)

该课程可按如下方式使用:

new A(0, 0); 
new A(-1, 0); //invalid 

要满足休眠要求,一种解决方法是添加私有默认构造函数使用字段访问。 (我省略了hibernate映射。)

public class H { 
    int x; 
    int y; 

    public H(int x, int y) { 
        this.x = x; 
        this.y = y; 
        checkInvariants(this.x, this.y); 
    } 

    H(){} 

    private void checkInvariants(int x, int y) { 
        if (x + y « 0) throw new IllegalArgumentException(); 
    } 
} 

这有两个主要缺点:  *您开始实现依赖于客户端(Hibernate)的代码。理想情况下,一个班级不知道其来电者。  *此解决方法的一个特定问题是,如果符合不变量,则hibernate启动的实例未检查。您信任从数据库加载的数据是有问题的。即使您的应用程序是唯一使用此特定数据库模式的应用程序,管理员也可能会进行临时更改。

第二种解决方法是检查用户代码中的不变量

public class I { 
    private int x; 
    private int y; 

    public I() {} 

    public void checkInvariants() { 
        if (x + y « 0) throw new IllegalArgumentException(); 
    } 

    public void setX(int x){ 
        this.x = x; 
    } 

    public void setY(int y){ 
        this.y = y; 
    } 
} 

I i = new I(); 
i.setX(-1); 
i.setY(0); 
i.checkInvariants(); 

显然,这会使用户代码更复杂容易出错。此设计无法满足实例在创建后保持一致并且在每次状态更改(方法调用)后保持一致的期望。每个用户都必须检查他创建的每个实例的不变量(可能是间接使用hibernate)。

这个问题是否有更好的解决方案:

  • 不是太复杂
  • 没有明确了解其用户
  • 没有依赖hibernate框架?

我认为必须放松一些约束才能找到务实的解决方案。唯一的硬约束是对hibernate框架没有依赖性。 (域对象之外的Hibernate特定代码是可以的)。

(出于好奇:是否有支持“构造函数注入”的ORM框架?)

3 个答案:

答案 0 :(得分:5)

答案 1 :(得分:3)

  

关系模型的连贯性   是至关重要的概念   喘气。因为固有的   数学稳定性   作为关系数据基础的原则   建模,我们可以完全自信   那个查询我们的结果   原始数据库确实会生成   真实有效的事实。 - 来自“The Art of Sql”一书 - Stephane Faroult

您的数据库应包含有效的事实或事实,您从数据库加载的数据不需要任何额外的验证,您从数据库中取出的数据应该足够好。

但如果您担心数据不好,那么我会建议存在更严重的问题。

我见过的解决方案包括一个临时数据库,其中所有数据都被清理,在进入真实数据库之前进行验证,以便在输入之前检查任何手动修复。

无论哪种方式,一旦您的数据库中的数据或虚假陈述损坏,您还可以信任什么?

答案 2 :(得分:2)

一种方法将基于您的“类I”,但是类本身第一次实际使用字段时调用checkInvariants()。这允许字段在赋值期间暂时无效(在调用setX()之后但在例如setY()之前)。但它仍然保证只使用有效数据。

您的示例类从不使用这些值,因此我假设可以通过getX(),getY()和doSomething()来访问值,例如:

public int getX(){ 
    return x; 
} 

public int getY(){ 
    return y; 
} 

public void doSomething(){
    x = x + y;
}

你会改变它:

private boolean validated = false;

public int getX(){ 
    if (!validated) {
        checkInvariants();
    }

    return x; 
} 

public int getY(){ 
    if (!validated) {
        checkInvariants();
    }
    return y; 
} 

public void doSomething(){
    if (!validated) {
        checkInvariants();
    }
    x = x + y;
}

public void checkInvariants() { 
    validated = true;
    if (x + y « 0) throw new IllegalArgumentException(); 
}

如果需要,您可以将'if(!validated)'移动到checkInvariants()中。

您可能也会查看Hibernate Validator,但它可能无法满足您的框架独立性要求。 http://docs.huihoo.com/hibernate/annotations-reference-3.2.1/validator.html