blob: 051e57d5de2980fcc138c13e024444cbfdf69569 [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 3.1 documentation"><meta xmlns:d="http://docbook.org/ns/docbook" name="description" content="User documentation for Apache Cayenne version 3.1"><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.3.1 (3.1)</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" title="Chapter&nbsp;7.&nbsp;Expressions"><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><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 with API</a></span></dt><dt><span class="section"><a href="expressions.html#expressions-in-memory">Evaluating Expressions in Memory</a></span></dt></dl></div><div class="section" title="Expressions Overview"><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" title="Path Expressions"><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"><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"><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"><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"><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" title="Creating Expressions from Strings"><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
understanding the semantics. A Cayenne expression can be represented as a String, which
can be later converted to an expression object using <code class="code">Expression.fromString</code>
static method. Here is an
example:</p><pre class="programlisting">String expString = "name like 'A%' and price &lt; 1000";
Expression exp = Expression.fromString(expString);</pre><p>This
particular expression may be used to match Paintings with names that start with "A" and
a price 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 all 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">Named parameters.</span> Expressions can have named parameters (names that
start with "$"). 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 some
examples:</p><pre class="programlisting">Expression template = Expression.fromString("name = $name");
...
Map p1 = Collections.singletonMap("name", "Salvador Dali");
Expression qualifier1 = template.expWithParameters(p1);
...
Map p2 = Collections.singletonMap("name", "Monet");
Expression qualifier2 = template.expWithParameters(p2);</pre><p>To
create a named parameterized expression with a LIKE clause, SQL wildcards must be part
of the values in the Map and not the expression string
itself:</p><pre class="programlisting">Expression template = Expression.fromString("name like $name");
...
Map p1 = Collections.singletonMap("name", "Salvador%");
Expression qualifier1 = template.expWithParameters(p1);</pre><p>When
matching on a relationship, parameters can be Persistent objects or
ObjectIds:</p><pre class="programlisting">Expression template = Expression.fromString("artist = $artist");
...
Artist dali = // asume we fetched this one already
Map p1 = Collections.singletonMap("artist", dali);
Expression qualifier1 = template.expWithParameters(p1);</pre><p>Uninitialized
parameters will be automatically pruned from expressions, so a user can omit some
parameters when creating an expression from a parameterized
template:</p><pre class="programlisting">Expression template = Expression.fromString("name like $name and dateOfBirth &gt; $date");
...
Map p1 = Collections.singletonMap("name", "Salvador%");
Expression qualifier1 = template.expWithParameters(p1);
// qualifier1 is now equals to "name like 'Salvador%'", the 'dateOfBirth' condition was
// pruned, as no value was specified for the $date parameter</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 can be 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" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>A formal definition of all possible valid expressions in a form of JavaCC
grammar is provided in Appendix C</p></div><p>
</p></div><div class="section" title="Creating Expressions with API"><div class="titlepage"><div><div><h2 class="title"><a name="expressions-with-expressionfactory"></a>Creating Expressions with 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 some degree of compile-time checking of
expressions validity. The API is cenetred around ExpressionFactory class, and the
Expression class. ExpressionFactory contains a number of rather self-explanatory factory
methods. We won't be going over all of them in detail, but will rather show a few
general examples and some gotchas. </p><p>The following code recreates the expression from the previous chapter, but now using
expression
API:</p><pre class="programlisting">// String expression: name like 'A%' and price &lt; 1000
Expression e1 = ExpressionFactory.likeExp(Painting.NAME_PROPERTY, "A%");
Expression e2 = ExpressionFactory.lessExp(Painting.PRICE_PROPERTY, 1000);
Expression finalExp = e1.andExp(e2); </pre><p>This
is more verbose than creating it from String, but it is also more resilient to the
entity properties renaming and precludes semantic errors in the expression String.</p><div class="note" title="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" 2 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>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. "|exhibits.paintings",
"exhibits|paintings", etc. Internally Cayenne would generate distinct aliases for each
of the split expressions, forcing separate joins.</p></div><div class="section" title="Evaluating Expressions in Memory"><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 = ExpressionFactory.inExp(User.NAME_PROPERTY, "John", "Bob");
User user = ...
if(e.match(user)) {
...
}</pre><p>Reading
property
value:</p><pre class="programlisting">Expression e = Expression.fromString(User.NAME_PROPERTY);
String name = e.evaluate(user);</pre><p>Filtering a list of
objects:</p><pre class="programlisting">Expression e = ExpressionFactory.inExp(User.NAME_PROPERTY, "John", "Bob");
List&lt;User&gt; unfiltered = ...
List&lt;User&gt; filtered = e.filterObjects(unfiltered);</pre><p>
</p><div class="note" title="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><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>