views:

254

answers:

6

Here goes a SQL-Java coding style question...

How do others here deal with creating complex custom queries in Java cleanly?

I am speaking of the seemingly simple task of preparing a string which is the SQL statement to be executed.

I am aware of HQL, and also of stored procedures, but honestly I don't really love these solutions. Perhaps I can be convinced differently. Stored procedures are annoying to deploy/maintain, and parsing performance is not such a huge issue in my case -- flexibility takes precedence. HQL seems like a big leap, and has some limitations for my complex queries.

TO be clear, I am talking of super-ugly looking code like this:

    return 
        "(" + topTwenty + ")" +
        "UNION " +
        "(" + twentyBeforeMe + ")" +
        "UNION " +
        "(" + meDummyQuery + ")" +
        "UNION " +
        "(" + twentyAfterMe + ")";

Where the variables topTwenty are for example also subqueries created similarly.

I never thought I'd say this, but it was cleaner in PHP which had the multi-line string and $variable embedding in the strings.

Do people ever use a trivial templating library? How do you neatly keep the strings in the program? Or do you put them in a separate file (also seems annoying to me somehow).

+3  A: 

You probably want to look at parameterizing your queries which you can do with JDBC PreparedStatements. If you're coming from the PHP world, it's analogous to PDO. Basically. all your dynamic $variables from the PHP string you alluded to above, would be ?s in a parameterized query. The ?s get substituted in a safe (read proper escaping) way by methods of the PreparedStatement implementation.

See Sun's PreparedStatement tutorial for examples and further explanation.

Asaph
Does a PreparedStatement support embedded subselects in place of the question marks? Most libraries I'm familiar with only support embedding literal values, not queries. If that's the case here, the user would still need to create the example query using some sort of string formatter or template, though parameters in the individual subselects could be escaped using a PreparedStatement.
Dathan
@Dathan: No, PreparedStatements only support literal values in place of question marks. You would build the embedded subselects by concatenating strings.
Asaph
+2  A: 

Java has PrintFormat class which does very similar thing as sprintf in C, you might want to leverage it to format your query.

Following code snippet is taken from http://java.sun.com/developer/technicalArticles/Programming/sprintf/

System.out.println(
new PrintfFormat("Pre %s Post%%").sprintf("target")
);
Jay Zeng
+6  A: 

Without going into database abstraction, you could probably make your queries more readable with a PreparedStatment -- not to mention it contributes to security.

Jeff
Jeff, that's a good tip, but even with the prepared statements, I still have these monster, monster strings, like" SELECT " +" field1, " +" field2 " + [" FROM" ...
Artem
Looking at PreparedStatement, I see it uses numerical indices for indexing parameters... With the statements of any complexity that seems like asking for a lot of trouble.
Artem
Nah, real trouble is when your users manage to sneak in a "; delete * from users" into one of your fields. ;)
PSpeed
I would recommend you to format your query before passing to PreparedStatement() (such as leveraging PrintfFormat() or any other class or your custom code).
Jay Zeng
+1  A: 

http://code.google.com/p/sqlck/, this is my project by the way.

fuzzy lollipop
A: 

You could try using StringBuilder.append() if you think that will make your code look cleaner:

return new StringBuilder()
   .append("(").append(topTwenty).append(")")
   .toString();
kchellap
this is a 100% sure way to introduce SQL Injection attack vectors!
fuzzy lollipop
+1  A: 

What I do is to use String.format for table names and things that can't be parametrized, and this class(NamedPreparedStament), which allows to use good names for bind variables instead of question marks.

String sql = "SELECT id FROM %s WHERE id > :lastInsertedYesterday ";
NamedParameterStatement p = new NamedParameterStatement(con, 
                                    String.format(sql, "table1"));
p.setInt("lastInsertedYesterday", lastOne);
Samuel
Thank you, Sam. I am going to accept your answer -- NamedPreparedStatements look pretty nice, and your String.format is also pretty nice-looking.
Artem
After looking around based on your answer, I also found this NamedParameterJdbcTemplate -- http://www.vaannila.com/spring/spring-named-parameter-jdbc-template.html. Since we are already using Spring in this project, I will give this a shot.
Artem