blob: 598dfb0c5c374fcd09221f45b3fc73da01feabe2 [file] [log] [blame]
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title xmlns:d="http://docbook.org/ns/docbook">Chapter&nbsp;7.&nbsp;Expressions</title><link rel="stylesheet" type="text/css" href="css/cayenne-doc.css"><meta xmlns:d="http://docbook.org/ns/docbook" name="keywords" content="Cayenne 4.0 documentation"><meta xmlns:d="http://docbook.org/ns/docbook" name="description" content="User documentation for Apache Cayenne version 4.0"><link rel="home" href="index.html" title="Cayenne Guide"><link rel="up" href="cayenne-guide-part2.html" title="Part&nbsp;II.&nbsp;Cayenne Framework"><link rel="prev" href="persistent-objects-objectcontext.html" title="Chapter&nbsp;6.&nbsp;Persistent Objects and ObjectContext"><link rel="next" href="orderings.html" title="Chapter&nbsp;8.&nbsp;Orderings"><script xmlns:d="http://docbook.org/ns/docbook" type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-7036673-1']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div xmlns:d="http://docbook.org/ns/docbook" class="navheader"><table width="100%" summary="Navigation header"><tr><th class="versioninfo">v.4.0 (4.0.M5)</th><th align="center">Chapter&nbsp;7.&nbsp;Expressions</th><th></th></tr><tr><td width="20%" align="left"><a accesskey="p" href="persistent-objects-objectcontext.html">Prev</a>&nbsp;</td><th width="60%" align="center"><a accesskey="u" href="cayenne-guide-part2.html">Part&nbsp;II.&nbsp;Cayenne Framework</a></th><td width="20%" align="right">&nbsp;<a accesskey="n" href="orderings.html">Next</a></td></tr></table><hr></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="expressions"></a>Chapter&nbsp;7.&nbsp;Expressions</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl class="toc"><dt><span class="section"><a href="expressions.html#expressions-overview">Expressions Overview</a></span></dt><dt><span class="section"><a href="expressions.html#path-expressions">Path Expressions</a></span></dt><dt><span class="section"><a href="expressions.html#expressions-from-strings">Creating Expressions from Strings </a></span></dt><dt><span class="section"><a href="expressions.html#expressions-with-expressionfactory">Creating Expressions via API</a></span></dt><dt><span class="section"><a href="expressions.html#expressions-in-memory">Evaluating Expressions in Memory</a></span></dt><dt><span class="section"><a href="expressions.html#expressions-to-ejbql">Translating Expressions to EJBQL</a></span></dt></dl></div><div class="section"><div class="titlepage"><div><div><h2 class="title"><a name="expressions-overview"></a>Expressions Overview</h2></div></div></div><p>Cayenne provides a simple yet powerful object-based expression language. The most common
usese of expressions are to build qualifiers and orderings of queries that are later
converted to SQL by Cayenne and to evaluate in-memory against specific objects (to
access certain values in the object graph or to perform in-memory object filtering and
sorting). Cayenne provides API to build expressions in the code and a parser to create
expressions from strings.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title"><a name="path-expressions"></a>Path Expressions</h2></div></div></div><p>Before discussing how to build expressions, it is important to understand one group of
expressions widely used in Cayenne - path expressions. There are two types of path
expressions - object and database, used for navigating graphs of connected objects or
joined DB tables respectively. Object paths are much more commonly used, as after all
Cayenne is supposed to provide a degree of isolation of the object model from the
database. However database paths are helpful in certain situations. General structure of
path expressions is the following:</p><pre class="programlisting"> [db:]segment[+][.segment[+]...]</pre><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>"db:" is an optional prefix indicating that the following path is a DB
path. Otherwise it is an object path.</p></li><li class="listitem"><p>"segment" is a name of a property (relationship or attribute in Cayenne
terms) in the path. Path must have at least one segment; segments are
separated by dot (".").</p></li><li class="listitem"><p>"+" An "OUTER JOIN" path component. Currently "+" only has effect when
translated to SQL as OUTER JOIN. When evaluating expressions in memory, it
is ignored.</p></li></ul></div><p>An object path expression represents a chain of property names rooted in a certain
(unspecified during expression creation) object and "navigating" to its related value.
E.g. a path expression "artist.name" might be a property path starting from a Painting
object, pointing to the related Artist object, and then to its name attribute. A few
more examples:</p><p>
</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>"name" - can be used to navigate (read) the "name" property of a Person
(or any other type of object that has a "name" property).</p></li><li class="listitem"><p>"artist.exhibits.closingDate" - can be used to navigate to a closing date
of any of the exhibits of a Painting's Artist object.</p></li><li class="listitem"><p>"artist.exhibits+.closingDate" - same as the previous example, but when
translated into SQL, an OUTER JOIN will be used for "exhibits".</p></li></ul></div><p>
</p><p>Similarly a database path expression is a dot-separated path through DB table joins
and columns. In Cayenne joins are mapped as DbRelationships with some symbolic names
(the closest concept to DbRelationship name in the DB world is a named foreign key
constraint. But DbRelationship names are usually chosen arbitrarily, without regard to
constraints naming or even constraints presence). A database path therefore might look
like this - "db:dbrelationshipX.dbrelationshipY.COLUMN_Z". More specific examples:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>"db:NAME" - can be used to navigate to the value of "NAME" column of some
unspecified table.</p></li><li class="listitem"><p>"db:artist.artistExhibits.exhibit.CLOSING_DATE" - can be used to match a
closing date of any of the exhibits of a related artist record.</p></li></ul></div><p>Cayenne supports "aliases" in path Expressions. E.g. the same expression can be
written using explicit path or an alias:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>"artist.exhibits.closingDate" - full path</p></li><li class="listitem"><p>"e.closingDate" - alias "e" is used for "artist.exhibits".</p></li></ul></div><p>SelectQuery using the second form of the path expression must be made
aware of the alias via <span class="italic">
"SelectQuery.aliasPathSplits(..)"</span>, otherwise an Exception will be
thrown. The main use of aliases is to allow users to control how SQL joins are generated
if the same path is encountered more than once in any given Expression. Each alias for
any given path would result in a separate join. Without aliases, a single join will be
used for a group of matching paths.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title"><a name="expressions-from-strings"></a>Creating Expressions from Strings </h2></div></div></div><p>While in most cases users are likely to rely on API from the following section for
expression creation, we'll start by showing String expressions, as this will help
to understand the semantics. A Cayenne expression can be represented as a String, which
can be converted to an expression object using <code class="code">ExpressionFactory.exp</code> static
method. Here is an
example:</p><pre class="programlisting">String expString = <span xmlns="http://www.w3.org/1999/xhtml" class="hl-string">"name like 'A%' and price &lt; 1000"</span>;
Expression exp = ExpressionFactory.exp(expString);</pre><p>This
particular expression may be used to match Paintings whose names that start with
"A" and whose price is less than $1000. While this example is pretty
self-explanatory, there are a few points worth mentioning. "name" and
"price" here are object paths discussed earlier. As always, paths themselves
are not attached to a specific root entity and can be applied to any entity that has
similarly named attributes or relationships. So when we are saying that this expression
"may be used to match Paintings", we are implying that there may be other
entities, for which this expression is valid. Now the expression details... </p><p><span class="italic">Character constants</span> that are not paths or numeric values
should be enclosed in single or double quotes. Two of the expressions below are
equivalent:</p><pre class="programlisting">name = 'ABC'
// double quotes are escaped inside Java Strings of course
name = \"ABC\"</pre><p><span class="italic">Case sensitivity.</span> Expression operators are case
sensitive and are usually lowercase. Complex words follow the Java camel-case
style:</p><pre class="programlisting">// valid
name likeIgnoreCase 'A%'
// invalid - will throw a parse exception
name LIKEIGNORECASE 'A%'</pre><p><span class="italic">Grouping with parenthesis:</span>
</p><pre class="programlisting">value = (price + 250.00) * 3</pre><p>
</p><p><span class="italic">Path prefixes.</span> Object expressions are unquoted strings,
optionally prefixed by "obj:" (usually they are not prefixed at all actually). Database
expressions are always prefixed with "db:". A special kind of prefix, not discussed yet
is "enum:" that prefixes an enumeration
constant:</p><pre class="programlisting">// object path
name = 'Salvador Dali'
// same object path - a rarely used form
obj:name = 'Salvador Dali'
// multi-segment object path
artist.name = 'Salvador Dali'
// db path
db:NAME = 'Salvador Dali'
// enumeration constant
name = enum:org.foo.EnumClass.VALUE1</pre><p>
<span class="italic">Binary conditions</span> are expressions that contain a path
on the left, a value on the right, and some operation between them, such as equals,
like, etc. They can be used as qualifiers in SelectQueries:</p><pre class="programlisting">name like 'A%'</pre><p>
<span class="italic">Parameters.</span> Expressions can contain named parameters
(names that start with "$") that can be substituted with values either by name
or by position. Parameterized expressions allow to create reusable expression templates.
Also if an Expression contains a complex object that doesn't have a simple String
representation (e.g. a Date, a DataObject, an ObjectId), parameterizing such expression
is the only way to represent it as String. Here are the examples of both positional and
named parameter
bindings:</p><pre class="programlisting">Expression template = ExpressionFactory.exp(<span xmlns="http://www.w3.org/1999/xhtml" class="hl-string">"name = $name"</span>);
...
<span xmlns="http://www.w3.org/1999/xhtml" class="hl-comment">// name binding</span>
Map p1 = Collections.singletonMap(<span xmlns="http://www.w3.org/1999/xhtml" class="hl-string">"name"</span>, <span xmlns="http://www.w3.org/1999/xhtml" class="hl-string">"Salvador Dali"</span>);
Expression qualifier1 = template.params(p1);
...
<span xmlns="http://www.w3.org/1999/xhtml" class="hl-comment">// positional binding</span>
Expression qualifier2 = template.paramsArray(<span xmlns="http://www.w3.org/1999/xhtml" class="hl-string">"Monet"</span>);</pre><p>Positional binding is usually shorter. You can pass positional bindings to the
<code class="code">"exp(..)"</code> factory method (its second argument is a params
vararg):</p><pre class="programlisting">Expression qualifier = ExpressionFactory.exp("name = $name", "Monet");</pre><p>In parameterized expressions with LIKE clause, SQL wildcards must be part of the
values in the Map and not the expression string
itself:</p><pre class="programlisting">Expression qualifier = ExpressionFactory.exp(<span xmlns="http://www.w3.org/1999/xhtml" class="hl-string">"name like $name"</span>, <span xmlns="http://www.w3.org/1999/xhtml" class="hl-string">"Salvador%"</span>);</pre><p>When
matching on a relationship, the value parameter must be either a Persistent object, an
<code class="code">org.apache.cayenne.ObjectId</code>, or a numeric ID value (for single column
IDs).
E.g.:</p><pre class="programlisting">Artist dali = ... <span xmlns="http://www.w3.org/1999/xhtml" class="hl-comment">// asume we fetched this one already</span>
Expression qualifier = ExpressionFactory.exp(<span xmlns="http://www.w3.org/1999/xhtml" class="hl-string">"artist = $artist"</span>, dali);</pre><p>When
using positional binding, Cayenne would expect values for <span class="bold"><strong>all</strong></span> parameters to be present. Binding by name offers extra flexibility:
subexpressions with uninitialized parameters are automatically pruned from the
expression. So e.g. if certain parts of the expression criteria are not provided to the
application, you can still build a valid
expression:</p><pre class="programlisting">Expression template = ExpressionFactory.exp(<span xmlns="http://www.w3.org/1999/xhtml" class="hl-string">"name like $name and dateOfBirth &gt; $date"</span>);
...
Map p1 = Collections.singletonMap(<span xmlns="http://www.w3.org/1999/xhtml" class="hl-string">"name"</span>, <span xmlns="http://www.w3.org/1999/xhtml" class="hl-string">"Salvador%"</span>);
Expression qualifier1 = template.params(p1);
<span xmlns="http://www.w3.org/1999/xhtml" class="hl-comment">// "qualifier1" is now "name like 'Salvador%'".</span>
<span xmlns="http://www.w3.org/1999/xhtml" class="hl-comment">// 'dateOfBirth &gt; $date' condition was pruned, as no value was specified for </span>
<span xmlns="http://www.w3.org/1999/xhtml" class="hl-comment">// the $date parameter</span></pre><p><span class="italic">Null handling.</span> Handling of Java nulls as operands
is no different from normal values. Instead of using special conditional operators, like
SQL does (IS NULL, IS NOT NULL), "=" and "!=" expressions are used
directly with null values. It is up to Cayenne to translate expressions with nulls to
the valid SQL.</p><p>
</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>A formal definition of the expression grammar is provided in Appendix C</p></div><p>
</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title"><a name="expressions-with-expressionfactory"></a>Creating Expressions via API</h2></div></div></div><p>Creating expressions from Strings is a powerful and dynamic approach, however a safer
alternative is to use Java API. It provides compile-time checking of expressions
validity. The API in question is provided by <code class="code">ExpressionFactory</code> class (that
we've seen already), <code class="code">Property</code> class and <code class="code">Expression</code> class
itself. <code class="code">ExpressionFactory</code> contains a number of self-explanatory static
methods that can be used to build expressions. E.g.:</p><p>
</p><pre class="programlisting"><span xmlns="http://www.w3.org/1999/xhtml" class="hl-comment">// String expression: name like 'A%' and price &lt; 1000</span>
Expression e1 = ExpressionFactory.likeExp(<span xmlns="http://www.w3.org/1999/xhtml" class="hl-string">"name"</span>, <span xmlns="http://www.w3.org/1999/xhtml" class="hl-string">"A%"</span>);
Expression e2 = ExpressionFactory.lessExp(<span xmlns="http://www.w3.org/1999/xhtml" class="hl-string">"price, 1000);
</span>Expression finalExp = e1.andExp(e2); </pre><p>
</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>The last line in the example above shows how to create a new expression by
"chaining" two other epxressions. A common error when chaining
expressions is to assume that "andExp" and "orExp" append
another expression to the current expression. In fact a new expression is
created. I.e. Expression API treats existing expressions as immutable.</p></div><p>
</p><p>As discussed earlier, Cayenne supports aliases in path Expressions, allowing to
control how SQL joins are generated if the same path is encountered more than once in
the same Expression. Two ExpressionFactory methods allow to implicitly generate aliases
to "split" match paths into individual joins if
needed:</p><pre class="programlisting">Expression matchAllExp(String path, Collection values)
Expression matchAllExp(String path, Object... values)</pre><p>"Path" argument to both of these methods can use a split character (a pipe
symbol '|') instead of dot to indicate that relationship following a path
should be split into a separate set of joins, one per collection value. There can only
be one split at most in any given path. Split must always precede a relationship. E.g.
<code class="code">"|exhibits.paintings"</code>, <code class="code">"exhibits|paintings"</code>, etc.
Internally Cayenne would generate distinct aliases for each of the split expressions,
forcing separate joins.</p><p>While ExpressionFactory is pretty powerful, there's an even easier way to create
expression using static Property objects generated by Cayenne for each persistent class.
Some
examples:</p><pre class="programlisting">// Artist.NAME is generated by Cayenne and has a type of Property&lt;String&gt;
Expression e1 = Artist.NAME.eq("Pablo");
// Chaining multiple properties into a path..
// Painting.ARTIST is generated by Cayenne and has a type of Property&lt;Artist&gt;
Expression e2 = Painting.ARTIST.dot(Artist.NAME).eq("Pablo");</pre><p>Property objects provide the API mostly analogius to ExpressionFactory, though it is
significantly shorter and is aware of the value types. It provides compile-time checks
of both property names and types of arguments in conditions. We will use Property-based
API in further examples.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title"><a name="expressions-in-memory"></a>Evaluating Expressions in Memory</h2></div></div></div><p>When used in a query, an expression is converted to SQL WHERE clause (or ORDER BY
clause) by Cayenne during query execution. Thus the actual evaluation against the data
is done by the database engine. However the same expressions can also be used for
accessing object properties, calculating values, in-memory filtering. </p><p>Checking whether an object satisfies an
expression:</p><pre class="programlisting">Expression e = Artist.NAME.in(<span xmlns="http://www.w3.org/1999/xhtml" class="hl-string">"John"</span>, <span xmlns="http://www.w3.org/1999/xhtml" class="hl-string">"Bob"</span>);
Artist artist = ...
<span xmlns="http://www.w3.org/1999/xhtml" class="hl-keyword">if</span>(e.match(artist)) {
...
}</pre><p>Reading
property
value:</p><pre class="programlisting">String name = Artist.NAME.path().evaluate(artist);</pre><p>Filtering a list of
objects:</p><pre class="programlisting">Expression e = Artist.NAME.in(<span xmlns="http://www.w3.org/1999/xhtml" class="hl-string">"John"</span>, <span xmlns="http://www.w3.org/1999/xhtml" class="hl-string">"Bob"</span>);
List&lt;Artist&gt; unfiltered = ...
List&lt;Artist&gt; filtered = e.filterObjects(unfiltered);</pre><p>
</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>Current limitation of in-memory expressions is that no collections are
permitted in the property path.</p></div><p>
</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title"><a name="expressions-to-ejbql"></a>Translating Expressions to EJBQL</h2></div></div></div><p>
<a class="link" href="queries.html#ejbqlquery" title="EJBQLQuery">EJBQL</a> is a textual query language that can be used
with Cayenne. In some situations, it is convenient to be able to convert Expression
instances into EJBQL. Expressions support this conversion. An example is shown below.
</p><pre class="programlisting">String serial = ...
Expression e = Pkg.SERIAL.eq(serial);
List&lt;Object&gt; params = <span xmlns="http://www.w3.org/1999/xhtml" class="hl-keyword">new</span> ArrayList&lt;Object&gt;();
EJBQLQuery query = <span xmlns="http://www.w3.org/1999/xhtml" class="hl-keyword">new</span> EJBQLQuery(<span xmlns="http://www.w3.org/1999/xhtml" class="hl-string">"SELECT p FROM Pkg p WHERE "</span> + e.toEJBQL(params,<span xmlns="http://www.w3.org/1999/xhtml" class="hl-string">"p"</span>);
<span xmlns="http://www.w3.org/1999/xhtml" class="hl-keyword">for</span>(<span xmlns="http://www.w3.org/1999/xhtml" class="hl-keyword">int</span> i=<span xmlns="http://www.w3.org/1999/xhtml" class="hl-number">0</span>;i&lt;params.size();i++) {
query.setParameter(i+<span xmlns="http://www.w3.org/1999/xhtml" class="hl-number">1</span>, params.get(i));
}</pre><p>
This would be equivalent to the following purely EJBQL querying logic;
</p><pre class="programlisting">EJBQLQuery query = <span xmlns="http://www.w3.org/1999/xhtml" class="hl-keyword">new</span> EJBQLQuery(<span xmlns="http://www.w3.org/1999/xhtml" class="hl-string">"SELECT p FROM Pkg p WHERE p.serial = ?1"</span>);
query.setParameter(<span xmlns="http://www.w3.org/1999/xhtml" class="hl-number">1</span>,serial);</pre><p>
</p></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="persistent-objects-objectcontext.html">Prev</a>&nbsp;</td><td width="20%" align="center"><a accesskey="u" href="cayenne-guide-part2.html">Up</a></td><td width="40%" align="right">&nbsp;<a accesskey="n" href="orderings.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">Chapter&nbsp;6.&nbsp;Persistent Objects and ObjectContext&nbsp;</td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top">&nbsp;Chapter&nbsp;8.&nbsp;Orderings</td></tr></table></div></body></html>