Java 通过服务API公开Hibernate标准

Java 通过服务API公开Hibernate标准,java,hibernate,Java,Hibernate,这更多的是一个设计问题,而不是实现问题,这将是一个漫长的过程,所以请容忍我。最好用一个例子来解释: 假设我有一个名为Product的业务实体,它有一系列属性(名称、价格、供应商等)。 它由接口(产品)和实现(ProductImpl,在Hibernate中映射)以及基本CRUD服务接口(ProductService)和实现(ProductServiceImpl)表示。 产品和产品服务是作为API公开的,它们的实现不是。 我想向ProductService添加一个List findPro

这更多的是一个设计问题,而不是实现问题,这将是一个漫长的过程,所以请容忍我。最好用一个例子来解释:

假设我有一个名为Product的业务实体,它有一系列属性(名称、价格、供应商等)。

它由接口(产品)和实现(ProductImpl,在Hibernate中映射)以及基本CRUD服务接口(ProductService)和实现(ProductServiceImpl)表示。
产品和产品服务是作为API公开的,它们的实现不是。

我想向ProductService添加一个List findProducts(QueryCriteria criteria criteria)方法,该方法将返回满足给定条件的产品列表。 这些要求是:

  • 按直接产品属性查询(例如
    Product.price gt 50.0
  • 按关联查询(例如,
    product.vendor.name=“Oracle”
  • 排序结果(例如,
    按product.vendor.name desc、product.price asc排序“
  • 应用附加过滤器。与API客户端指定的上述3项不同,服务可基于客户端身份应用附加过滤器(例如,调用此方法的客户端可能仅限于查看给定供应商生产的产品)。此类过滤器优先于客户端指定的任何标准(例如,如果过滤器设置为
    product.vendor.name=“Microsoft”
    ,则上述(2)中的查询应生成空结果集
  • 因此,问题是这样一个方法使用的QueryCriteria接口应该是什么样子?我可以想到3种解决方案,但我不喜欢其中任何一种:

    • 允许客户端直接指定HQL(从“where”子句开始)。 这是最简单的解决方案,但在安全方面也是最有问题的。即使假设过滤器(#4)足够简单,可以通过Hibernate的会话过滤器实现,HQL仍然需要解析,以确保查询参数被指定为参数,而不是内联的
    • 使用包装很薄的Hibernate的DetachedCriteria代替QueryCriteria。 “精简包装”,因为不能允许客户机直接创建DetachedCriteria,因为无法控制为哪个映射实体创建它。 此外,这也不如HQL灵活,因为有些查询不容易(或根本不可能)通过Criteria API表达。与HQL方法一样,筛选器(#4)将仅限于Hibernate会话筛选器
    • 编写我自己的QueryCriteria接口/实现,在幕后形成DetachedCriteria或HQL。 虽然这可能是最灵活的解决方案,但它必须从CriteriaAPI复制大量代码,这似乎不太理想
    如果您对上述方法的有效性有任何评论,或者对我没有想到的简单优雅的解决方案表示祝贺,我们将不胜感激


    另外,在我的具体案例中,所有API客户机都是内部的且“半可信的”——也就是说,我不太担心有人试图故意破坏某些东西,而是担心糟糕的编程导致5个表的笛卡尔乘积:-)不过,最好能想出一个解决方案,使API能够向公众公开。

    公开这样的实现细节从来都不是一个好主意。从那时起,您就被绑定到该库。更糟糕的是,库的任何API更改都会导致服务的API i更改。任何遗留的安全注意事项

    critera中使用的bean属性名(属性名、enum with less、equal和more以及value的三元组)怎么样?在模型上使用bean包装器,您可以将其转换为hibernate标准


    模型更改后,还可以将此属性名称转换为新版本。

    Hmm-有趣的问题

    仔细考虑后,编写您自己的标准接口可能是一个不错的选择。它不会将您与实现捆绑在一起,并且会降低安全性问题

    根据涉及的对象数量,我们还考虑返回整套产品(应用必要的过滤器),然后让最终用户应用带有lambdaj或类似过滤器。请参阅:


    Hibernate是一个低级基础架构框架,因此应该隐藏在幕后。如果下个月您的应用程序出于某种原因需要切换到另一个ORM框架,那么您的API将毫无用处。即使在同一应用程序中的层之间进行封装,也是至关重要的

    说到这里,我认为您的方法应该接收执行搜索所需信息的抽象。我建议您创建
    产品
    字段的枚举,并实现一个或两个简单的限制版本

    该方法的参数可以是相等限制列表、另一个相对限制列表,当然还有一个顺序指示符(枚举值之一加上asc/desc的标志)


    这只是一个大致的方向,我希望我能把我的观点讲清楚 如果有可能扩展您的API,我建议让您的API“更丰富”——添加更多的方法,如下面的一些方法,使您的服务听起来更自然。要使您的API更大而不显得臃肿,这可能会很棘手,但如果您遵循类似的命名方案,使用它似乎会很自然

    productService.findProductsByName("Widget")
    productService.findProductsByName(STARTS_WITH,"Widg")
    productService.findProductsByVendorName("Oracle")
    productService.findProductsByPrice(OVER,50)
    
    通过使用CollectionUtils和谓词,组合结果(应用多个限制)可以留待客户端在收到结果集后执行。您甚至可以为API的使用者构建一些通用谓词,只是为了更好。CollectionUtils.select()很有趣

    选项二:如果无法扩展API,那么我会选择第三个项目

    • 编写我自己的QueryCriteria接口/实现,它将形成
      Product.Restriction restriction = new Product.Restriction().name("Widget").vendor("Oracle").price(OVER,50) );
      productService.findProducts(restriction);
      
      public List<Entity> findEntities(String queryName, QueryParameters parameters);
      
      List<Product> products = findProducts("latestUpdates",
       new QueryParameters()
        .add("vendor", "Oracle")
        .add("price", "50.0")
      );
      
      List<Product> products = findProducts("latestUpdates",
       new QueryParameters(product, "vendor", "price"));