JDBI SQL对象查询中的动态顺序

scl*_*sen 12 java sql-order-by sqlobject jdbi

如何在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
);
Run Code Online (Sandbox Code Playgroud)

要么

@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
);
Run Code Online (Sandbox Code Playgroud)

krd*_*rdx 30

我最近一直在探索与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();

}
Run Code Online (Sandbox Code Playgroud)

@ UseStringTemplate3StatementLocator - 这个注释允许我们<arg>在查询中使用语法.这些参数将被我们通过@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>
Run Code Online (Sandbox Code Playgroud)

SQL INJECTION警告 应该注意,这会打开我们,Sql Injection因为值直接插入到查询中.(:arg与查询和@Bind注释中的语法相对应,后者使用预准备语句并防止sql注入).至少应该清理将用于@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);

  }
}
Run Code Online (Sandbox Code Playgroud)

  • 这一直让我发疯,特别是缺乏文件.@krdx你的答案是彻底的,非常有帮助,谢谢.如果可能的话,你能解释为什么`@Bind("foo")String foo`不会使用`:foo`将字符串替换成查询?或者如果您找到了涵盖此内容的文档? (4认同)
  • 这个答案也拯救了我的生命.`'org.antlr:stringtemplate:3.2.1''也适用于我,但最新的`4.0.2`却没有.请问为什么这个注释`@UseStringTemplate3StatementLocator`需要这样的依赖才能使它工作 (2认同)