ElSql is a simple library, deliberately so. Many other solutions add far more techniques to support interoperation between Java and the SQL database. This solution simply separates the SQL, as an SQL-like DSL, into an external file. Basic tools then link back to Java. Integration with Spring uses the Spring SqlParameterSource class.
The SQL-like DSL contains tags and SQL. In fact it can contain any text outside the tags, as the SQL itself is not validated.
The DSL is deliberately simple. It is parsed in a line-by-line basis - no significant element to the parser may wrap over a line. The DSL is whitespace aware - two spaces are used to create blocks (instead of curly braces). In combination, this tends to result in readable, if slightly spread out, SQL.
The DSL consists of named snippets. A snippet may include other snippets allowing a degree of re-use. An application, wishing to perform a query or update, asks for a named snippet supplying the set of variables its going to send to JDBC. The ElSql classes lookup the snippet, resolve the SQL using the specified variables, and return it to the application that then uses Spring to resolve the variables themselves into the final SQL ready for the database call.
The tags cover the common awkward cases when building up SQL queries from Java. This includes dynamic generation of WHERE clauses, exact or wildcard matching and paging. Beyond this, there is little if any support - adding more tags complicates the DSL for everyone.
Variables are passed into the DSL using
In Spring, this is the object that has to be created anyway, so there is typically no coding overhead here.
Variables are always referred to using a colon prefix within the elsql file.
These are the tags:
The name tag creates a named block which can be referred to from the application or another part of the elsql file. The tag must be on a line by itself. This is the only permitted tag at the top level.
@NAME(SearchByName) SELECT * FROM foo WHERE name = :name
The include tag includes the contents of a named block. If the parameter is a variable (prefixed by colon) then the name of the named block is looked up from the variable. The tag may be embedded in the middle of a line.
@NAME(SearchByName) SELECT @INCLUDE(Rows) FROM foo WHERE name = :name @NAME(Rows) name, type, value
The value tag simply outputs the value of the variable. The tag may be embedded in the middle of a line.
@NAME(SearchByName) SELECT surname, forename FROM @VALUE(:table) WHERE name = :name
The where tag works together with the and/or tags to build dynamic searches. The tag will output an SQL WHERE, but only if there is at least some content output from the block. Normally, the where tag is not needed, as there is typically always one active where clause. The where tag must be on a line by itself.
These tags are equivalent and output SQL AND or OR. The block that the tag contains is only output if the expression is true. The output SQL will avoid outputting the AND or OR if it immediately follows a WHERE. The and/or tag must be on a line by itself.
The expression is evaluated as follows. If the variable does not exist, then the result is false. Otherwise, if the expression is (:foo) and foo is a boolean, then the result is the boolean value. Otherwise, if the expression is (:foo) and foo is not a boolean, then the result is true. Otherwise, if the expression is (:foo = bar) then the result is true if the variable equals "bar" ignoring case.
@NAME(Search) SELECT * FROM foo @WHERE @AND(:name) name = :name @AND(:type) type = :type
This tag performs a simple "if statement", as it might in Java. See above for how the expression is evaluated.
The like tag adds either an SQL = or an SQL LIKE based on the specified variable. If the tag has no variable in brackets, then the text between the like tag and the end of the line is parsed for a variable. This tag can differ by database, so the actual SQL is generated by the configuration class.
@NAME(Search) SELECT * FROM foo WHERE name @LIKE :name AND UPPER(type) @LIKE UPPER(:type)
The end-like tag is used on rare occasions to scope the end of the like tag. Normally, the SQL should be written such that the end of the like tag is the end of the line.
The paging tag adds the SQL code to page the results of a search. These can differ by database, so the actual SQL is generated by the configuration class. The tag bases its actions on the specified integer variables which should begin with a colon. This replaces the OFFSETFETCH/FETCH tags in most situations as it enables window functions to be used where necessary.
@NAME(Search) @PAGING(:rows_to_offset,:rows_to_fetch) SELECT * FROM foo WHERE name = 'bar' ORDER BY date
The offset-fetch tag adds the SQL OFFSET and FETCH clauses for paging results. These can differ by database, so the actual SQL is generated by the configuration class. The tag bases its actions on the specified integer variables which should begin with a colon. The names "paging_offset" and "paging_fetch" are used if the variables are not specified.
@NAME(Search) SELECT * FROM foo WHERE name = 'bar' ORDER BY date @OFFSETFETCH(:rows_to_offset,:rows_to_fetch)
The fetch tag adds the SQL FETCH clause. It works as per the offset-fetch tag, sometimes known as LIMIT.
@NAME(SearchVariableRowCount) SELECT * FROM foo WHERE name = 'bar' ORDER BY date @FETCH(:num_rows) @NAME(SearchFixedRowCount) SELECT * FROM foo WHERE name = 'bar' ORDER BY date @FETCH(20)
In general, ElSql encourages compelx SQL to be coded in Java. However there can be cases where it can be beneficial to have a simple looping facility in ElSql itself. Bear in mind that it is not advisable to try and push the looping support too far!
@LOOP tag introuces the loop and has a single variable defining the loop size, such as
@LOOP(:size). Within the loop, use
@LOOPINDEX to output a zero-based number matching the index around the loop. At the end of the loop indented block, optionally provide an
@LOOPJOIN followed by text to join each item in the loop. An example explains it well:
@NAME(Test1) SELECT * FROM foo WHERE @LOOP(:size) (a = :a@LOOPINDEX AND b = :b@LOOPINDEX) @LOOPJOIN OR
could be used to output:
SELECT * FROM foo WHERE (a = :a0 AND b = :b0) OR (a = :a1 AND b = :b1)
Last edited by Stephen Colebourne,