Title: Expression Factory Utilities
Sometimes there is a need to build an expression by combining other existing expressions. Also quiet often it is desirable to use strongly typed API instead of interpreted string expressions. The following sections describe ExpressionFactory and Expression methods that allow to construct expressions step by step via API calls.
The most simple expressions are the ones that match an object property path with a value or a list of values. ExpressionFactory provides a set of methods to build such "path/value" expressions:
As was mentioned earlier, the type of a second Object argument depends on the type of property path points to. It is important to mention that paths that end with a relationship name (both to-one and to-many) can be matched against Persistent objects, thus absolving you from the need to know a PK or FK when building expressions. This behavior is not specific to ExpressionFactory, it works the same way with Expression.fromString(..) as well.
import org.apache.cayenne.exp.Expression; import org.apache.cayenne.exp.ExpressionFactory; import org.apache.cayenne.query.SelectQuery; ... // find artist paintings (if we don't want to use relationship for whatever reason) Artist a = ...; Expression qual = ExpressionFactory.matchExp("toArtist", a); SelectQuery select = new SelectQuery(Painting.class, qual);
Expression class itself provides a set of convenience methods to chain expressions as they are built from smaller parts. Note that each of these methods does not modify the original expression, rather it builds and returns a new instance of the expression.
Example of using chaining:
import org.apache.cayenne.exp.Expression; import org.apache.cayenne.exp.ExpressionFactory; import org.apache.cayenne.query.SelectQuery; ... // find artists whose name starts with "D" // with invalid or null date of birth Expression qual = ExpressionFactory.greaterOrEqualExp("dateOfBirth", new Date()); // 1. chain expressions, note the assignment back to "qual", // since a new instance is created qual = qual.orExp(ExpressionFactory.matchExp("dateOfBirth", null)); // 2. "AND" applies to a combined earlier criteria qual = qual.andExp(ExpressionFactory.likeIgnoreCaseExp("artistName", "D%")); SelectQuery select = new SelectQuery(Artist.class, qual);
There is a way to create complex expressions either from the Lists of expressions or from the Maps containing values using "path" Strings as keys. This approach significantly simplifies connecting Cayenne queries to the UI, and reduces the number of steps needed to create expressions in other cases. ExpressionFactory provides the following methods:
Example of creating complex expressions:
Map map = new HashMap(); map.put("login", "joeuser"); map.put("password", "secret"); // the last parameter refers to the operation inside each key/value pair. Expression qual = ExpressionFactory.matchAllExp(map, Expression.EQUAL_TO);
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:
"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.