JaQu

# JaQu What is JaQu Differences to Other Data Access Tools Current State Building the JaQu Library Requirements Example Code Configuration Natural Syntax Other Ideas Similar Projects ## What is JaQu Note: This project is currently in maintenance mode. A friendly fork of JaQu is available under the name iciql. JaQu stands for Java Query and allows to access databases using pure Java. JaQu provides a fluent interface (or internal DSL). JaQu is something like LINQ for Java (LINQ stands for "language integrated query" and is a Microsoft .NET technology). The following JaQu code:
    Product p = new Product();
    List<Product> soldOutProducts =
        db.from(p).where(p.unitsInStock).is(0).select();
stands for the SQL statement:
    SELECT * FROM PRODUCTS P
    WHERE P.UNITS_IN_STOCK = 0
## Differences to Other Data Access Tools Unlike SQL, JaQu can be easily integrated in Java applications. Because JaQu is pure Java, auto-complete in the IDE is supported. Type checking is performed by the compiler. JaQu fully protects against SQL injection. JaQu is meant as replacement for JDBC and SQL and not as much as a replacement for tools like Hibernate. With JaQu, you don't write SQL statements as strings. JaQu is much smaller and simpler than other persistence frameworks such as Hibernate, but it also does not provide all the features of those. Unlike iBatis and Hibernate, no XML or annotation based configuration is required; instead the configuration (if required at all) is done in pure Java, within the application. JaQu does not require or contain any data caching mechanism. Like JDBC and iBatis, JaQu provides full control over when and what SQL statements are executed (but without having to write SQL statements as strings). ### Restrictions Primitive types (eg. `boolean, int, long, double`) are not supported. Use `java.lang.Boolean, Integer, Long, Double` instead. ### Why in Java? Most applications are written in Java. Mixing Java and another language (for example Scala or Groovy) in the same application is complicated: you would need to split the application and database code, and write adapter / wrapper code. ## Current State Currently, JaQu is only tested with the H2 database. The API may change in future versions. JaQu is not part of the h2 jar file, however the source code is included in H2, under: `src/test/org/h2/test/jaqu/*` (samples and tests) `src/tools/org/h2/jaqu/*` (framework) ## Building the JaQu Library To create the JaQu jar file, run: `build jarJaqu`. This will create the file `bin/h2jaqu.jar`. ## Requirements JaQu requires Java 6. Annotations are not need. Currently, JaQu is only tested with the H2 database engine, however in theory it should work with any database that supports the JDBC API. ## Example Code
    package org.h2.test.jaqu;
    import java.math.BigDecimal;
    import java.util.List;
    import org.h2.jaqu.Db;
    import static org.h2.jaqu.Function.*;

    public class Test {
        Db db;

        public static void main(String[] args) throws Exception {
            new SamplesTest().test();
        }

        public void test() throws Exception {
            db = Db.open("jdbc:h2:mem:", "sa", "sa");
            db.insertAll(Product.getProductList());
            db.insertAll(Customer.getCustomerList());
            db.insertAll(Order.getOrderList());
            testLength();
            testCount();
            testGroup();
            testSelectManyCompoundFrom2();
            testWhereSimple4();
            testSelectSimple2();
            testAnonymousTypes3();
            testWhereSimple2();
            testWhereSimple3();
            db.close();
        }

        private void testWhereSimple2() throws Exception {
            Product p = new Product();
            List<Product> soldOutProducts =
                db.from(p).
                where(p.unitsInStock).is(0).
                orderBy(p.productId).select();
        }

        private void testWhereSimple3() throws Exception {
            Product p = new Product();
            List<Product> expensiveInStockProducts =
                db.from(p).
                where(p.unitsInStock).bigger(0).
                and(p.unitPrice).bigger(3.0).
                orderBy(p.productId).select();
        }

        private void testWhereSimple4() throws Exception {
            Customer c = new Customer();
            List<Customer> waCustomers =
                db.from(c).
                where(c.region).is("WA").
                select();
        }

        private void testSelectSimple2() throws Exception {
            Product p = new Product();
            List<String> productNames =
                db.from(p).
                orderBy(p.productId).select(p.productName);
        }

        public static class ProductPrice {
            public String productName;
            public String category;
            public Double price;
        }

        private void testAnonymousTypes3() throws Exception {
            final Product p = new Product();
            List<ProductPrice> productInfos =
                db.from(p).orderBy(p.productId).
                select(new ProductPrice() {{
                        productName = p.productName;
                        category = p.category;
                        price = p.unitPrice;
                }});
        }

        public static class CustOrder {
            public String customerId;
            public Integer orderId;
            public BigDecimal total;
        }

        private void testSelectManyCompoundFrom2() throws Exception {
            final Customer c = new Customer();
            final Order o = new Order();
            List<CustOrder> orders =
                db.from(c).
                innerJoin(o).on(c.customerId).is(o.customerId).
                where(o.total).smaller(new BigDecimal("500.00")).
                orderBy(1).
                select(new CustOrder() {{
                    customerId = c.customerId;
                    orderId = o.orderId;
                    total = o.total;
                }});
        }

        private void testLength() throws Exception {
            Product p = new Product();
            List<Integer> lengths =
                db.from(p).
                where(length(p.productName)).smaller(10).
                orderBy(1).
                selectDistinct(length(p.productName));
        }

        private void testCount() throws Exception {
            long count = db.from(new Product()).selectCount();
        }

        public static class ProductGroup {
            public String category;
            public Long productCount;
        }

        private void testGroup() throws Exception {
            final Product p = new Product();
            List<ProductGroup> list =
                db.from(p).
                groupBy(p.category).
                orderBy(1).
                select(new ProductGroup() {{
                    category = p.category;
                    productCount = count();
                }});
        }

    }
## Configuration JaQu does not require any configuration when using the default field to column mapping. To define table indices, or if you want to map a class to a table with a different name, or a field to a column with another name, create a function called `define` in the data class. Example:
    import static org.h2.jaqu.Define.*;

    public class Product implements Table {

        public Integer productId;
        public String productName;
        public String category;
        public Double unitPrice;
        public Integer unitsInStock;

        public void define() {
            tableName("Product");
            primaryKey(productId);
            index(productName, category);
        }

    }
The method `define()` contains the mapping definition. It is called once when the class is used for the first time. Like annotations, the mapping is defined in the class itself. Unlike when using annotations, the compiler can check the syntax even for multi-column objects (multi-column indexes, multi-column primary keys and so on). Because the definition is written in Java, the configuration can be set at runtime, which is not possible using annotations. Unlike XML mapping configuration, the configuration is integrated in the class itself. ## Natural Syntax The plan is to support more natural (pure Java) syntax in conditions. To do that, the condition class is de-compiled to a SQL condition. A proof of concept decompiler is included (but it doesn't fully work yet; patches are welcome). The planned syntax is:
    long count = db.from(co).
        where(new Filter() { public boolean where() {
            return co.id == x
                && co.name.equals(name)
                && co.value == new BigDecimal("1")
                && co.amount == 1L
                && co.birthday.before(new java.util.Date())
                && co.created.before(java.sql.Timestamp.valueOf("2005-05-05 05:05:05"))
                && co.time.before(java.sql.Time.valueOf("23:23:23"));
            } }).selectCount();
## Other Ideas This project has just been started, and nothing is fixed yet. Some ideas are: Support queries on collections (instead of using a database). Provide API level compatibility with JPA (so that JaQu can be used as an extension of JPA). Internally use a JPA implementation (for example Hibernate) instead of SQL directly. Use PreparedStatements and cache them. ## Similar Projects iciql (a friendly fork of JaQu) Cement Framework Dreamsource ORM Empire-db JEQUEL: Java Embedded QUEry Language Joist jOOQ JoSQL LIQUidFORM Quaere (Alias implementation) Quaere Querydsl Squill