-
Notifications
You must be signed in to change notification settings - Fork 0
/
searching.html
353 lines (351 loc) · 13.1 KB
/
searching.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title>exedio persistence - Searching Trail</title>
<meta name="keywords" content="Persistence, Java">
<meta name="description" content="persistence framework for java">
<link rel="stylesheet" href="cope.css">
</head>
<body>
<!-- HEADER -->
<div id="header_wrap" class="outer">
<header class="inner">
<a id="forkme_banner" href="https://github.com/exedio">View on GitHub</a>
<h1 id="project_title">exedio persistence</h1>
<h2 id="project_tagline">Persistence Framework for Java.</h2>
<a id="toplink" href="index.html">Go to top</a>
</header>
</div>
<!-- MAIN CONTENT -->
<div id="main_content_wrap" class="outer">
<section id="main_content" class="inner">
<h2>Searching Trail</h2>
<p>
This trail shows you how to query your data with exedio persistence.
If you are familiar with SQL,
you will recognize it's features within the API provided by the framework.
However, you will never directly write SQL,
nor any other text based query language.
This will show you most common mistakes as compile-time errors,
and it will completely remove any chance of
<a href="https://en.wikipedia.org/wiki/SQL_injection">SQL injection</a>.
</p>
<h3 id="contents">Contents</h3>
<ul>
<li><a href="#simple">Simple Query</a></li>
<li><a href="#conditions">Conditions</a></li>
<li><a href="#sorting">Sorting</a></li>
<li><a href="#limits">Limits</a></li>
<li><a href="#selecting">Selecting and Aggregating</a></li>
<li><a href="#joins">Joins</a></li>
<li><a href="#further">Further Reading</a></li>
</ul>
<h3 id="simple">Simple Query</h3>
<p>
First we define a simple example model
consisting of one persistent class only:
</p>
<pre>
class Customer extends <a href="api/com/exedio/cope/Item.html">Item</a>
{
static final <a href="api/com/exedio/cope/StringField.html">StringField</a> email =
new <a href="api/com/exedio/cope/StringField.html#<init>()">StringField</a>();
static final <a href="api/com/exedio/cope/IntegerField.html">IntegerField</a> loginCounter =
new <a href="api/com/exedio/cope/IntegerField.html#<init>()">IntegerField</a>();
}
</pre>
<p>
Lets start with a simple query searching for all instances of
this class:
</p>
<pre>
List<Customer> allCustomers =
Customer.TYPE.<a href="api/com/exedio/cope/Type.html#search()">search</a>();
</pre>
<p>
Note, that the static final field <code>Customer.TYPE</code> is
generated by the source instrumentor.
Compiling this statement does not cause an "unchecked warning",
the return type of search is really a <code>List<Customer></code>
</p>
<p>
If you put the code above into class <code>Customer</code>,
you can write even shorter:
</p>
<pre>
List<Customer> allCustomers = TYPE.<a href="api/com/exedio/cope/Type.html#search()">search</a>();
</pre>
<p>
For brevity we will assume, that all the examples below are
located in class <code>Customer</code>.
</p>
<h3 id="conditions">Conditions</h3>
<p>
Conditions restrict the number of items found by a query.
This is equivalent to the "where clause" in SQL.
Lets search for the customer with a certain email address:
</p>
<pre>
TYPE.<a href="api/com/exedio/cope/Type.html#search(com.exedio.cope.Condition)">search</a>(email.<a href="api/com/exedio/cope/Function.html#equal(E)">equal</a>("[email protected]"));
</pre>
<p>
For a "like condition" write:
</p>
<pre>
TYPE.<a href="api/com/exedio/cope/Type.html#search(com.exedio.cope.Condition)">search</a>(email.<a href="api/com/exedio/cope/StringFunction.html#like(java.lang.String)"><b>like</b></a>("search@exedio.<b>%</b>"));
</pre>
<p>
There is a convenience method, that lets you write equivalently but shorter:
</p>
<pre>
TYPE.<a href="api/com/exedio/cope/Type.html#search(com.exedio.cope.Condition)">search</a>(email.<a href="api/com/exedio/cope/StringFunction.html#startsWith(java.lang.String)"><b>startsWith</b></a>("search@exedio."));
</pre>
<p>
Of course searching works for integer fields as well.
Here a query, that searches for customers,
that have logged in at least 8 times:
</p>
<pre>
TYPE.<a href="api/com/exedio/cope/Type.html#search(com.exedio.cope.Condition)">search</a>(loginCounter.<a href="api/com/exedio/cope/Function.html#greaterOrEqual(E)">greaterOrEqual</a>(8));
</pre>
<p>
To demonstrate composite conditions,
here another query, that searches for customers,
that have logged in at least 8 times, but at most 12 times:
</p>
<pre>
TYPE.<a href="api/com/exedio/cope/Type.html#search(com.exedio.cope.Condition)">search</a>(loginCounter.<a href="api/com/exedio/cope/Function.html#greaterOrEqual(E)">greaterOrEqual</a>(8).<a href="api/com/exedio/cope/Condition.html#and(com.exedio.cope.Condition)"><b>and</b></a>(loginCounter.<a href="api/com/exedio/cope/Function.html#lessOrEqual(E)">lessOrEqual</a>(12)));
</pre>
<p>
which is equivalent to:
</p>
<pre>
TYPE.<a href="api/com/exedio/cope/Type.html#search(com.exedio.cope.Condition)">search</a>(loginCounter.<a href="api/com/exedio/cope/Function.html#between(E,E)">between</a>(8, 12));
</pre>
<p>
Of course there is an OR-condition as well:
</p>
<pre>
TYPE.<a href="api/com/exedio/cope/Type.html#search(com.exedio.cope.Condition)">search</a>(email.<a href="api/com/exedio/cope/Function.html#equal(E)">equal</a>("[email protected]").<a href="api/com/exedio/cope/Condition.html#or(com.exedio.cope.Condition)"><b>or</b></a>(email.<a href="api/com/exedio/cope/Function.html#equal(E)">equal</a>("[email protected]")));
</pre>
<p>
which is equivalent to:
</p>
<pre>
TYPE.<a href="api/com/exedio/cope/Type.html#search(com.exedio.cope.Condition)">search</a>(email.<a href="api/com/exedio/cope/Function.html#in(E...)">in</a>("[email protected]", "[email protected]"));
</pre>
<p>
In contrast to the queries above,
all the following queries will not compile:
</p>
<pre>
TYPE.<a href="api/com/exedio/cope/Type.html#search(com.exedio.cope.Condition)">search</a>(email.equal(8));
TYPE.<a href="api/com/exedio/cope/Type.html#search(com.exedio.cope.Condition)">search</a>(loginCounter.greaterOrEqual("[email protected]"));
TYPE.<a href="api/com/exedio/cope/Type.html#search(com.exedio.cope.Condition)">search</a>(loginCounter.like("search@exedio.%"));
</pre>
<p>
The first one tries to compare a string field to an integer literal.
The second query compares an integer field to a string field.
The third uses the operator "like" on an integer field instead of a string field.
</p>
<h3 id="sorting">Sorting</h3>
<p>
Here again a query, that searches for customers,
that have logged in more than 8 times,
but now sorted by their email adresses:
</p>
<pre>
TYPE.<a href="api/com/exedio/cope/Type.html#search(com.exedio.cope.Condition,com.exedio.cope.Function,boolean)">search</a>(loginCounter.<a href="api/com/exedio/cope/Function.html#greaterOrEqual(E)">greaterOrEqual</a>(8), <b>email, true</b>);
</pre>
<p>
The third parameter specifies accending order.
Use <code>false</code> to order descendingly.
</p>
<p>
If you want to order by multiple fields,
you have to use an explicit query for the first time.
This looks like this:
</p>
<pre>
Query<Customer> q =
TYPE.newQuery(loginCounter.<a href="api/com/exedio/cope/Function.html#greaterOrEqual(E)">greaterOrEqual</a>(8));
q.addOrderBy(loginCounter);
q.addOrderByDescending(email);
List<Customer> customers = q.search();
</pre>
<p>
This query finds all customers,
which have logged in at least 8 times,
sorted by the number of their logins ascendingly
and their email adress descendingly.
</p>
<p>
Note, that all the methods <code>Type.search(...)</code> in the examples before
use an instance of <code>Query</code> in their implementation as well.
They are convenience methods for the most common and simple queries.
</p>
<h3 id="limits">Limits</h3>
<p>
Sometimes you want to fetch just a part of the result of a query.
This is commonly needed for paging.
The following query gets the third page of a list of all customers,
which have logged in at least 8 times.
Each page contains 10 customers:
</p>
<pre>
Query<Customer> q =
TYPE.newQuery(loginCounter.<a href="api/com/exedio/cope/Function.html#greaterOrEqual(E)">greaterOrEqual</a>(8));
q.setLimit(20, 10);
List<Customer> customers = q.search();
</pre>
<p>
Sometimes you may just want to have the first element of the result.
Then you would probably write something like this:
</p>
<pre>
Query<Customer> q =
TYPE.newQuery(loginCounter.<a href="api/com/exedio/cope/Function.html#greaterOrEqual(E)">greaterOrEqual</a>(8));
q.setLimit(0, 1);
List<Customer> customers = q.search();
</pre>
<p>
After such a one-line-only query,
there often some code like this:
</p>
<pre>
if(customers.size()==0)
return null;
else if(customers.size()==1)
return customers.get(0);
else
throw new RuntimeException("oops");
</pre>
<p>
There is a convenience method <a href="api/com/exedio/cope/Query.html#searchSingleton()">searchSingleton</a>,
which avoids writing such code over and over again:
</p>
<pre>
Query<Customer> q =
TYPE.newQuery(loginCounter.<a href="api/com/exedio/cope/Function.html#greaterOrEqual(E)">greaterOrEqual</a>(8));
q.setLimit(0, 1);
return q.<a href="api/com/exedio/cope/Query.html#searchSingleton()">searchSingleton</a>();
</pre>
<p>
This method is useful, whenever you are sure,
that the result set can have at most one element.
This happens if
you use limits as above or
you restrict your query to match a <i>unique</i> field or
when using <i>aggregators</i> as explained below.
</p>
<h3 id="selecting">Selecting and Aggregating</h3>
<p>
Sometimes you just want to deal with values of fields, not items.
For instance you might want to have a list of the email addresses of
all customers:
</p>
<pre>
Query<String> q = new Query<String>(email);
List<String> emails = q.search();
</pre>
<p>
Of course you can combine this with sorting and limits.
To get a list of the greatest 10 login counters you could write:
</p>
<pre>
Query<Integer> q = new Query<Integer>(loginCounter);
q.setOrderBy(loginCounter, true);
q.setLimit(0, 10);
List<Integer> loginCounters = q.search();
</pre>
<p>
Or you might want to compute the sum of the login counters
of all customers.
This is called aggregating:
</p>
<pre>
Query<Integer> q =
new Query<Integer>(loginCounter.<b>sum()</b>);
Integer totalLoginCounters = q.searchSingleton();
</pre>
<p>
Aggregates can be used for getting minimum and maximum as well.
The following example queries the greatest login counter:
</p>
<pre>
Query<Integer> q =
new Query<Integer>(loginCounter.<b>max()</b>);
Integer maximumLoginCounter = q.searchSingleton();
</pre>
<h3 id="joins">Joins</h3>
<p>
To demonstrate joins with exedio persistence,
we have to define another persistent class:
</p>
<pre>
class Order extends <a href="api/com/exedio/cope/Item.html">Item</a>
{
static final <a href="api/com/exedio/cope/ItemField.html">ItemField</a><Customer> buyer =
ItemField.<a href="api/com/exedio/cope/ItemField.html#create(java.lang.Class)">create</a>(Customer.class);
static final <a href="api/com/exedio/cope/DoubleField.html">DoubleField</a> total =
new <a href="api/com/exedio/cope/DoubleField.html#<init>()">DoubleField</a>();
}
</pre>
<p>
Now we can use a join to search for all orders,
where the customer has a email address
from the .com top level domain:
</p>
<pre>
Query<Order> q = Order.TYPE.newQuery(Customer.email.endsWith(".com"));
q.join(Customer.TYPE, Order.buyer.equal(Customer.TYPE.getThis()));
List<Order> orders = q.search();
</pre>
<p>
Note the join condition in the second line,
it specifies a <i>natural join</i> on the item field <code>buyer</code>.
The join condition can also be specified in a short form,
using a convenience method:
</p>
<pre>
q.join(Customer.TYPE, Order.buyer.<b>equalTarget()</b>);
</pre>
<h3 id="further">Further Reading</h3>
<p>
This was the searching trail of the tour.
You may now proceed to trails:
</p>
<ul>
<li>
<a href="transactions.html">Transactions Trail</a>
tells you how to use transactions.
</li>
<li>
<a href="fields2.html">Field Reloaded Trail</a>
covers all the more specific possibilities to store data with exedio persistence.
</li>
<li>
<a href="webapplications.html">Web Application Trail</a>
shows you the little differences when using the framework within a web container.
</li>
</ul>
</section>
</div>
<!-- FOOTER -->
<div id="footer_wrap" class="outer">
<footer class="inner">
<p>
Maintained by
<a href="https://www.exedio.com/" target="_top">exedio</a>
Gesellschaft für Softwareentwicklung mbH.
</p>
<a href="https://validator.w3.org/check?uri=referer" class="validhtml">Valid HTML5</a>
<p>
Slate theme by <a href="https://github.com/jasoncostello">Jason Costello</a>.
Published with <a href="https://pages.github.com">GitHub Pages</a>.
</p>
</footer>
</div>
</body>
</html>