KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sleepycat > persist > EntityJoin


1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 2002,2006 Oracle. All rights reserved.
5  *
6  * $Id: EntityJoin.java,v 1.7 2006/10/30 21:14:30 bostic Exp $
7  */

8
9 package com.sleepycat.persist;
10
11 import java.util.ArrayList JavaDoc;
12 import java.util.Iterator JavaDoc;
13 import java.util.List JavaDoc;
14
15 import com.sleepycat.bind.EntityBinding;
16 import com.sleepycat.bind.EntryBinding;
17 import com.sleepycat.je.Cursor;
18 import com.sleepycat.je.CursorConfig;
19 import com.sleepycat.je.Database;
20 import com.sleepycat.je.DatabaseEntry;
21 import com.sleepycat.je.DatabaseException;
22 import com.sleepycat.je.JoinCursor;
23 import com.sleepycat.je.LockMode;
24 import com.sleepycat.je.OperationStatus;
25 import com.sleepycat.je.Transaction;
26
27 /**
28  * Performs an equality join on two or more secondary keys.
29  *
30  * <p>{@code EntityJoin} objects are thread-safe. Multiple threads may safely
31  * call the methods of a shared {@code EntityJoin} object.</p>
32  *
33  * <p>An equality join is a match on all entities in a given primary index that
34  * have two or more specific secondary key values. Note that key ranges may
35  * not be matched by an equality join, only exact keys are matched.</p>
36  *
37  * <p>For example:</p>
38  * <pre class="code">
39  * // Index declarations -- see {@link <a HREF="package-summary.html#example">package summary example</a>}.
40  * //
41  * {@literal PrimaryIndex<String,Person> personBySsn;}
42  * {@literal SecondaryIndex<String,String,Person> personByParentSsn;}
43  * {@literal SecondaryIndex<Long,String,Person> personByEmployerIds;}
44  * Employer employer = ...;
45  *
46  * // Match on all Person objects having parentSsn "111-11-1111" and also
47  * // containing an employerId of employer.id. In other words, match on all
48  * // of Bob's children that work for a given employer.
49  * //
50  * {@literal EntityJoin<String,Person> join = new EntityJoin(personBySsn);}
51  * join.addCondition(personByParentSsn, "111-11-1111");
52  * join.addCondition(personByEmployerIds, employer.id);
53  *
54  * // Perform the join operation by traversing the results with a cursor.
55  * //
56  * {@literal ForwardCursor<Person> results = join.entities();}
57  * try {
58  * for (Person person : results) {
59  * System.out.println(person.ssn + ' ' + person.name);
60  * }
61  * } finally {
62  * results.close();
63  * }</pre>
64  *
65  * @author Mark Hayes
66  */

67 public class EntityJoin<PK,E> {
68
69     private PrimaryIndex<PK,E> primary;
70     private List JavaDoc<Condition> conditions;
71
72     /**
73      * Creates a join object for a given primary index.
74      *
75      * @param index the primary index on which the join will operate.
76      */

77     public EntityJoin(PrimaryIndex<PK,E> index) {
78         primary = index;
79         conditions = new ArrayList JavaDoc<Condition>();
80     }
81
82     /**
83      * Adds a secondary key condition to the equality join. Only entities
84      * having the given key value in the given secondary index will be returned
85      * by the join operation.
86      *
87      * @param index the secondary index containing the given key value.
88      *
89      * @param key the key value to match during the join.
90      */

91     public <SK> void addCondition(SecondaryIndex<SK,PK,E> index, SK key) {
92
93         /* Make key entry. */
94         DatabaseEntry keyEntry = new DatabaseEntry();
95         index.getKeyBinding().objectToEntry(key, keyEntry);
96
97         /* Use keys database if available. */
98         Database db = index.getKeysDatabase();
99         if (db == null) {
100             db = index.getDatabase();
101         }
102
103         /* Add condition. */
104         conditions.add(new Condition(db, keyEntry));
105     }
106
107     /**
108      * Opens a cursor that returns the entities qualifying for the join. The
109      * join operation is performed as the returned cursor is accessed.
110      *
111      * <p>The operations performed with the cursor will not be transaction
112      * protected, and {@link CursorConfig#DEFAULT} is used implicitly.</p>
113      *
114      * @return the cursor.
115      *
116      * @throws IllegalStateException if less than two conditions were added.
117      */

118     public ForwardCursor<E> entities()
119         throws DatabaseException {
120
121         return entities(null, null);
122     }
123
124     /**
125      * Opens a cursor that returns the entities qualifying for the join. The
126      * join operation is performed as the returned cursor is accessed.
127      *
128      * @param txn the transaction used to protect all operations performed with
129      * the cursor, or null if the operations should not be transaction
130      * protected.
131      *
132      * @param config the cursor configuration that determines the default lock
133      * mode used for all cursor operations, or null to implicitly use {@link
134      * CursorConfig#DEFAULT}.
135      *
136      * @return the cursor.
137      *
138      * @throws IllegalStateException if less than two conditions were added.
139      */

140     public ForwardCursor<E> entities(Transaction txn, CursorConfig config)
141         throws DatabaseException {
142
143         return new JoinForwardCursor<E>(txn, config, false);
144     }
145
146     /**
147      * Opens a cursor that returns the primary keys of entities qualifying for
148      * the join. The join operation is performed as the returned cursor is
149      * accessed.
150      *
151      * <p>The operations performed with the cursor will not be transaction
152      * protected, and {@link CursorConfig#DEFAULT} is used implicitly.</p>
153      *
154      * @return the cursor.
155      *
156      * @throws IllegalStateException if less than two conditions were added.
157      */

158     public ForwardCursor<PK> keys()
159         throws DatabaseException {
160
161         return keys(null, null);
162     }
163
164     /**
165      * Opens a cursor that returns the primary keys of entities qualifying for
166      * the join. The join operation is performed as the returned cursor is
167      * accessed.
168      *
169      * @param txn the transaction used to protect all operations performed with
170      * the cursor, or null if the operations should not be transaction
171      * protected.
172      *
173      * @param config the cursor configuration that determines the default lock
174      * mode used for all cursor operations, or null to implicitly use {@link
175      * CursorConfig#DEFAULT}.
176      *
177      * @return the cursor.
178      *
179      * @throws IllegalStateException if less than two conditions were added.
180      */

181     public ForwardCursor<PK> keys(Transaction txn, CursorConfig config)
182         throws DatabaseException {
183
184         return new JoinForwardCursor<PK>(txn, config, true);
185     }
186
187     private static class Condition {
188
189         private Database db;
190         private DatabaseEntry key;
191
192         Condition(Database db, DatabaseEntry key) {
193             this.db = db;
194             this.key = key;
195         }
196
197         Cursor openCursor(Transaction txn, CursorConfig config)
198             throws DatabaseException {
199
200             OperationStatus status;
201             Cursor cursor = db.openCursor(txn, config);
202             try {
203                 DatabaseEntry data = BasicIndex.NO_RETURN_ENTRY;
204                 status = cursor.getSearchKey(key, data, null);
205             } catch (DatabaseException e) {
206                 try {
207                     cursor.close();
208                 } catch (DatabaseException ignored) {}
209                 throw e;
210             }
211             if (status == OperationStatus.SUCCESS) {
212                 return cursor;
213             } else {
214                 cursor.close();
215                 return null;
216             }
217         }
218     }
219
220     private class JoinForwardCursor<V> implements ForwardCursor<V> {
221
222         private Cursor[] cursors;
223         private JoinCursor joinCursor;
224         private boolean doKeys;
225
226         JoinForwardCursor(Transaction txn, CursorConfig config, boolean doKeys)
227             throws DatabaseException {
228
229             this.doKeys = doKeys;
230             try {
231                 cursors = new Cursor[conditions.size()];
232                 for (int i = 0; i < cursors.length; i += 1) {
233                     Condition cond = conditions.get(i);
234                     Cursor cursor = cond.openCursor(txn, config);
235                     if (cursor == null) {
236                         /* Leave joinCursor null. */
237                         doClose(null);
238                         return;
239                     }
240                     cursors[i] = cursor;
241                 }
242                 joinCursor = primary.getDatabase().join(cursors, null);
243             } catch (DatabaseException e) {
244                 /* doClose will throw e. */
245                 doClose(e);
246             }
247         }
248
249         public V next()
250             throws DatabaseException {
251             
252             return next(null);
253         }
254
255         public V next(LockMode lockMode)
256             throws DatabaseException {
257
258             if (joinCursor == null) {
259                 return null;
260             }
261             if (doKeys) {
262                 DatabaseEntry key = new DatabaseEntry();
263                 OperationStatus status = joinCursor.getNext(key, lockMode);
264                 if (status == OperationStatus.SUCCESS) {
265                     EntryBinding binding = primary.getKeyBinding();
266                     return (V) binding.entryToObject(key);
267                 }
268             } else {
269                 DatabaseEntry key = new DatabaseEntry();
270                 DatabaseEntry data = new DatabaseEntry();
271                 OperationStatus status =
272                     joinCursor.getNext(key, data, lockMode);
273                 if (status == OperationStatus.SUCCESS) {
274                     EntityBinding binding = primary.getEntityBinding();
275                     return (V) binding.entryToObject(key, data);
276                 }
277             }
278             return null;
279         }
280
281         public Iterator JavaDoc<V> iterator() {
282             return iterator(null);
283         }
284
285         public Iterator JavaDoc<V> iterator(LockMode lockMode) {
286             return new BasicIterator<V>(this, lockMode);
287         }
288
289         public void close()
290             throws DatabaseException {
291
292             doClose(null);
293         }
294
295         private void doClose(DatabaseException firstException)
296             throws DatabaseException {
297
298             if (joinCursor != null) {
299                 try {
300                     joinCursor.close();
301                     joinCursor = null;
302                 } catch (DatabaseException e) {
303                     if (firstException == null) {
304                         firstException = e;
305                     }
306                 }
307             }
308             for (int i = 0; i < cursors.length; i += 1) {
309                 Cursor cursor = cursors[i];
310                 if (cursor != null) {
311                     try {
312                         cursor.close();
313                         cursors[i] = null;
314                     } catch (DatabaseException e) {
315                         if (firstException == null) {
316                             firstException = e;
317                         }
318                     }
319                 }
320             }
321             if (firstException != null) {
322                 throw firstException;
323             }
324         }
325     }
326 }
327
Popular Tags