2013-05-28 30 views
12

Làm thế nào để bạn đặt hàng với các truy vấn đối tượng SQL trong JDBI?Thứ tự động trong các truy vấn đối tượng SQL JDBI

tôi muốn làm một cái gì đó như:

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

hoặc

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

Trả lời

26

Tôi vừa mới được khám phá DropWizard mà đi kèm với JDBI và nhanh chóng đi qua cùng một vấn đề. Thật không may JDBI có tài liệu mờ nhạt (JavaDoc và một số xét nghiệm đơn vị mẫu trên kho git của nó không cắt nó một mình) mà là đáng thất vọng.

Đây là những gì tôi thấy rằng đạt được một trật tự động trong một API Object Sql cho JDBI dựa trên DAO mẫu của tôi:

@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 - chú thích đây là những gì cho phép chúng ta sử dụng cú pháp <arg> trong các truy vấn. Những arg này sẽ được thay thế bằng bất kỳ giá trị nào chúng tôi cung cấp thông qua chú thích @Define.

Để có thể sử dụng tính năng này, tôi đã có thêm bổ sung phụ thuộc này để pom.xml tập tin của tôi:

<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 INJECTION CẢNH BÁO Cần lưu ý rằng điều này sẽ mở ra chúng tôi lên đến Sql Injection kể từ khi giá trị được chèn trực tiếp vào truy vấn. (Ngược lại với cú pháp :arg trong truy vấn và chú thích @Bind sử dụng câu lệnh đã chuẩn bị và bảo vệ chống lại tiêm sql). Ít nhất bạn nên khử trùng các thông số sẽ được sử dụng cho các trường @Define. (Ví dụ đơn giản cho DropWizard bên dưới).

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

    } 
} 
+4

Điều này đã khiến tôi phát điên cả đêm, đặc biệt là thiếu tài liệu. @ krdx câu trả lời của bạn là kỹ lưỡng và cực kỳ hữu ích, cảm ơn bạn. nếu có thể, bạn có thể giải thích tại sao '@Bind (" foo ") Chuỗi foo' sẽ không thay thế chuỗi vào truy vấn bằng cách sử dụng': foo'? hoặc nếu bạn đã tìm thấy tài liệu bao gồm điều này? –

+2

Câu trả lời này cũng cứu mạng tôi. ''org.antlr: stringtemplate: 3.2.1'' cũng làm việc cho tôi nhưng phiên bản mới nhất' 4.0.2' thì không. Tôi có thể hỏi lý do tại sao chú giải này '@ UseStringTemplate3StatementLocator' cần sự phụ thuộc như vậy để làm cho nó hoạt động được không? – DerekY

-1

tốt nó quay ra rằng bạn thêm ORDER BY để truy vấn của bạn như vậy

@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); 
+0

Tôi nghĩ đó là về sự lựa chọn năng động của trường mà bạn đang đặt hàng – Bernhard

2

Tôi nghĩ nó vì thư viện Chuỗi Template được giả định được cung cấp và giả định rằng thất bại khi chạy. Thêm sau đây để áp dụng POM nên khắc phục vấn đề:

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

Bằng cách nhìn vào JDBI 2 pom, bạn có thể thấy như sau:

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

Nghĩa JDBI sẽ không phàn nàn về sự vắng mặt của lib StringTemplate.

Các vấn đề liên quan