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

Ant*_*ric 6 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();
Run Code Online (Sandbox Code Playgroud)

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

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

 product.setPromo("Discounts up to 10%");
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

Ant*_*ric 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);
Run Code Online (Sandbox Code Playgroud)

这是尝试从问题中解决代码示例中的问题 - "价格"字段的默认值问题不再存在,因为此框架要求明确设置标准.使查询范围的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 */
Run Code Online (Sandbox Code Playgroud)

可以使用更复杂的限制.

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

在向同事展示框架后,他提到许多数据映射对象都有私有设置器,这使得这种标准设置也很困难(与Example API不同的问题!).所以,我也考虑到了这一点.getter也可以查询,而不是使用setter.

 restrictions.is(product.getName()).eq("Foo");
 restrictions.is(product.getPrice()).gt(10);
 restrictions.is(product.getPromo()).order(false);
Run Code Online (Sandbox Code Playgroud)

我还在对象上添加了一些额外的检查,以确保更好的类型安全性 - 例如,相对标准(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
       }            
 }
Run Code Online (Sandbox Code Playgroud)

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