如何克服Hibernate Criteria和Example API的限制?

时间:2010-09-09 19:44:12

标签: java hibernate upgrade hibernate-criteria

我所处的位置是我们公司拥有高度可配置的数据库搜索服务,因此以编程方式配置查询非常有用。 Criteria API功能强大,但当我们的某个开发人员重构其中一个数据对象时,标准限制并不表示它们在我们运行单元测试之前就已被破坏,或者更糟糕的是,它们是在我们的生产环境中运行的。最近,由于这个问题,我们的重构项目在工作时间上意外地翻了一倍,项目规划存在差距,如果我们知道它需要多长时间,我们可能会采取另一种方法。

我想使用Example API来解决这个问题。如果我们在实际POJO属性上指定“where”条件,Java编译器可以大声地指示我们的查询是不可靠的。但是,Example API中只有这么多功能,并且它在很多方面都有限制。采取以下示例

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

这里,正在查询属性“name”(其中名称如'P%'),如果我要删除或重命名字段“name”,我们会立即知道。但物业“价格”怎么样?它被排除,因为Product对象有一些默认值,所以我们将“price”属性名称传递给排除过滤器。现在,如果“价格”被删除,这个查询将在语法上无效,直到运行时才会知道。跛。

另一个问题 - 如果我们添加第二个where子句会怎么样:

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

由于调用了enableLike(),此示例将匹配促销文本“折扣高达10%”,还有“折扣高达10,000,000美元”或其他任何匹配的内容。通常,Example对象的查询范围修改(例如enableLike()或ignoreCase()并不总是适用于要检查的每个属性。

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

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

Example API有哪些替代方法可以获得我们需要的那种编译时建议?

3 个答案:

答案 0 :(得分:5)

我的公司为开发人员提供了可以在宠物项目上进行实验和工作的日子(Google),我花了一些时间研究框架,以便在解决上述限制时使用Example查询。我想出了一些可能对其他对Example查询感兴趣的人有用的东西。以下是使用Product示例的框架示例。

 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"));

在向同事展示框架之后,他提到许多数据映射对象都有私有设置器,这使得这种标准设置也很困难(与Example 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)都需要一个值? extends参数的Comparable。此外,如果您使用上面指定的样式的getter,并且getter上存在@Transient注释,则会引发运行时错误。

但等等,还有更多!

如果您喜欢Hibernate的内置Restrictions实用程序可以静态导入,那么您可以执行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/

答案 1 :(得分:1)

API看起来很棒!

Restrictions.order(boolean)闻起来像控制耦合。有点不清楚布尔参数的值代表什么。

我建议替换或补充orderAscending()和orderDescending()。

答案 2 :(得分:1)

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