This is an automated email from the ASF dual-hosted git repository. aadamchik pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/cayenne.git
The following commit(s) were added to refs/heads/master by this push: new 203ca11 SQL query docs reorg: 203ca11 is described below commit 203ca11ac0cdab74c39dac0a4b564f4c017650fd Author: Andrus Adamchik <and...@objectstyle.com> AuthorDate: Sat Nov 2 11:52:13 2019 +0300 SQL query docs reorg: * removing chapter on SQLTemplate * rewriting scrioting guide to apply to SQLSelect/SQLExec * various minor edits --- .../asciidoc/_cayenne-guide/part2/queries.adoc | 41 +- .../_cayenne-guide/part2/queries/objectselect.adoc | 4 +- .../asciidoc/_cayenne-guide/part2/queries/sql.adoc | 29 +- .../_cayenne-guide/part2/queries/sqlscripting.adoc | 282 ++++++++++++ .../_cayenne-guide/part2/queries/sqltemplate.adoc | 505 --------------------- 5 files changed, 306 insertions(+), 555 deletions(-) diff --git a/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part2/queries.adoc b/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part2/queries.adoc index fd776e3..f997834 100644 --- a/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part2/queries.adoc +++ b/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part2/queries.adoc @@ -28,15 +28,14 @@ that for whatever reason can not be adequately described by the built-in set. Queries can be roughly categorized as "object" and "native". Object queries (most notably `ObjectSelect`, `SelectById`, and `EJBQLQuery`) are built with abstractions originating in the object model (the "object" side in the "object-relational" divide). -E.g. ObjectSelect is assembled from a Java class of the objects to fetch, -a qualifier expression, orderings, etc. - all of this expressed in terms of the object model. +E.g. `ObjectSelect` consists of a Java class of objects to fetch, a qualifier expression, orderings, etc. - all of this +expressed in terms of the object model. -Native queries describe a desired DB operation as SQL code (`SQLSelect`, `SQLTemplate` query) -or a reference to a stored procedure (`ProcedureQuery`), etc. -The results of native queries are usually presented as Lists of Maps, -with each map representing a row in the DB (a term "data row" is often used to describe such a map). -They can potentially be converted to objects, however it may take a considerable effort to do so. -Native queries are also less (if at all) portable across databases than object queries. +Native queries describe a desired DB operation using SQL (`SQLSelect`, `SQLExec` query), a reference to a stored +procedure (`ProcedureQuery`), etc. The results of native queries are lists of scalars, lists of `Object[]` or lists of +maps (a term "data row" is often used to describe such a map). Some of them can potentially be converted to persistent +objects (though usually with considerable effort). Native queries are less (if at all) portable across databases +than object queries. include::queries/objectselect.adoc[] @@ -45,6 +44,8 @@ include::queries/selectbyid.adoc[] include::queries/sql.adoc[] +include::queries/sqlscripting.adoc[] + include::queries/mapped.adoc[] include::queries/procedure.adoc[] @@ -53,27 +54,3 @@ include::queries/ejbql.adoc[] include::queries/custom.adoc[] -include::queries/sqltemplate.adoc[] - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part2/queries/objectselect.adoc b/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part2/queries/objectselect.adoc index 9163ad8..b45141a 100644 --- a/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part2/queries/objectselect.adoc +++ b/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part2/queries/objectselect.adoc @@ -14,10 +14,10 @@ [[select]] ==== ObjectSelect -===== Selecting objects - NOTE: `ObjectSelect` supersedes older `SelectQuery`. `SelectQuery` is deprecated since 4.2. +===== Selecting objects + `ObjectSelect` is the most commonly used query in Cayenne applications. This may be the only query you will ever need. It returns a list of persistent objects (or data rows) of a certain type specified in the query: diff --git a/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part2/queries/sql.adoc b/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part2/queries/sql.adoc index dc14ba6..58429ce 100644 --- a/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part2/queries/sql.adoc +++ b/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part2/queries/sql.adoc @@ -13,20 +13,20 @@ // License. ==== SQLSelect and SQLExec -`SQLSelect` and `SQLExec` are essentially a "fluent" versions of older `SQLTemplate` query. -`SQLSelect` can be used (as name suggests) to select custom data in form of entities, -separate columns, collection of `DataRow` or Object[]. `SQLExec` is designed to just execute any raw SQL code -(e.g. updates, deletes, DDLs, etc.) This queries support all directives described in <<SQLTemplate>> section. Also you can predefine -result type of columns. +SQL is very powerful and allows to manipulate data in ways that can not always be described as a graph of related entities. +Cayenne acknowledges this fact and provides a facility to execute SQL, sometimes allowing to map results back to +persistent objects. `SQLSelect` and `SQLExec` are a pair of queries that allow to run native SQL. `SQLSelect` can be +used (as name suggests) to select custom data in form of entities, separate columns, collection of `DataRow` or Object[]. +`SQLExec` is designed to execute any raw SQL code (e.g. updates, deletes, DDLs, etc.). -Here is example of how to use SQLSelect: +Both queries support advanced SQL templating, with variable substitution and special directives as described +<<sqlscripting,in the next chapter>>. Here we'll just provide a few simple examples: [source, java] ---- // Selecting objects List<Painting> paintings = SQLSelect - .query(Painting.class, "SELECT * FROM PAINTING WHERE PAINTING_TITLE LIKE #bind($title)") - .params("title", "painting%") + .query(Painting.class, "SELECT * FROM PAINTING WHERE PAINTING_TITLE LIKE 'A%'") .upperColumnNames() .localCache() .limit(100) @@ -34,28 +34,25 @@ List<Painting> paintings = SQLSelect // Selecting scalar values List<String> paintingNames = SQLSelect - .scalarQuery(String.class, "SELECT PAINTING_TITLE FROM PAINTING WHERE ESTIMATED_PRICE > #bind($price)") - .params("price", 100000) + .scalarQuery(String.class, "SELECT PAINTING_TITLE FROM PAINTING WHERE ESTIMATED_PRICE > 100000") .select(context); // Selecting DataRow with predefined types List<DataRow> result = SQLSelect - .dataRowQuery("SELECT * FROM ARTIST_CT", Integer.class, String.class, LocalDateTime.class) + .dataRowQuery("SELECT * FROM ARTIST", Integer.class, String.class, LocalDateTime.class) .select(context); // Selecting Object[] with predefined types List<Object[]> result = SQLSelect - .scalarQuery("SELECT * FROM ARTIST_CT", Integer.class, String.class, LocalDateTime.class) + .scalarQuery("SELECT * FROM ARTIST", Integer.class, String.class, LocalDateTime.class) .select(context); ---- - -And here is example of how to use `SQLExec`: +And here is an example of how to use `SQLExec`: [source, java] ---- int inserted = SQLExec - .query("INSERT INTO ARTIST (ARTIST_ID, ARTIST_NAME) VALUES (#bind($id), #bind($name))") - .paramsArray(55, "Picasso") + .query("INSERT INTO ARTIST (ARTIST_ID, ARTIST_NAME) VALUES (55, 'Picasso')") .update(context); ---- diff --git a/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part2/queries/sqlscripting.adoc b/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part2/queries/sqlscripting.adoc new file mode 100644 index 0000000..80ad70d --- /dev/null +++ b/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part2/queries/sqlscripting.adoc @@ -0,0 +1,282 @@ +// Licensed to the Apache Software Foundation (ASF) under one or more +// contributor license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright ownership. +// The ASF licenses this file to you under the Apache License, Version +// 2.0 (the "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 Unless required by +// applicable law or agreed to in writing, software distributed under the +// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for +// the specific language governing permissions and limitations under the +// License. +[#sqlscripting] +==== Scripting SQL Queries + +A powerful feature of `SQLSelect` and `SQLExec` is that a SQL string is treated by Cayenne as a dynamic template. Before +sending it to DB as a PreparedStatement, the String is evaluated, resolving the dynamic parts. The two main scripting +elements are "variables" (that look like `$var`) and "directives" (that look like `#directive(p1 p2 p3)`). In the discussion +below we'll use both selecting and updating examples, as scripting works the same way for both `SQLSelect` and `SQLExec`. + +===== Variable Substitution + +All variables in the template string are replaced from query parameters: + +[source, java] +---- +// this will generate SQL like this: "delete from mydb.PAINTING" +SQLExec query = SQLExec.query("delete from $tableName") + .params("mydb.PAINTING"); +---- + +Variable substitution within the text uses `object.toString()` method to replace the variable value. +This may not be appropriate in all situations. E.g. passing a date object in a `WHERE` clause expression may be +converted to a String not understood by the target DB SQL parser. In such cases variable should be wrapped in +`#bind` directive as described below. + +[#directives] +===== Directives + +"Directives" look like `#directive(p1 p2 p3)` (notice the absence of comma between the arguments). The folliwng +directives are supported in SQL templates: + +====== #bind + +Creates a `PreparedStatement` positional parameter in place of the directive, binding the value to it before statement +execution. `#bind` is allowed in places where a "?" would be allowed in a PreparedStatement. +And in such places it almost always makes sense to pass objects to the template via some flavor of `#bind` instead of +inserting them inline. + +Semantics: + +[source] +---- +#bind(value) +#bind(value jdbcType) +#bind(value jdbcType scale) +---- + +Arguments: + +- `value` - can either be a char constant or a variable that is resolved from the query parameters. +Note that the variable can be a collection, that will be automatically expanded into a list of individual value bindings. +This is useful for instance to build IN conditions. + +- `jdbcType` - is a JDBC data type of the parameter as defined in `java.sql.Types`. + +- `scale` - An optional scale of the numeric value. Same as "scale" in PreparedStatement. + +Usage: + +[source] +---- +#bind($xyz) +#bind('str') +#bind($xyz 'VARCHAR') +#bind($xyz 'DECIMAL' 2) +---- + +Full example: + +[source, SQL] +---- +update ARTIST set NAME = #bind($name) where ID = #bind($id) +---- + + +====== #bindEqual + +Same as `#bind`, but also includes the "=" sign in front of the value binding. +Look at the example below - we took the #bind example and replaced `"ID = #bind(..)"` with `"ID #bindEqual(..)"`. +Motivation for this directive is to handle NULL SQL syntax. If the value is not null, `= ?` is generated, but if it is, +the resulting SQL would look like `IS NULL`, which is compliant with what the DB expects. + +Semantics: + +[source] +---- +#bindEqual(value) +#bindEqual(value jdbcType) +#bindEqual(value jdbcType scale) +---- + +Arguments: (same as #bind) + +Usage: + +[source] +---- +#bindEqual($xyz) +#bindEqual('str') +#bindEqual($xyz 'VARCHAR') +#bindEqual($xyz 'DECIMAL' 2) +---- + +Full example: + +[source, SQL] +---- +update ARTIST set NAME = #bind($name) where ID #bindEqual($id) +---- + +====== #bindNotEqual + +This directive deals with the same issue as `#bindEqual` above, only it generates "!=" in front of the value (or `IS NOT NULL`). + +Semantics: + +[source] +---- +#bindNotEqual(value) +#bindNotEqual(value jdbcType) +#bindNotEqual(value jdbcType scale) +---- + +Arguments: (same as #bind) + +Usage: + +[source] +---- +#bindNotEqual($xyz) +#bindNotEqual('str') +#bindNotEqual($xyz 'VARCHAR') +#bindNotEqual($xyz 'DECIMAL' 2) +---- + +Full example: + +[source, SQL] +---- +update ARTIST set NAME = #bind($name) where ID #bindNotEqual($id) +---- + +====== #bindObjectEqual + +It can be tricky to use a Persistent object or an ObjectId in a binding, especially for tables with compound primary +keys. This directive helps to handle such binding. It maps columns in the query to the names of Persistent object ID +columns, extracts ID values from the object, and generates SQL like "COL1 = ? AND COL2 = ? ..." , binding positional +parameters to ID values. It can also correctly handle null object. Also notice how we are specifying an array for +multi-column PK. + +Semantics: + +[source] +---- +#bindObjectEqual(value columns idColumns) +---- + +Arguments: + +- `value` - must be a variable that is resolved from the query parameters to a Persistent or ObjectId. + +- `columns` - the names of the columns to generate in the SQL. + +- `idColumn` - the names of the ID columns for a given entity. Must match the order of "columns" to match against. + +Usage: + +[source] +---- +#bindObjectEqual($a 't0.ID' 'ID') +#bindObjectEqual($b ['t0.FK1', 't0.FK2'] ['PK1', 'PK2']) +---- + +Full example: + +[source, java] +---- +String sql = "SELECT * FROM PAINTING t0 WHERE #bindObjectEqual($a 't0.ARTIST_ID' 'ARTIST_ID' )"; +Artist artistParam = ...; + +SQLSelect select = SQLSelect.query(Painting.class, sql) + .params("a", artistParam); +---- + +====== #bindObjectNotEqual + +Same as `#bindObjectEqual` above, only generates `!=` operator for value comparison (or `IS NOT NULL`). + +Semantics: + +[source] +---- +#bindObjectNotEqual(value columns idColumns) +---- + +Arguments: (same as #bindObjectEqual) + +Usage: + +[source] +---- +#bindObjectNotEqual($a 't0.ID' 'ID') +#bindObjectNotEqual($b ['t0.FK1', 't0.FK2'] ['PK1', 'PK2']) +---- + +Full example: + +[source, java] +---- +String sql = "SELECT * FROM PAINTING t0 WHERE #bindObjectNotEqual($a 't0.ARTIST_ID' 'ARTIST_ID' )"; +Artist artistParam = ...; + +SQLSelect select = SQLSelect.query(Painting.class, sql) + .params("a", artistParam); +---- + +====== #result + +Used around a column in `SELECT` clause to define the type conversion of the column value (e.g. it may force a conversion +from Integer to Long) and/or define column name in the result (useful when fetching objects or DataRows). + +NOTE: You don't have to use `#result` for any given query if the default data types and column names coming from the +query suit your needs. But if you do, you have to provide `#result` for every single result column, otherwise such column +will be ignored. + +Semantics: + +[source] +---- +#result(column) +#result(column javaType) +#result(column javaType alias) +#result(column javaType alias dataRowKey) +---- + +Arguments: + +- `column` - the name of the column to render in SQL SELECT clause. + +- `javaType` - a fully-qualified Java class name for a given result column. +For simplicity most common Java types used in JDBC can be specified without a package. +These include all numeric types, primitives, String, SQL dates, BigDecimal and BigInteger. +So `"#result('A' 'String')"`, `"#result('B' 'java.lang.String')"` and `"#result('C' 'int')"` are all valid + +- `alias` - specifies both the SQL alias of the column and the value key in the DataRow. If omitted, "column" value is used. + +- `dataRowKey` - needed if SQL 'alias' is not appropriate as a DataRow key on the Cayenne side. +One common case when this happens is when a DataRow retrieved from a query is mapped using joint prefetch keys (see below). +In this case DataRow must use database path expressions for joint column keys, and their format is incompatible with most databases alias format. + +Usage: + +[source] +---- +#result('NAME') +#result('DATE_OF_BIRTH' 'java.util.Date') +#result('DOB' 'java.util.Date' 'DATE_OF_BIRTH') +#result('DOB' 'java.util.Date' '' 'artist.DATE_OF_BIRTH') +#result('SALARY' 'float') +---- + +Full example: + +[source, SQL] +---- +SELECT #result('ID' 'int'), #result('NAME' 'String'), #result('DATE_OF_BIRTH' 'java.util.Date') FROM ARTIST +---- + +NOTE: For advanced features you may look at the <<Apache Velocity extension>> + diff --git a/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part2/queries/sqltemplate.adoc b/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part2/queries/sqltemplate.adoc deleted file mode 100644 index a7f5dc9..0000000 --- a/docs/asciidoc/cayenne-guide/src/docs/asciidoc/_cayenne-guide/part2/queries/sqltemplate.adoc +++ /dev/null @@ -1,505 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one or more -// contributor license agreements. See the NOTICE file distributed with -// this work for additional information regarding copyright ownership. -// The ASF licenses this file to you under the Apache License, Version -// 2.0 (the "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 Unless required by -// applicable law or agreed to in writing, software distributed under the -// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -// CONDITIONS OF ANY KIND, either express or implied. See the License for -// the specific language governing permissions and limitations under the -// License. -[[sqltemplate]] -==== SQLTemplate -SQLTemplate is a query that allows to run native SQL from a Cayenne application. -It comes handy when the standard ORM concepts are not sufficient for a given query or an update. -SQL is too powerful and allows to manipulate data in ways that are not easily described as a graph of related entities. -Cayenne acknowledges this fact and provides this facility to execute SQL, mapping the result to objects when possible. -Here are examples of selecting and non-selecting SQLTemplates: - - -[source, java] ----- -SQLTemplate select = new SQLTemplate(Artist.class, "select * from ARTIST"); -List<Artist> result = context.performQuery(select); ----- - -[source, java] ----- -SQLTemplate update = new SQLTemplate(Artist.class, "delete from ARTIST"); -QueryResponse response = context.performGenericQuery(update); ----- - -Cayenne doesn't make any attempt to make sense of the SQL semantics, -so it doesn't know whether a given query is performing a select or update, etc. -It is the the user's decision to run a given query as a selecting or "generic". - -NOTE: Any data modifications done to DB as a result of `SQLTemplate` execution do not change the state -of objects in the ObjectContext. So some objects in the context may become stale as a result. - -Another point to note is that the first argument to the `SQLTemplate` constructor - the Java class - -has the same meaning as in SelectQuery only when the result can be converted to objects -(e.g. when this is a selecting query and it is selecting all columns from one table). -In this case it denotes the "root" entity of this query result. If the query does not denote a single entity result, -this argument is only used for query routing, i.e. determining which database it should be run against. -You are free to use any persistent class or even a DataMap instance in such situation. -It will work as long as the passed "root" maps to the same database as the current query. - -To achieve interoperability between multiple RDBMS a user can specify multiple SQL statements -for the same `SQLTemplate`, each corresponding to a native SQL dialect. -A key used to look up the right dialect during execution is a fully qualified class name of the corresponding `DbAdapter`. -If no DB-specific statement is present for a given DB, a default generic statement is used. -E.g. in all the examples above a default statement will be used regardless of the runtime database. -So in most cases you won't need to explicitly "translate" your SQL to all possible dialects. -Here is how this works in practice: - -[source, java] ----- -SQLTemplate select = new SQLTemplate(Artist.class, "select * from ARTIST"); - -// For Postgres it would be nice to trim padding of all CHAR columns. -// Otherwise those will be returned with whitespace on the right. -// assuming "NAME" is defined as CHAR... -String pgSQL = "SELECT ARTIST_ID, RTRIM(NAME), DATE_OF_BIRTH FROM ARTIST"; -query.setTemplate(PostgresAdapter.class.getName(), pgSQL); ----- - -===== Scripting SQLTemplate with templates - -The most interesting aspect of SQLTemplate (and the reason why it is called a "template") -is that a SQL string is treated by Cayenne as a template. Before sending it to DB as a PreparedStatement, -the String is evaluated, that does variable substitutions, and performs special callbacks -in response to various directives, thus controlling query interaction with the JDBC layer. - -Here we'll describe the two main scripting elements - "variables" (that look like `$var`) -and "directives" (that look like `#directive(p1 p2 p3)`). -Cayenne defines a number of directives to bind parameters to `PreparedStatement` -and to control the structure of the `ResultSet`. These directives are described in the following sections. - - -===== Variable Substitution - -All variables in the template string are replaced from query parameters: - - -[source, java] ----- -SQLTemplate query = new SQLTemplate(Artist.class, "delete from $tableName"); -query.setParameters(Collections.singletonMap("tableName", "mydb.PAINTING")); - -// this will generate SQL like this: "delete from mydb.PAINTING" ----- - -The example above demonstrates the point made earlier in this chapter - -even if we don't know upfront which table the query will run against, -we can still use a fixed "root" in constructor (`Artist.class` in this case), -as we are not planning on converting the result to objects. - -Variable substitution within the text uses `object.toString()` method to replace the variable value. -Keep in mind that this may not be appropriate in all situations. -E.g. passing a date object in a WHERE clause expression may be converted to a String not understood by the target RDBMS SQL parser. -In such cases variable should be wrapped in `#bind` directive as described below. - -[[directives]] -===== Directives - -These are the Cayenne directives used to customize SQLTemplate parsing and integrate it with the JDBC layer: - -====== #bind - -Creates a PreparedStatement positional parameter in place of the directive, binding the value to it before statement execution. -`#bind` is allowed in places where a "?" would be allowed in a PreparedStatement. -And in such places it almost always makes sense to pass objects to the template via this -or other forms of `#bind` instead of inserting them inline. - -Semantics: - -[source] ----- -#bind(value) -#bind(value jdbcType) -#bind(value jdbcType scale) ----- - -Arguments: - -- `value` - can either be a char constant or a variable that is resolved from the query parameters. -Note that the variable can be a collection, that will be automatically expanded into a list of individual value bindings. -This is useful for instance to build IN conditions. - -- `jdbcType` - is a JDBC data type of the parameter as defined in `java.sql.Types`. - -- `scale` - An optional scale of the numeric value. Same as "scale" in PreparedStatement. - -Usage: - -[source] ----- -#bind($xyz) -#bind('str') -#bind($xyz 'VARCHAR') -#bind($xyz 'DECIMAL' 2) ----- - -Full example: - -[source, SQL] ----- -update ARTIST set NAME = #bind($name) where ID = #bind($id) ----- - - -====== #bindEqual - -Same as #bind, but also includes the "=" sign in front of the value binding. -Look at the example below - we took the #bind example and replaced `"ID = #bind(..)"` with `"ID #bindEqual(..)"`. -While it looks like a clumsy shortcut to eliminate the equal sign, -the actual reason why this is useful is that it allows the value to be null. -If the value is not null, `"= ?"` is generated, but if it is, the resulting chunk of the SQL would look like `"IS NULL"` -and will be compilant with what the DB expects. - -Semantics: - - -[source] ----- -#bindEqual(value) -#bindEqual(value jdbcType) -#bindEqual(value jdbcType scale) ----- - -Arguments: (same as #bind) - -Usage: - -[source] ----- -#bindEqual($xyz) -#bindEqual('str') -#bindEqual($xyz 'VARCHAR') -#bindEqual($xyz 'DECIMAL' 2) ----- - - -Full example: - - -[source, SQL] ----- -update ARTIST set NAME = #bind($name) where ID #bindEqual($id) ----- - -====== #bindNotEqual - -This directive deals with the same issue as `#bindEqual` above, only it generates "not equal" in front of the value (or IS NOT NULL). - -Semantics: - -[source] ----- -#bindNotEqual(value) -#bindNotEqual(value jdbcType) -#bindNotEqual(value jdbcType scale) ----- - -Arguments: (same as #bind) - -Usage: - -[source] ----- -#bindNotEqual($xyz) -#bindNotEqual('str') -#bindNotEqual($xyz 'VARCHAR') -#bindNotEqual($xyz 'DECIMAL' 2) ----- - -Full example: - - -[source, SQL] ----- -update ARTIST set NAME = #bind($name) where ID #bindEqual($id) ----- - -====== #bindObjectEqual - -It can be tricky to use a Persistent object or an ObjectId in a binding, -especially for tables with compound primary keys. This directive helps to handle such binding. -It maps columns in the query to the names of Persistent object ID columns, extracts ID values from the object, -and generates SQL like "COL1 = ? AND COL2 = ? ..." , binding positional parameters to ID values. -It can also correctly handle null object. Also notice how we are specifying an array for multi-column PK. - -Semantics: - -[source] ----- -#bindObjectEqual(value columns idColumns) ----- - -Arguments: - -- `value` - must be a variable that is resolved from the query parameters to a Persistent or ObjectId. - -- `columns` - the names of the columns to generate in the SQL. - -- `idColumn` - the names of the ID columns for a given entity. Must match the order of "columns" to match against. - -Usage: - -[source] ----- -#bindObjectEqual($a 't0.ID' 'ID') -#bindObjectEqual($b ['t0.FK1', 't0.FK2'] ['PK1', 'PK2']) ----- - -Full example: - -[source, java] ----- -String sql = "SELECT * FROM PAINTING t0 WHERE #bindObjectEqual($a 't0.ARTIST_ID' 'ARTIST_ID' ) ORDER BY PAINTING_ID"; -SQLTemplate select = new SQLTemplate(Artist.class, sql); - -Artist a = .... -select.setParameters(Collections.singletonMap("a", a)); ----- - -====== #bindObjectNotEqual - -Same as #bindObjectEqual above, only generates "not equal" operator for value comparison (or IS NOT NULL). - -Semantics: - -[source] ----- -#bindObjectNotEqual(value columns idColumns) ----- - -Arguments: (same as #bindObjectEqual) - -Usage: - -[source] ----- -#bindObjectNotEqual($a 't0.ID' 'ID') -#bindObjectNotEqual($b ['t0.FK1', 't0.FK2'] ['PK1', 'PK2']) ----- - -Full example: - -[source, java] ----- -String sql = "SELECT * FROM PAINTING t0 WHERE #bindObjectNotEqual($a 't0.ARTIST_ID' 'ARTIST_ID' ) ORDER BY PAINTING_ID"; -SQLTemplate select = new SQLTemplate(Artist.class, sql); - -Artist a = .... -select.setParameters(Collections.singletonMap("a", a)); ----- - -====== #result - -Renders a column in SELECT clause of a query and maps it to a key in the result DataRow. -Also ensures the value read is of the correct type. This allows to create a DataRow (and ultimately - a persistent object) -from an arbitrary ResultSet. - -Semantics: - -[source] ----- -#result(column) -#result(column javaType) -#result(column javaType alias) -#result(column javaType alias dataRowKey) ----- - -Arguments: - -- `column` - the name of the column to render in SQL SELECT clause. - -- `javaType` - a fully-qualified Java class name for a given result column. -For simplicity most common Java types used in JDBC can be specified without a package. -These include all numeric types, primitives, String, SQL dates, BigDecimal and BigInteger. -So `"#result('A' 'String')"`, `"#result('B' 'java.lang.String')"` and `"#result('C' 'int')"` are all valid - -- `alias` - specifies both the SQL alias of the column and the value key in the DataRow. If omitted, "column" value is used. - -- `dataRowKey` - needed if SQL 'alias' is not appropriate as a DataRow key on the Cayenne side. -One common case when this happens is when a DataRow retrieved from a query is mapped using joint prefetch keys (see below). -In this case DataRow must use database path expressions for joint column keys, and their format is incompatible with most databases alias format. - -Usage: - -[source] ----- -#result('NAME') -#result('DATE_OF_BIRTH' 'java.util.Date') -#result('DOB' 'java.util.Date' 'DATE_OF_BIRTH') -#result('DOB' 'java.util.Date' '' 'artist.DATE_OF_BIRTH') -#result('SALARY' 'float') ----- - -Full example: - - -[source, SQL] ----- -SELECT #result('ID' 'int'), #result('NAME' 'String'), #result('DATE_OF_BIRTH' 'java.util.Date') FROM ARTIST ----- - -NOTE: For advanced features you may look at the <<Apache Velocity extension>> - -====== Mapping SQLTemplate Results - -Here we'll discuss how to convert the data selected via SQLTemplate to some useable format, compatible with other query results. -It can either be very simple or very complex, depending on the structure of the SQL, -JDBC driver nature and the desired result structure. This section presents various tips and tricks dealing with result mapping. - -By default SQLTemplate is expected to return a List of Persistent objects of its root type. This is the simple case: - -[source, Java] ----- -SQLTemplate query = new SQLTemplate(Artist.class, "SELECT * FROM ARTIST"); - -// List of Artists -List<Artist> artists = context.performQuery(query); ----- - -Just like SelectQuery, SQLTemplate can fetch DataRows. In fact DataRows option is very useful with SQLTemplate, -as the result type most often than not does not represent a Cayenne entity, -but instead may be some aggregated report or any other data whose object structure is opaque to Cayenne: - -[source, Java] ----- -String sql = "SELECT t0.NAME, COUNT(1) FROM ARTIST t0 JOIN PAINTING t1 ON (t0.ID = t1.ARTIST_ID) " - + "GROUP BY t0.NAME ORDER BY COUNT(1)"; -SQLTemplate query = new SQLTemplate(Artist.class, sql); - -// ensure we are fetching DataRows -query.setFetchingDataRows(true); - -// List of DataRow -List<DataRow> rows = context.performQuery(query); ----- - -In the example above, even though the query root is Artist. -The result is a list of artist names with painting counts (as mentioned before in such case "root" -is only used to find the DB to fetch against, but has no bearning on the result). -The DataRows here are the most appropriate and desired result type. - -In a more advanced case you may decide to fetch a list of scalars or a list of `Object[]` -with each array entry being either an entity or a scalar. -You probably won't be doing this too often and it requires quite a lot of work to setup, -but if you want your `SQLTemplate` to return results similar to `EJBQLQuery`, -it is doable using `SQLResult` as described below: - -[source, Java] ----- -SQLTemplate query = new SQLTemplate(Painting.class, "SELECT ESTIMATED_PRICE P FROM PAINTING"); - -// let Cayenne know that result is a scalar -SQLResult resultDescriptor = new SQLResult(); -resultDescriptor.addColumnResult("P"); -query.setResult(resultDescriptor); - -// List of BigDecimals -List<BigDecimal> prices = context.performQuery(query); ----- - -[source, Java] ----- -SQLTemplate query = new SQLTemplate(Artist.class, "SELECT t0.ID, t0.NAME, t0.DATE_OF_BIRTH, COUNT(t1.PAINTING_ID) C " + - "FROM ARTIST t0 LEFT JOIN PAINTING t1 ON (t0.ID = t1.ARTIST_ID) " + - "GROUP BY t0.ID, t0.NAME, t0.DATE_OF_BIRTH"); - -// let Cayenne know that result is a mix of Artist objects and the count of their paintings -EntityResult artistResult = new EntityResult(Artist.class); -artistResult.addDbField(Artist.ID_PK_COLUMN, "ARTIST_ID"); -artistResult.addObjectField(Artist.NAME_PROPERTY, "NAME"); -artistResult.addObjectField(Artist.DATE_OF_BIRTH_PROPERTY, "DATE_OF_BIRTH"); - -SQLResult resultDescriptor = new SQLResult(); -resultDescriptor.addEntityResult(artistResult); -resultDescriptor.addColumnResult("C"); -query.setResult(resultDescriptor); - -// List of Object[] -List<Object[]> data = context.performQuery(query); ----- - -You can fetch list of scalars, list of Object[] or list of DataRow with predefined result column types or using default types. -[source, Java] ----- -// Selecting Object[] with predefined types -SQLTemplate q3 = new SQLTemplate(Artist.class, "SELECT ARTIST_ID, ARTIST_NAME FROM ARTIST"); - q3.setResultColumnsTypes(Double.class, String.class); - q3.setUseScalar(true); -List<Object[]> result = context.performQuery(q3); - -// Selecting DataRow with predefined types -SQLTemplate q3 = new SQLTemplate(Artist.class, "SELECT ARTIST_ID, ARTIST_NAME FROM ARTIST"); - q3.setResultColumnsTypes(Double.class, String.class); - q3.setFetchingDataRows(true); -List<DataRow> result = context.performQuery(q3); ----- - -Another trick related to mapping result sets is making Cayenne recognize prefetched entities in the result set. -This emulates "joint" prefetching of `SelectQuery`, and is achieved by special column naming. -Columns belonging to the "root" entity of the query should use unqualified names corresponding to the root `DbEntity` columns. -For each related entity column names must be prefixed with relationship name and a dot (e.g. "toArtist.ID"). -Column naming can be controlled with `#result` directive: - -[source, Java] ----- -String sql = "SELECT distinct " - + "#result('t1.ESTIMATED_PRICE' 'BigDecimal' '' 'paintings.ESTIMATED_PRICE'), " - + "#result('t1.PAINTING_TITLE' 'String' '' 'paintings.PAINTING_TITLE'), " - + "#result('t1.GALLERY_ID' 'int' '' 'paintings.GALLERY_ID'), " - + "#result('t1.ID' 'int' '' 'paintings.ID'), " - + "#result('NAME' 'String'), " - + "#result('DATE_OF_BIRTH' 'java.util.Date'), " - + "#result('t0.ID' 'int' '' 'ID') " - + "FROM ARTIST t0, PAINTING t1 " - + "WHERE t0.ID = t1.ARTIST_ID"; - -SQLTemplate q = new SQLTemplate(Artist.class, sql); -q.addPrefetch(Artist.PAINTINGS_PROPERTY) -List<Artist> objects = context.performQuery(query); ----- - -And the final tip deals with capitalization of the DataRow keys. Queries like `"SELECT * FROM..."` -and even `"SELECT COLUMN1, COLUMN2, ... FROM ..."` can sometimes result in Cayenne exceptions -on attempts to convert fetched DataRows to objects. -Essentially any query that is not using a `#result` directive to describe the result set is prone to this problem, -as different databases may produce different capitalization of the `java.sql.ResultSet` columns. - -The most universal way to address this issue is to describe each column explicitly in the SQLTemplate via `#result`, -e.g.: `"SELECT #result('column1'), #result('column2'), .."`. -However this quickly becomes impractical for tables with lots of columns. -For such cases Cayenne provides a shortcut based on the fact that an ORM mapping usually follows some naming convention -for the column names. Simply put, for case-insensitive databases developers -normally use either all lowercase or all uppercase column names. -Here is the API that takes advantage of that user knowledge and forces Cayenne to follow -a given naming convention for the DataRow keys (this is also available as a dropdown in the Modeler): - -[source, Java] ----- -SQLTemplate query = new SQLTemplate("SELECT * FROM ARTIST"); -query.setColumnNamesCapitalization(CapsStrategy.LOWER); -List objects = context.performQuery(query); ----- - -or - -[source, Java] ----- -SQLTemplate query = new SQLTemplate("SELECT * FROM ARTIST"); -query.setColumnNamesCapitalization(CapsStrategy.UPPER); -List objects = context.performQuery(query); ----- - -None of this affects the generated SQL, but the resulting DataRows are using correct capitalization. - -NOTE: You probably shouldn't bother with this unless you are getting -`CayenneRuntimeExceptions` when fetching with `SQLTemplate`. \ No newline at end of file