My favorites | Sign in
Project Home Downloads Wiki Issues Source
READ-ONLY: This project has been archived. For more information see this post.
Project Information
Members
Featured
Downloads
Wiki pages
Links

LIQUidFORM stands for Language Integrated QUeries For Object Relational Mapping and is a Java library that provides a Java Domain Specific Language for building type-safe and refactoring proof JPA queries.

The project draws its inspiration from the LINQ project, but its aim is only to help in writting JPA queries against your domain model. See the Scope section for a thorough understanding of what LIQUidFORM is and what it is not.

The Problem

Have you ever created a domain model and related JPA queries in your favorite IDE, just to find that you can't refactor it ?

Suppose you have the following persistent class :

@Entity
public class Person {
    private String firstName;
    private String surname;

    public String getFirstName() {
        return firstName;
    }

    public String getSurname() {
        return surname;
    }

    // setters omitted
}

and somewhere in your code, you query the model using for example

List people = em.createQuery(
    "SELECT FROM Person p WHERE p.surname LIKE 'Smith%'")
    .getResultList();

Thanks to modern IDEs, if you ever happen to change the attribute surname to let's say lastName you'll update all java references but not the JPA query string ! Thus you'll end up querying for a surname property that doesn't exist anymore, a bug that will hopefully be detected early, but isn't it sad that the query did not get refactored ?

The Solution

LIQUidFORM attempts to solve just that (and a little more). Its aim is to capture your intent when creating the query, so that when you change Person.getSurname() to Person.getLastName(), the query string gets updated as well. Here is how it goes :

Person p = LiquidForm.use(Person.class, "p");
List people = em.createQuery(
    select(p).from(Person.class).as(p).where(like(p.getSurname(), "Smith%")).toString())
    .getResultList();

As you can see, the framework offers a syntax that is very similar to the JPA query syntax. As a matter of fact, LIQUidFORM does very little : it just constructs a String representation of your query, just like you would by hand, that is in turn passed to your JPA engine of choice.

But because in that construct p.getSurname() is an actual method call to the method named getSurname on the type Person, it will get refactored when you change surname to lastName. Of course, you also get other direct indications from your IDE when you perform other kinds of refactorings. For example, if you happen to delete the Person type, you'll be granted by a compile time error for sure. Something that would have gone unnoticed otherwise !

Additional benefits

Because your queries are now built using actual java code, you get strong type safety. This manifests itself eg. when constructing WHERE clauses :

// Won't compile if Person.age is a number
select(p).from(Person.class).as(p).where(like(p.getAge(), "some string"))

This support is not perfect, but that's still better than the plain string syntax that you're used to...

Scope

It is important to understand the following about LIQUidFORM's aim :

  • it is not LINQ for Java : although it provides a DSL for querying, the framework is not a query engine. As a matter of fact, it knows nothing about querying, it just records method invocations and performs some tricks :-)
  • Similarly, it is not an object relational mapper or even a lightweight Java to SQL engine like Hibernate and iBatis are respectively. LIQUidFORM knows nothing about SQL and databases
  • Its goal is to be JPA engine agnostic, although syntax extensions for eg. HQL may be considered in the future

Status

  • The code is still considered a Proof of Concept but allows the creation of "everyday queries" as seen in the above examples:
    • SELECT entities, properties and nested properties are supported
    • SELECT aliased paths are supported
    • SELECT aggregates (DISTINCT, MAX, AVG, etc) are supported and type aware
    • SELECT new POJO(..) syntax is not yet supported
    • FROM entities, aliasing
    • FROM cartesian product, inner and outer joins, fetch joins
    • FROM IN(collection) syntax
    • WHERE, HAVING with complex conditions : AND, OR, NOT, parentheses grouping
    • ORDER BY, GROUP BY constructs are supported
    • sub-queries are supported and type-safe
    • UPDATE, DELETE statements are not supported~
  • Feedback and a helping hand are more than welcome if you find this project useful
Powered by Google Project Hosting