2010-09-09 70 views
6

我处于我们公司具有高度可配置的数据库搜索服务的位置,对于此类服务,以编程方式配置查询非常有用。 Criteria API功能强大,但是当我们的开发人员重构其中一个数据对象时,条件限制不会表示它们在我们运行单元测试之前已经损坏,或者更糟糕的是,它们在我们的生产环境中正常运行。最近,由于这个问题,我们有一个重构项目出乎意料地在工作时间翻了一番,项目规划中存在一个缺口,如果我们知道需要多长时间,我们可能会采取另一种方法。如何克服Hibernate标准和示例API的局限性?

我想使用示例API来解决此问题。如果我们在真正的POJO属性中指定'where'条件,那么Java编译器可以大声表明我们的查询是borked的。但是,Example API中只有很多功能,并且在很多方面都受到限制。看看下面的例子

Product product = new Product(); 
product.setName("P%"); 
Example prdExample = Example.create(product); 
prdExample.excludeProperty("price"); 
prdExample.enableLike(); 
prdExample.ignoreCase(); 

在这里,“名称”属性被查询反对(其中名称像“P%”),如果我是删除或重命名字段“名”,我们会立即知道。但是,财产“价格”呢?它被排除了,因为Product对象有一些默认值,所以我们将“price”属性名称传递给排除过滤器。现在,如果“价格”被删除,这个查询在语法上是无效的,并且直到运行时才会知道。瘸。

另一个问题 - 如果我们添加了什么第二where子句:

product.setPromo("Discounts up to 10%"); 

因为调用enableLike()的,这个例子将匹配的宣传文字“折扣高达10%”,但也“折扣高达1000万美元”或其他任何匹配。通常,Example对象的查询范围修改(如enableLike()或ignoreCase())并不总是适用于每个正在检查的属性。

这是第三个主要问题 - 其他特殊标准呢?使用标准示例框架无法获得价格高于10美元的每件产品。无法通过促销,降序来订购结果。如果产品对象加入某个制造商,则无法在相关的制造商对象上添加条件。没有办法在制造商的标准上安全地指定FetchMode(虽然这是一般的Criteria API的问题 - 无效提取的关系失败,甚至更多的定时炸弹)

对于所有上述示例,您需要返回Criteria API并使用属性的字符串表示形式进行查询 - 同样,消除了示例查询的最大好处。

示例API还有什么替代方法可以获得我们需要的编译时建议?

+0

这是非常有趣的东西,但stackoverflow不是一个论坛,你不会在这里得到反馈,你的问题可能会被关闭在当前的形式。正如常见问题解答中所述,“只要您假装自己处于危险境地:提问并回答自己的问题也是完全正确的:以问题的形式表述它”*。我建议改写它,并将细节作为答案发布。 – 2010-09-10 17:53:08

+0

@Pascal谢谢,我会继续并重新编制问题 – 2010-09-10 18:46:25

+0

不客气。并感谢张贴这个。 – 2010-09-11 03:17:21

回答

5

我的公司为开发人员提供了可以试验和开展宠物项目(Google)的日子,并且花了一些时间研究框架以使用示例查询,同时解决了上述限制。我想出了一些可能对其他对示例查询感兴趣的人有用的东西。以下是使用产品示例的框架示例。

Criteria criteriaQuery = session.createCriteria(Product.class); 

Restrictions<Product> restrictions = Restrictions.create(Product.class); 
Product example = restrictions.getQueryObject(); 
example.setName(restrictions.like("N%")); 
example.setPromo("Discounts up to 10%"); 

restrictions.addRestrictions(criteriaQuery); 

下面是从问题解决中的代码示例中的问题的尝试 - 对“价格”字段不再存在默认值的问题,因为该框架要求的标准进行明确设置。具有查询范围enableLike()的第二个问题消失了 - 匹配器仅在“名称”字段中。

在这个框架中,问题中提到的其他问题也没有了。这里是示例实现。

product.setPrice(restrictions.gt(10)); // price > 10 
product.setPromo(restrictions.order(false)); // order by promo desc 
Restrictions<Manufacturer> manufacturerRestrictions 
     = Restrictions.create(Manufacturer.class); 
//configure manuf restrictions in the same manner... 
product.setManufacturer(restrictions.join(manufacturerRestrictions)); 
/* there are also joinSet() and joinList() methods 
    for one-to-many relationships as well */ 

更复杂的限制是可用的。

product.setPrice(restrictions.between(45,55)); 
product.setManufacturer(restrictions.fetch(FetchMode.JOIN)); 
product.setName(restrictions.or("Foo", "Bar")); 

展示给同事的框架后,他提到,许多数据映射对象有私人setter方法,使得这种基准设定困难以及(与实例API一个不同的问题!)。所以,我也解释了这一点。取而代之的是使用setter,getter也是可查询的。

restrictions.is(product.getName()).eq("Foo"); 
restrictions.is(product.getPrice()).gt(10); 
restrictions.is(product.getPromo()).order(false); 

我还添加了一些额外的检查的对象,以确保更好的类型安全性 - 例如,相对标准(GT,GE,LE,LT)都需要一个值?为参数扩展Comparable。另外,如果你在上面指定的样式中使用getter,并且getter上存在@Transient注释,它将引发运行时错误。

但是等等,还有更多!

如果您喜欢Hibernate的内置限制实用程序可以静态导入,以便您可以执行诸如criteria.addRestriction(eq(“name”,“foo”))而不会使代码真的变得冗长,那么选项也是如此。

Restrictions<Product> restrictions = new Restrictions<Product>(){ 
     public void query(Product queryObject){ 
     queryObject.setPrice(gt(10)); 
     queryObject.setPromo(order(false)); 
     //gt() and order() inherited from Restrictions 
     }    
} 

这就是现在 - 非常感谢您提前的任何反馈!我们已经在Sourceforge上发布了代码,供有兴趣的人使用。 http://sourceforge.net/projects/hqbe2/

+0

我喜欢它。看起来你受到各种嘲讽框架的启发? – waxwing 2010-09-12 20:20:38

+0

@waxwing是啊我爱EasyMock :) – 2010-09-12 20:23:40

+0

你会如何将限制与或?如“年龄大于18岁或(年龄> 16岁且父母同意)”的人“? – meriton 2010-09-12 21:22:49

1

该API看起来不错!

限制。秩(布尔)气味像控制耦合。有点不清楚布尔参数的值是什么。

我建议用orderAscending()和orderDescending()来代替或补充。

1

看看Querydsl。他们的JPA/Hibernate模块需要代码生成。他们的Java集合模块使用代理,但目前不能与JPA/Hibernate一起使用。