6

While looking for java libraries to build queries in a database agnostic way I came across many including iciql, querydsl, jooq, joist, hibernate etc.

I wanted something that does not require configuration files and can work with dynamic schemas. For my application, I come to know about the database and the schema at runtime so I won't have any configuration files or domain classes for the schema.

This seems to be one of the core goals of querydsl but going through the documentation for querydsl I see a lot of examples for building dynamic queries using domain classes but I have not come across anything that explains how to build such database agnostic queries using just the dynamic information I have about the schema.

Jooq offers such functionality(See: http://www.jooq.org/doc/3.2/manual/getting-started/use-cases/jooq-as-a-standalone-sql-builder/) but have a restrictive license if I want to expand my focus to Oracle or MS SQL(Which I may not love but need to support).

Can someone with experience in querydsl let me know if such a thing is possible with querydsl, and if yes, how.

If someone know of any other too which can satisfy my requirements, it would be really appreciated.

MickJ
  • 2,127
  • 3
  • 20
  • 34
  • jOOQ users have used jOOQ excessively using the use-case you described, so this is a well-supported scenario. – Lukas Eder Feb 07 '14 at 13:37
  • Thanks Lukas. I know that jOOQ supports this. I am trying to see if this can be done with querydsl since it seems to possess the backbone needed for it and jOOQ requires licensing for adding support for major commercial databases like Oracle, MS SQL and Sybase, that I cannot do. – MickJ Feb 07 '14 at 16:13

3 Answers3

6

A very simple SQL query such as this:

@Transactional
public User findById(Long id) {
    return new SQLQuery(getConnection(), getConfiguration())
      .from(user)
      .where(user.id.eq(id))
      .singleResult(user);
}

...can be created dynamically like this (without any sugar added):

@Transactional
public User findById(Long id) {
    Path<Object> userPath = new PathImpl<Object>(Object.class, "user");
    NumberPath<Long> idPath = Expressions.numberPath(Long.class, userPath, "id");
    StringPath usernamePath = Expressions.stringPath(userPath, "username");
    Tuple tuple = new SQLQuery(getConnection(), getConfiguration())
      .from(userPath)
      .where(idPath.eq(id))
      .singleResult(idPath, usernamePath);
    return new User(tuple.get(idPath), tuple.get(usernamePath));
}
ponzao
  • 20,684
  • 3
  • 41
  • 58
  • 1
    Looks promising. I will try out and respond back with success or failure. Thanks. – MickJ Feb 07 '14 at 16:18
  • @MickJ, no problem! This is actually quite an interesting (and rare?) way of using Querydsl and we would love to hear how it pans out (http://twitter.com/querydsl). – ponzao Feb 07 '14 at 22:39
  • Hi @ponzao: I had a follow up question. I have created it here: http://stackoverflow.com/questions/21689222/how-to-get-fully-materialized-query-from-querydsl It would be great to get your input on it. Basically I am not getting schema.table notations and all parameters show up as '?' instead of being fully materialized in a query. – MickJ Feb 10 '14 at 22:31
5

Here is a small variation of ponzao's solution using PathBuilder

@Transactional
public User findById(Long id) {        
    PathBuilder<Object> userPath = new PathBuilder<Object>(Object.class, "user");
    NumberPath<Long> idPath = userPath.getNumber("id", Long.class);
    StringPath usernamePath = userPath.getString("username");
    Tuple tuple = new SQLQuery(getConnection(), getConfiguration())
      .from(userPath)
      .where(idPath.eq(id))
      .singleResult(idPath, usernamePath);
    return new User(tuple.get(idPath), tuple.get(usernamePath));
}
Timo Westkämper
  • 21,824
  • 5
  • 78
  • 111
1

Update: Timo has invalidated my original response by showing me how to do what I want WITHOUT having to replace the SQLQuery class. Here is his comment:

query.getSQL(field1, field2, ... fieldN), getSQL is consistent with the
other methods which also take the projection arguments at last

I have removed my unnecessary class. Here is an example of using SQLQuery directly to get the SQL string WITHOUT executing the query (e.g., without using the list method):

SQLQuery rquery = new SQLQuery(connection , dialect);

// Use getSQL with projections
rquery.from(qtable)
    .where(qtable.qfield1.eq("somevalue"));

SQLBindings bindings = rquery.getSQL(qtable.qfield1, qtable.qfield2);

// Get the SQL string from the SQLBindings
System.out.println(bindings.getSql());

// Get the SQL parameters from the SQLBindings for the parameterized query
System.out.println(bindings.getBindings());

This response answers how to use QueryDSL to build out a complete SQL query without actually executing the query. It does not address your additional requirements about "dynamic schemas" and "without domain objects"...

David Fleeman
  • 2,588
  • 14
  • 17
  • Thanks for your response. While this does explain building of query, it does need creation of domain classes and so would not work with the dynamic schemas as my use case requires. – MickJ Feb 07 '14 at 16:10
  • @david-fleeman You can get the SQL string and bindings out directly via this method http://www.querydsl.com/static/querydsl/3.3.0/apidocs/com/mysema/query/sql/AbstractSQLQuery.html#getSQL%28com.mysema.query.types.Expression...%29 – Timo Westkämper Feb 07 '14 at 19:14
  • @TimoWestkämper - yes, I show that in my answer as well if you look at the code. The issue is the `select field1, field2` part of the query is NOT part of the string until you actually call the `list()` method which executes the query as well. If you want to get the full SQL without executing, then as far as I could tell you need my helper class. Please correct me if I am wrong! – David Fleeman Feb 07 '14 at 19:21
  • 1
    Use it like this: query.getSQL(field1, field2, ... fieldN), getSQL is consistent with the other methods which also take the projection arguments at last. – Timo Westkämper Feb 07 '14 at 19:32
  • @TimoWestkämper - ha, don't know how I missed that. Thanks Timo! Will incorporate your comment up into my answer so that future readers see how to do it right. – David Fleeman Feb 07 '14 at 19:33