【问题标题】:Dynamic Order in JDBI SQL Object QueriesJDBI SQL 对象查询中的动态顺序
【发布时间】:2016-10-24 20:57:50
【问题描述】:

如何在 JDBI 中使用 SQL 对象查询进行排序?

我想做这样的事情:

@SqlQuery(
    "SELECT * FROM users " +
    "WHERE something = :something " +
    "ORDER BY :orderBy :orderDir"
)
List<User> getUsers(
    @Bind("something") Integer something
  , @BindOrderBy("orderBy") String orderBy
  , @BindOrderDir("orderDir") String orderDir
);

@SqlQuery(
    "SELECT * FROM users " +
    "WHERE something = :something " +
    "ORDER BY :orderBy :orderDir"
)
List<User> getUsers(
    @Bind("something") Integer something
  , @Bind("orderBy") OrderBy orderBy
  , @Bind("orderDir") OrderDir orderDir
);

【问题讨论】:

    标签: java sql-order-by sqlobject jdbi


    【解决方案1】:

    我最近一直在探索与 JDBI 捆绑在一起的 DropWizard,并很快遇到了同样的问题。不幸的是,JDBI 的文档乏善可陈(JavaDoc 和它的 git 存储库上的一些示例单元测试并没有单独进行),这令人失望。

    这是我发现的基于我的示例 DAO 在 JDBI 的 Sql 对象 API 中实现动态顺序的内容:

    @UseStringTemplate3StatementLocator
    public interface ProductsDao {
    
        @RegisterMapperFactory(BeanMapperFactory.class) // will map the result of the query to a list of Product POJOs(Beans)
        @SqlQuery("select * from products order by <orderby> <order> limit :limit offset :offset")
        List<Product> getProducts(@Define("orderby") String orderBy, @Define("order") String order,
                                         @Bind("limit") int limit, @Bind("offset") int offset);
    
        @SqlQuery("select count(*) from products")
        int getProductsCount();
    
    }
    

    @UseStringTemplate3StatementLocator - 这个注解允许我们在查询中使用&lt;arg&gt; 语法。这些 args 将被我们通过 @Define 注释提供的任何值替换。

    为了能够使用此功能,我必须另外将此依赖项添加到我的 pom.xml 文件中:

    <dependency>
      <groupId>antlr</groupId>
      <artifactId>stringtemplate</artifactId>
      <version>2.3b6</version> <!-- I am not sure if this specific version is meant to be used though -->
    </dependency>
    

    SQL 注入警告 应该注意的是,这会打开Sql Injection,因为这些值是直接插入到查询中的。 (与查询中的:arg 语法和使用准备好的语句并防止sql 注入的@Bind 注释相反)。至少您应该清理将用于@Define 字段的参数。 (下面是 DropWizard 的简单示例)。

    @Path("/products")
    @Produces(MediaType.APPLICATION_JSON)
    public class ProductsResource {
      private static ImmutableSet<String> orderByChoices = ImmutableSet.of("id", "name", "price", "manufactureDate");
    
      private final ProductsDao dao;
    
      public ProductsResource(ProductsDao dao) {
        this.dao = dao;
      }
    
      @GET
      // Use @InjectParam to bind many query parameters to a POJO(Bean) instead. 
      // https://jersey.java.net/apidocs/1.17/jersey/com/sun/jersey/api/core/InjectParam.html
      // i.e. public List<Product> index(@InjectParam ProductsRequest request)
      // Also use custom Java types for consuming request parameters. This allows to move such validation/sanitization logic outside the 'index' method.
      // https://jersey.java.net/documentation/1.17/jax-rs.html#d4e260 
      public List<Product> index(@DefaultValue("id")  @QueryParam("orderby") String orderBy,
                                 @DefaultValue("asc") @QueryParam("order")   String order,
                                 @DefaultValue("20")  @QueryParam("perpage") IntParam perpage,
                                 @DefaultValue("0")   @QueryParam("page")    IntParam page)
    
       int limit, offset;
    
       order = order.toLowerCase(); 
       orderBy = orderBy.toLowerCase();    
    
       if (!orderByChoices.contains(orderBy)) orderBy = "id"; //sanitize <orderby>
       if (order != "asc" && order != "desc") order = "asc";  //sanitize <order>
    
       limit = perpage.get();
       offset = page.get() < 0 ? 0 : page.get() * limit;
    
       return dao.getProducts(orderBy, order, limit, offset);
    
      }
    }
    

    【讨论】:

    • 这让我整晚都发疯了,尤其是缺乏文档。 @krdx 您的回答非常详尽,非常有帮助,谢谢。如果可能,您能解释一下为什么 @Bind("foo") String foo 不会使用 :foo 将字符串替换到查询中吗?或者您是否找到了涵盖此内容的文档?
    • 这个答案也救了我的命。 'org.antlr:stringtemplate:3.2.1' 也适用于我,但最新的 4.0.2 不适用。请问为什么这个注解@UseStringTemplate3StatementLocator需要这样的依赖才能让它工作
    • 如果我的 orderby、order、limit、offset 来自一个对象(比如Page)怎么办。例如:dao.getProducts(Page page)。在这种情况下我该如何@Define他们?
    【解决方案2】:

    我认为这是因为假设提供了字符串模板库,并且该假设在运行时失败。将以下内容添加到应用程序 POM 应该可以解决问题:

    <dependency>
      <groupId>org.antlr</groupId>
      <artifactId>stringtemplate</artifactId>
      <version>3.2.1</version>
    </dependency>
    

    通过查看 JDBI 2 pom,您可以看到以下内容:

    <dependency>
      <groupId>org.antlr</groupId>
      <artifactId>stringtemplate</artifactId>
      <version>3.2.1</version>
      <optional>true</optional>
    </dependency>
    

    意思是JDBI不会抱怨缺少stringtemplate lib。

    【讨论】:

      【解决方案3】:

      事实证明,您将 ORDER BY 添加到查询中

      @SqlQuery("SELECT * FROM incident_events WHERE incident_id=:incidentId ORDER BY event_time DESC LIMIT :limit OFFSET :offset")
      List<IncidentEvent> getPaginated(@Bind("incidentId") int incidentId, @Bind("limit") int limit, @Bind("offset") int offset);
      

      【讨论】:

      • 我认为这是关于您订购的字段的动态选择
      猜你喜欢
      • 2013-09-09
      • 1970-01-01
      • 1970-01-01
      • 2021-10-11
      • 1970-01-01
      • 2019-08-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多