KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 2002,2006 Oracle. All rights reserved.
5  *
6  * $Id: SecondaryIndex.java,v 1.14 2006/12/04 18:52:56 linda Exp $
7  */

8
9 package com.sleepycat.persist;
10
11 import java.util.Map JavaDoc;
12 import java.util.SortedMap JavaDoc;
13
14 import com.sleepycat.bind.EntityBinding;
15 import com.sleepycat.bind.EntryBinding;
16 import com.sleepycat.collections.StoredSortedMap;
17 import com.sleepycat.je.Database;
18 import com.sleepycat.je.DatabaseConfig;
19 import com.sleepycat.je.DatabaseEntry;
20 import com.sleepycat.je.DatabaseException;
21 import com.sleepycat.je.LockMode;
22 import com.sleepycat.je.OperationStatus;
23 import com.sleepycat.je.SecondaryDatabase;
24 import com.sleepycat.je.Transaction;
25 import com.sleepycat.persist.model.DeleteAction;
26 import com.sleepycat.persist.model.Relationship;
27 import com.sleepycat.persist.model.SecondaryKey;
28
29 /**
30  * The secondary index for an entity class and a secondary key.
31  *
32  * <p>{@code SecondaryIndex} objects are thread-safe. Multiple threads may
33  * safely call the methods of a shared {@code SecondaryIndex} object.</p>
34  *
35  * <p>{@code SecondaryIndex} implements {@link EntityIndex} to map the
36  * secondary key type (SK) to the entity type (E). In other words, entities
37  * are accessed by secondary key values.</p>
38  *
39  * <p>The {@link SecondaryKey} annotation may be used to define a secondary key
40  * as shown in the following example.</p>
41  *
42  * <pre class="code">
43  * {@literal @Entity}
44  * class Employee {
45  *
46  * {@literal @PrimaryKey}
47  * long id;
48  *
49  * {@literal @SecondaryKey(relate=MANY_TO_ONE)}
50  * String department;
51  *
52  * String name;
53  *
54  * private Employee() {}
55  * }</pre>
56  *
57  * <p>Before obtaining a {@code SecondaryIndex}, the {@link PrimaryIndex} must
58  * be obtained for the entity class. To obtain the {@code SecondaryIndex} call
59  * {@link EntityStore#getSecondaryIndex EntityStore.getSecondaryIndex}, passing
60  * the primary index, the secondary key class and the secondary key name. For
61  * example:</p>
62  *
63  * <pre class="code">
64  * EntityStore store = new EntityStore(...);
65  *
66  * {@code PrimaryIndex<Long,Employee>} primaryIndex =
67  * store.getPrimaryIndex(Long.class, Employee.class);
68  *
69  * {@code SecondaryIndex<String,Long,Employee>} secondaryIndex =
70  * store.getSecondaryIndex(primaryIndex, String.class, "department");</pre>
71  *
72  * <p>Since {@code SecondaryIndex} implements the {@link EntityIndex}
73  * interface, it shares the common index methods for retrieving and deleting
74  * entities, opening cursors and using transactions. See {@link EntityIndex}
75  * for more information on these topics.</p>
76  *
77  * <p>{@code SecondaryIndex} does <em>not</em> provide methods for inserting
78  * and updating entities. That must be done using the {@link
79  * PrimaryIndex}.</p>
80  *
81  * <p>Note that a {@code SecondaryIndex} has three type parameters {@code
82  * <SK,PK,E>} or in the example {@code <String,Long,Employee>} while a {@link
83  * PrimaryIndex} has only two type parameters {@code <PK,E>} or {@code
84  * <Long,Employee>}. This is because a {@code SecondaryIndex} has an extra
85  * level of mapping: It maps from secondary key to primary key, and then from
86  * primary key to entity. For example, consider this entity:</p>
87  *
88  * <p><table class="code" border="1">
89  * <tr><th>ID</th><th>Department</th><th>Name</th></tr>
90  * <tr><td>1</td><td>Engineering</td><td>Jane Smith</td></tr>
91  * </table></p>
92  *
93  * <p>The {@link PrimaryIndex} maps from id directly to the entity, or from
94  * primary key 1 to the "Jane Smith" entity in the example. The {@code
95  * SecondaryIndex} maps from department to id, or from secondary key
96  * "Engineering" to primary key 1 in the example, and then uses the {@code
97  * PrimaryIndex} to map from the primary key to the entity.</p>
98  *
99  * <p>Because of this extra type parameter and extra level of mapping, a {@code
100  * SecondaryIndex} can provide more than one mapping, or view, of the entities
101  * in the primary index. The main mapping of a {@code SecondaryIndex} is to
102  * map from secondary key (SK) to entity (E), or in the example, from the
103  * String department key to the Employee entity. The {@code SecondaryIndex}
104  * itself, by implementing {@code EntityIndex<SK,E>}, provides this
105  * mapping.</p>
106  *
107  * <p>The second mapping provided by {@code SecondaryIndex} is from secondary
108  * key (SK) to primary key (PK), or in the example, from the String department
109  * key to the Long id key. The {@link #keysIndex} method provides this
110  * mapping. When accessing the keys index, the primary key is returned rather
111  * than the entity. When only the primary key is needed and not the entire
112  * entity, using the keys index is less expensive than using the secondary
113  * index because the primary index does not have to be accessed.</p>
114  *
115  * <p>The third mapping provided by {@code SecondaryIndex} is from primary key
116  * (PK) to entity (E), for the subset of entities having a given secondary key
117  * (SK). This mapping is provided by the {@link #subIndex} method. A
118  * sub-index is convenient when you are interested in working with the subset
119  * of entities having a particular secondary key value, for example, all
120  * employees in a given department.</p>
121  *
122  * <p>All three mappings, along with the mapping provided by the {@link
123  * PrimaryIndex}, are shown using example data in the {@link EntityIndex}
124  * interface documentation. See {@link EntityIndex} for more information.</p>
125  *
126  * <p>Note that when using an index, keys and values are stored and retrieved
127  * by value not by reference. In other words, if an entity object is stored
128  * and then retrieved, or retrieved twice, each object will be a separate
129  * instance. For example, in the code below the assertion will always
130  * fail.</p>
131  * <pre class="code">
132  * MyKey key = ...;
133  * MyEntity entity1 = index.get(key);
134  * MyEntity entity2 = index.get(key);
135  * assert entity1 == entity2; // always fails!
136  * </pre>
137  *
138  * <h3>One-to-One Relationships</h3>
139  *
140  * <p>A {@link Relationship#ONE_TO_ONE ONE_TO_ONE} relationship, although less
141  * common than other types of relationships, is the simplest type of
142  * relationship. A single entity is related to a single secondary key value.
143  * For example:</p>
144  *
145  * <pre class="code">
146  * {@literal @Entity}
147  * class Employee {
148  *
149  * {@literal @PrimaryKey}
150  * long id;
151  *
152  * {@literal @SecondaryKey(relate=ONE_TO_ONE)}
153  * String ssn;
154  *
155  * String name;
156  *
157  * private Employee() {}
158  * }
159  *
160  * {@code SecondaryIndex<String,Long,Employee>} employeeBySsn =
161  * store.getSecondaryIndex(primaryIndex, String.class, "ssn");</pre>
162  *
163  * <p>With a {@link Relationship#ONE_TO_ONE ONE_TO_ONE} relationship, the
164  * secondary key must be unique; in other words, no two entities may have the
165  * same secondary key value. If an attempt is made to store an entity having
166  * the same secondary key value as another existing entity, a {@link
167  * DatabaseException} will be thrown.</p>
168  *
169  * <p>Because the secondary key is unique, it is useful to lookup entities by
170  * secondary key using {@link EntityIndex#get}. For example:</p>
171  *
172  * <pre class="code">
173  * Employee employee = employeeBySsn.get(mySsn);</pre>
174  *
175  * <h3>Many-to-One Relationships</h3>
176  *
177  * <p>A {@link Relationship#MANY_TO_ONE MANY_TO_ONE} relationship is the most
178  * common type of relationship. One or more entities is related to a single
179  * secondary key value. For example:</p>
180  *
181  * <pre class="code">
182  * {@literal @Entity}
183  * class Employee {
184  *
185  * {@literal @PrimaryKey}
186  * long id;
187  *
188  * {@literal @SecondaryKey(relate=MANY_TO_ONE)}
189  * String department;
190  *
191  * String name;
192  *
193  * private Employee() {}
194  * }
195  *
196  * {@code SecondaryIndex<String,Long,Employee>} employeeByDepartment =
197  * store.getSecondaryIndex(primaryIndex, String.class, "department");</pre>
198  *
199  * <p>With a {@link Relationship#MANY_TO_ONE MANY_TO_ONE} relationship, the
200  * secondary key is not required to be unique; in other words, more than one
201  * entity may have the same secondary key value. In this example, more than
202  * one employee may belong to the same department.</p>
203  *
204  * <p>The most convenient way to access the employees in a given department is
205  * by using a sub-index. For example:</p>
206  *
207  * <pre class="code">
208  * {@code EntityIndex<Long,Entity>} subIndex = employeeByDepartment.subIndex(myDept);
209  * {@code EntityCursor<Employee>} cursor = subIndex.entities();
210  * try {
211  * for (Employee entity : cursor) {
212  * // Do something with the entity...
213  * }
214  * } finally {
215  * cursor.close();
216  * }</pre>
217  *
218  * <h3>One-to-Many Relationships</h3>
219  *
220  * <p>In a {@link Relationship#ONE_TO_MANY ONE_TO_MANY} relationship, a single
221  * entity is related to one or more secondary key values. For example:</p>
222  *
223  * <pre class="code">
224  * {@literal @Entity}
225  * class Employee {
226  *
227  * {@literal @PrimaryKey}
228  * long id;
229  *
230  * {@literal @SecondaryKey(relate=ONE_TO_MANY)}
231  * {@literal Set<String> emailAddresses = new HashSet<String>;}
232  *
233  * String name;
234  *
235  * private Employee() {}
236  * }
237  *
238  * {@code SecondaryIndex<String,Long,Employee>} employeeByEmail =
239  * store.getSecondaryIndex(primaryIndex, String.class, "emailAddresses");</pre>
240  *
241  * <p>With a {@link Relationship#ONE_TO_MANY ONE_TO_MANY} relationship, the
242  * secondary key must be unique; in other words, no two entities may have the
243  * same secondary key value. In this example, no two employees may have the
244  * same email address. If an attempt is made to store an entity having the
245  * same secondary key value as another existing entity, a {@link
246  * DatabaseException} will be thrown.</p>
247  *
248  * <p>Because the secondary key is unique, it is useful to lookup entities by
249  * secondary key using {@link EntityIndex#get}. For example:</p>
250  *
251  * <pre class="code">
252  * Employee employee = employeeByEmail.get(myEmailAddress);</pre>
253  *
254  * <p>The secondary key field for a {@link Relationship#ONE_TO_MANY
255  * ONE_TO_MANY} relationship must be an array or collection type. To access
256  * the email addresses of an employee, simply access the collection field
257  * directly. For example:</p>
258  *
259  * <pre class="code">
260  * Employee employee = primaryIndex.get(1); // Get the entity by primary key
261  * employee.emailAddresses.add(myNewEmail); // Add an email address
262  * primaryIndex.putNoReturn(1, employee); // Update the entity</pre>
263  *
264  * <h3>Many-to-Many Relationships</h3>
265  *
266  * <p>In a {@link Relationship#MANY_TO_MANY MANY_TO_MANY} relationship, one
267  * or more entities is related to one or more secondary key values. For
268  * example:</p>
269  *
270  * <pre class="code">
271  * {@literal @Entity}
272  * class Employee {
273  *
274  * {@literal @PrimaryKey}
275  * long id;
276  *
277  * {@literal @SecondaryKey(relate=MANY_TO_MANY)}
278  * {@literal Set<String> organizations = new HashSet<String>;}
279  *
280  * String name;
281  *
282  * private Employee() {}
283  * }
284  *
285  * {@code SecondaryIndex<String,Long,Employee>} employeeByOrganization =
286  * store.getSecondaryIndex(primaryIndex, String.class, "organizations");</pre>
287  *
288  * <p>With a {@link Relationship#MANY_TO_MANY MANY_TO_MANY} relationship, the
289  * secondary key is not required to be unique; in other words, more than one
290  * entity may have the same secondary key value. In this example, more than
291  * one employee may belong to the same organization.</p>
292  *
293  * <p>The most convenient way to access the employees in a given organization
294  * is by using a sub-index. For example:</p>
295  *
296  * <pre class="code">
297  * {@code EntityIndex<Long,Entity>} subIndex = employeeByOrganization.subIndex(myOrg);
298  * {@code EntityCursor<Employee>} cursor = subIndex.entities();
299  * try {
300  * for (Employee entity : cursor) {
301  * // Do something with the entity...
302  * }
303  * } finally {
304  * cursor.close();
305  * }</pre>
306  *
307  * <p>The secondary key field for a {@link Relationship#MANY_TO_MANY
308  * MANY_TO_MANY} relationship must be an array or collection type. To access
309  * the organizations of an employee, simply access the collection field
310  * directly. For example:</p>
311  *
312  * <pre class="code">
313  * Employee employee = primaryIndex.get(1); // Get the entity by primary key
314  * employee.organizations.remove(myOldOrg); // Remove an organization
315  * primaryIndex.putNoReturn(1, employee); // Update the entity</pre>
316  *
317  * <h3>Foreign Key Constraints for Related Entities</h3>
318  *
319  * <p>In all the examples above the secondary key is treated only as a simple
320  * value, such as a {@code String} department field. In many cases, that is
321  * sufficient. But in other cases, you may wish to constrain the secondary
322  * keys of one entity class to be valid primary keys of another entity
323  * class. For example, a Department entity may also be defined:</p>
324  *
325  * <pre class="code">
326  * {@literal @Entity}
327  * class Department {
328  *
329  * {@literal @PrimaryKey}
330  * String name;
331  *
332  * String missionStatement;
333  *
334  * private Department() {}
335  * }</pre>
336  *
337  * <p>You may wish to constrain the department field values of the Employee
338  * class in the examples above to be valid primary keys of the Department
339  * entity class. In other words, you may wish to ensure that the department
340  * field of an Employee will always refer to a valid Department entity.</p>
341  *
342  * <p>You can implement this constraint yourself by validating the department
343  * field before you store an Employee. For example:</p>
344  *
345  * <pre class="code">
346  * {@code PrimaryIndex<String,Department>} departmentIndex =
347  * store.getPrimaryIndex(String.class, Department.class);
348  *
349  * void storeEmployee(Employee employee) throws DatabaseException {
350  * if (departmentIndex.contains(employee.department)) {
351  * primaryIndex.putNoReturn(employee);
352  * } else {
353  * throw new IllegalArgumentException("Department does not exist: " +
354  * employee.department);
355  * }
356  * }</pre>
357  *
358  * <p>Or, instead you could define the Employee department field as a foreign
359  * key, and this validation will be done for you when you attempt to store the
360  * Employee entity. For example:</p>
361  *
362  * <pre class="code">
363  * {@literal @Entity}
364  * class Employee {
365  *
366  * {@literal @PrimaryKey}
367  * long id;
368  *
369  * {@literal @SecondaryKey(relate=MANY_TO_ONE, relatedEntity=Department.class)}
370  * String department;
371  *
372  * String name;
373  *
374  * private Employee() {}
375  * }</pre>
376  *
377  * <p>The {@code relatedEntity=Department.class} above defines the department
378  * field as a foreign key that refers to a Department entity. Whenever a
379  * Employee entity is stored, its department field value will be checked to
380  * ensure that a Department entity exists with that value as its primary key.
381  * If no such Department entity exists, then a {@link DatabaseException} is
382  * thrown, causing the transaction to be aborted (assuming that transactions
383  * are used).</p>
384  *
385  * <p>This begs the question: What happens when a Department entity is deleted
386  * while one or more Employee entities have department fields that refer to
387  * the deleted department's primary key? If the department were allowed to be
388  * deleted, the foreign key constraint for the Employee department field would
389  * be violated, because the Employee department field would refer to a
390  * department that does not exist.</p>
391  *
392  * <p>By default, when this situation arises the system does not allow the
393  * department to be deleted. Instead, a {@link DatabaseException} is thrown,
394  * causing the transaction to be aborted. In this case, in order to delete a
395  * department, the department field of all Employee entities must first be
396  * updated to refer to a different existing department, or set to null. This
397  * is the responsibility of the application.</p>
398  *
399  * <p>There are two additional ways of handling deletion of a Department
400  * entity. These alternatives are configured using the {@link
401  * SecondaryKey#onRelatedEntityDelete} annotation property. Setting this
402  * property to {@link DeleteAction#NULLIFY} causes the Employee department
403  * field to be automatically set to null when the department they refer to is
404  * deleted. This may or may not be desirable, depending on application
405  * policies. For example:</p>
406  *
407  * <pre class="code">
408  * {@literal @Entity}
409  * class Employee {
410  *
411  * {@literal @PrimaryKey}
412  * long id;
413  *
414  * {@code @SecondaryKey(relate=MANY_TO_ONE, relatedEntity=Department.class,
415  * onRelatedEntityDelete=NULLIFY)}
416  * String department;
417  *
418  * String name;
419  *
420  * private Employee() {}
421  * }</pre>
422  *
423  * <p>The {@link DeleteAction#CASCADE} value, on the other hand, causes the
424  * Employee entities to be automatically deleted when the department they refer
425  * to is deleted. This is probably not desirable in this particular example,
426  * but is useful for parent-child relationships. For example:</p>
427  *
428  * <pre class="code">
429  * {@literal @Entity}
430  * class Order {
431  *
432  * {@literal @PrimaryKey}
433  * long id;
434  *
435  * String description;
436  *
437  * private Order() {}
438  * }
439  *
440  * {@literal @Entity}
441  * class OrderItem {
442  *
443  * {@literal @PrimaryKey}
444  * long id;
445  *
446  * {@code @SecondaryKey(relate=MANY_TO_ONE, relatedEntity=Order.class,
447  * onRelatedEntityDelete=CASCADE)}
448  * long orderId;
449  *
450  * String description;
451  *
452  * private OrderItem() {}
453  * }</pre>
454  *
455  * <p>The OrderItem orderId field refers to its "parent" Order entity. When an
456  * Order entity is deleted, it may be useful to automatically delete its
457  * "child" OrderItem entities.</p>
458  *
459  * <p>For more information, see {@link SecondaryKey#onRelatedEntityDelete}.</p>
460  *
461  * <h3>One-to-Many versus Many-to-One for Related Entities</h3>
462  *
463  * <p>When there is a conceptual Many-to-One relationship such as Employee to
464  * Department as illustrated in the examples above, the relationship may be
465  * implemented either as Many-to-One in the Employee class or as One-to-Many in
466  * the Department class.</p>
467  *
468  * <p>Here is the Many-to-One approach.</p>
469  *
470  * <pre class="code">
471  * {@literal @Entity}
472  * class Employee {
473  *
474  * {@literal @PrimaryKey}
475  * long id;
476  *
477  * {@literal @SecondaryKey(relate=MANY_TO_ONE, relatedEntity=Department.class)}
478  * String department;
479  *
480  * String name;
481  *
482  * private Employee() {}
483  * }
484  *
485  * {@literal @Entity}
486  * class Department {
487  *
488  * {@literal @PrimaryKey}
489  * String name;
490  *
491  * String missionStatement;
492  *
493  * private Department() {}
494  * }</pre>
495  *
496  * <p>And here is the One-to-Many approach.</p>
497  *
498  * <pre class="code">
499  * {@literal @Entity}
500  * class Employee {
501  *
502  * {@literal @PrimaryKey}
503  * long id;
504  *
505  * String name;
506  *
507  * private Employee() {}
508  * }
509  *
510  * {@literal @Entity}
511  * class Department {
512  *
513  * {@literal @PrimaryKey}
514  * String name;
515  *
516  * String missionStatement;
517  *
518  * {@literal @SecondaryKey(relate=ONE_TO_MANY, relatedEntity=Employee.class)}
519  * {@literal Set<Long> employees = new HashSet<Long>;}
520  *
521  * private Department() {}
522  * }</pre>
523  *
524  * <p>Which approach is best? The Many-to-One approach better handles large
525  * number of entities on the to-Many side of the relationship because it
526  * doesn't store a collection of keys as an entity field. With Many-to-One a
527  * Btree is used to store the collection of keys and the Btree can easily
528  * handle very large numbers of keys. With One-to-Many, each time a related
529  * key is added or removed the entity on the One side of the relationship,
530  * along with the complete collection of related keys, must be updated.
531  * Therefore, if large numbers of keys may be stored per relationship,
532  * Many-to-One is recommended.</p>
533  *
534  * <p>If the number of entities per relationship is not a concern, then you may
535  * wish to choose the approach that is most natural in your application data
536  * model. For example, if you think of a Department as containing employees
537  * and you wish to modify the Department object each time an employee is added
538  * or removed, then you may wish to store a collection of Employee keys in the
539  * Department object (One-to-Many).</p>
540  *
541  * <p>Note that if you have a One-to-Many relationship and there is no related
542  * entity, then you don't have a choice -- you have to use One-to-Many because
543  * there is no entity on the to-Many side of the relationship where a
544  * Many-to-One key could be defined. An example is the Employee to email
545  * addresses relationship discussed above:</p>
546  *
547  * <pre class="code">
548  * {@literal @Entity}
549  * class Employee {
550  *
551  * {@literal @PrimaryKey}
552  * long id;
553  *
554  * {@literal @SecondaryKey(relate=ONE_TO_MANY)}
555  * {@literal Set<String> emailAddresses = new HashSet<String>;}
556  *
557  * String name;
558  *
559  * private Employee() {}
560  * }</pre>
561  *
562  * <p>For sake of argument imagine that each employee has thousands of email
563  * addresses and employees frequently add and remove email addresses. To
564  * avoid the potential performance problems associated with updating the
565  * Employee entity every time an email address is added or removed, you could
566  * create an EmployeeEmailAddress entity and use a Many-to-One relationship as
567  * shown below:</p>
568  *
569  * <pre class="code">
570  * {@literal @Entity}
571  * class Employee {
572  *
573  * {@literal @PrimaryKey}
574  * long id;
575  *
576  * String name;
577  *
578  * private Employee() {}
579  * }
580  *
581  * {@literal @Entity}
582  * class EmployeeEmailAddress {
583  *
584  * {@literal @PrimaryKey}
585  * String emailAddress;
586  *
587  * {@literal @SecondaryKey(relate=MANY_TO_ONE, relatedEntity=Employee.class)}
588  * long employeeId;
589  *
590  * private EmployeeEmailAddress() {}
591  * }</pre>
592  *
593  * <h3>Key Placement with Many-to-Many for Related Entities</h3>
594  *
595  * <p>As discussed in the section above, one drawback of a to-Many relationship
596  * (One-to-Many was discussed above and Many-to-Many is discussed here) is that
597  * it requires storing a collection of keys in an entity. Each time a key is
598  * added or removed, the containing entity must be updated. This has potential
599  * performance problems when there are large numbers of entities on the to-Many
600  * side of the relationship, in other words, when there are large numbers of
601  * keys in each secondary key field collection.</p>
602  *
603  * <p>If you have a Many-to-Many relationship with a reasonably small number of
604  * entities on one side of the relationship and a large number of entities on
605  * the other side, you can avoid the potential performance problems by defining
606  * the secondary key field on the side with a small number of entities.</p>
607  *
608  * <p>For example, in an Employee-to-Organization relationship, the number of
609  * organizations per employee will normally be reasonably small but the number
610  * of employees per organization may be very large. Therefore, to avoid
611  * potential performance problems, the secondary key field should be defined in
612  * the Employee class as shown below.</p>
613  *
614  * <pre class="code">
615  * {@literal @Entity}
616  * class Employee {
617  *
618  * {@literal @PrimaryKey}
619  * long id;
620  *
621  * {@literal @SecondaryKey(relate=MANY_TO_MANY, relatedEntity=Organization.class)}
622  * {@literal Set<String> organizations = new HashSet<String>;}
623  *
624  * String name;
625  *
626  * private Employee() {}
627  * }
628  *
629  * {@literal @Entity}
630  * class Organization {
631  *
632  * {@literal @PrimaryKey}
633  * String name;
634  *
635  * String description;
636  * }</pre>
637  *
638  * <p>If instead a {@code Set<Long> members} key had been defined in the
639  * Organization class, this set could potentially have a large number of
640  * elements and performance problems could result.</p>
641  *
642  * <h3>Many-to-Many Versus a Relationship Entity</h3>
643  *
644  * <p>If you have a Many-to-Many relationship with a large number of entities
645  * on <em>both</em> sides of the relationship, you can avoid the potential
646  * performance problems by using a <em>relationship entity</em>. A
647  * relationship entity defines the relationship between two other entities
648  * using two Many-to-One relationships.</p>
649  *
650  * <p>Imagine a relationship between cars and trucks indicating whenever a
651  * particular truck was passed on the road by a particular car. A given car
652  * may pass a large number of trucks and a given truck may be passed by a large
653  * number of cars. First look at a Many-to-Many relationship between these two
654  * entities:</p>
655  *
656  * <pre class="code">
657  * {@literal @Entity}
658  * class Car {
659  *
660  * {@literal @PrimaryKey}
661  * String licenseNumber;
662  *
663  * {@literal @SecondaryKey(relate=MANY_TO_MANY, relatedEntity=Truck.class)}
664  * {@literal Set<String> trucksPassed = new HashSet<String>;}
665  *
666  * String color;
667  *
668  * private Car() {}
669  * }
670  *
671  * {@literal @Entity}
672  * class Truck {
673  *
674  * {@literal @PrimaryKey}
675  * String licenseNumber;
676  *
677  * int tons;
678  *
679  * private Truck() {}
680  * }</pre>
681  *
682  * <p>With the Many-to-Many approach above, the {@code trucksPassed} set could
683  * potentially have a large number of elements and performance problems could
684  * result.</p>
685  *
686  * <p>To apply the relationship entity approach we define a new entity class
687  * named CarPassedTruck representing a single truck passed by a single car. We
688  * remove the secondary key from the Car class and use two secondary keys in
689  * the CarPassedTruck class instead.</p>
690  *
691  * <pre class="code">
692  * {@literal @Entity}
693  * class Car {
694  *
695  * {@literal @PrimaryKey}
696  * String licenseNumber;
697  *
698  * String color;
699  *
700  * private Car() {}
701  * }
702  *
703  * {@literal @Entity}
704  * class Truck {
705  *
706  * {@literal @PrimaryKey}
707  * String licenseNumber;
708  *
709  * int tons;
710  *
711  * private Truck() {}
712  * }
713  *
714  * {@literal @Entity}
715  * class CarPassedTruck {
716  *
717  * {@literal @PrimaryKey}
718  * long id;
719  *
720  * {@literal @SecondaryKey(relate=MANY_TO_ONE, relatedEntity=Car.class)}
721  * String carLicense;
722  *
723  * {@literal @SecondaryKey(relate=MANY_TO_ONE, relatedEntity=Truck.class)}
724  * String truckLicense;
725  *
726  * private CarPassedTruck() {}
727  * }</pre>
728  *
729  * <p>The CarPassedTruck entity can be used to access the relationship by car
730  * license or by truck license.</p>
731  *
732  * <p>You may use the relationship entity approach because of the potential
733  * performance problems mentioned above. Or, you may choose to use this
734  * approach in order to store other information about the relationship. For
735  * example, if for each car that passes a truck you wish to record how much
736  * faster the car was going than the truck, then a relationship entity is the
737  * logical place to store that property. In the example below the
738  * speedDifference property is added to the CarPassedTruck class.</p>
739  *
740  * <pre class="code">
741  * {@literal @Entity}
742  * class CarPassedTruck {
743  *
744  * {@literal @PrimaryKey}
745  * long id;
746  *
747  * {@literal @SecondaryKey(relate=MANY_TO_ONE, relatedEntity=Car.class)}
748  * String carLicense;
749  *
750  * {@literal @SecondaryKey(relate=MANY_TO_ONE, relatedEntity=Truck.class)}
751  * String truckLicense;
752  *
753  * int speedDifference;
754  *
755  * private CarPassedTruck() {}
756  * }</pre>
757  *
758  * <p>Be aware that the relationship entity approach adds overhead compared to
759  * Many-to-Many. There is one additional entity and one additional secondary
760  * key. These factors should be weighed against its advantages and the
761  * relevant application access patterns should be considered.</p>
762  *
763  * @author Mark Hayes
764  */

765 public class SecondaryIndex<SK,PK,E> extends BasicIndex<SK,E> {
766
767     private SecondaryDatabase secDb;
768     private Database keysDb;
769     private PrimaryIndex priIndex;
770     private EntityBinding entityBinding;
771     private EntityIndex<SK,PK> keysIndex;
772     private SortedMap JavaDoc<SK,E> map;
773
774     /**
775      * Creates a secondary index without using an <code>EntityStore</code>.
776      * When using an {@link EntityStore}, call {@link
777      * EntityStore#getSecondaryIndex getSecondaryIndex} instead.
778      *
779      * <p>This constructor is not normally needed and is provided for
780      * applications that wish to use custom bindings along with the Direct
781      * Persistence Layer. Normally, {@link EntityStore#getSecondaryIndex
782      * getSecondaryIndex} is used instead.</p>
783      *
784      * @param database the secondary database used for all access other than
785      * via a {@link #keysIndex}.
786      *
787      * @param keysDatabase another handle on the secondary database, opened
788      * without association to the primary, and used only for access via a
789      * {@link #keysIndex}. If this argument is null and the {@link #keysIndex}
790      * method is called, then the keys database will be opened automatically;
791      * however, the user is then responsible for closing the keys database. To
792      * get the keys database in order to close it, call {@link
793      * #getKeysDatabase}.
794      *
795      * @param primaryIndex the primary index associated with this secondary
796      * index.
797      *
798      * @param secondaryKeyClass the class of the secondary key.
799      *
800      * @param secondaryKeyBinding the binding to be used for secondary keys.
801      */

802     public SecondaryIndex(SecondaryDatabase database,
803                           Database keysDatabase,
804                           PrimaryIndex<PK,E> primaryIndex,
805                           Class JavaDoc<SK> secondaryKeyClass,
806                           EntryBinding secondaryKeyBinding)
807         throws DatabaseException {
808
809         super(database, secondaryKeyClass, secondaryKeyBinding,
810               new EntityValueAdapter(primaryIndex.getEntityClass(),
811                                      primaryIndex.getEntityBinding(),
812                                      true));
813         secDb = database;
814         keysDb = keysDatabase;
815         priIndex = primaryIndex;
816         entityBinding = primaryIndex.getEntityBinding();
817     }
818
819     /**
820      * Returns the underlying secondary database for this index.
821      *
822      * @return the secondary database.
823      */

824     public SecondaryDatabase getDatabase() {
825         return secDb;
826     }
827
828     /**
829      * Returns the underlying secondary database that is not associated with
830      * the primary database and is used for the {@link #keysIndex}.
831      *
832      * @return the keys database.
833      */

834     public Database getKeysDatabase() {
835         return keysDb;
836     }
837
838     /**
839      * Returns the primary index associated with this secondary index.
840      *
841      * @return the primary index.
842      */

843     public PrimaryIndex<PK,E> getPrimaryIndex() {
844         return priIndex;
845     }
846
847     /**
848      * Returns the secondary key class for this index.
849      *
850      * @return the class.
851      */

852     public Class JavaDoc<SK> getKeyClass() {
853         return keyClass;
854     }
855
856     /**
857      * Returns the secondary key binding for the index.
858      *
859      * @return the key binding.
860      */

861     public EntryBinding getKeyBinding() {
862         return keyBinding;
863     }
864
865     /**
866      * Returns a read-only keys index that maps secondary key to primary key.
867      * When accessing the keys index, the primary key is returned rather than
868      * the entity. When only the primary key is needed and not the entire
869      * entity, using the keys index is less expensive than using the secondary
870      * index because the primary index does not have to be accessed.
871      *
872      * <p>Note the following in the unusual case that you are <em>not</em>
873      * using an <code>EntityStore</code>: This method will open the keys
874      * database, a second database handle for the secondary database, if it is
875      * not already open. In this case, if you are <em>not</em> using an
876      * <code>EntityStore</code>, then you are responsible for closing the
877      * database returned by {@link #getKeysDatabase} before closing the
878      * environment. If you <em>are</em> using an <code>EntityStore</code>, the
879      * keys database will be closed automatically by {@link
880      * EntityStore#close}.</p>
881      *
882      * @return the keys index.
883      */

884     public synchronized EntityIndex<SK,PK> keysIndex()
885         throws DatabaseException {
886
887         if (keysIndex == null) {
888             if (keysDb == null) {
889                 DatabaseConfig config = secDb.getConfig();
890                 config.setReadOnly(true);
891                 keysDb = db.getEnvironment().openDatabase
892                     (null, secDb.getDatabaseName(), config);
893             }
894             keysIndex = new KeysIndex<SK,PK>
895                 (keysDb, keyClass, keyBinding,
896                  priIndex.getKeyClass(), priIndex.getKeyBinding());
897         }
898         return keysIndex;
899     }
900
901     /**
902      * Returns an index that maps primary key to entity for the subset of
903      * entities having a given secondary key (duplicates). A sub-index is
904      * convenient when you are interested in working with the subset of
905      * entities having a particular secondary key value.
906      *
907      * <p>When using a {@link Relationship#MANY_TO_ONE MANY_TO_ONE} or {@link
908      * Relationship#MANY_TO_MANY MANY_TO_MANY} secondary key, the sub-index
909      * represents the left (MANY) side of a relationship.</p>
910      *
911      * @param key the secondary key that identifies the entities in the
912      * sub-index.
913      *
914      * @return the sub-index.
915      */

916     public EntityIndex<PK,E> subIndex(SK key)
917         throws DatabaseException {
918
919         return new SubIndex(this, entityBinding, key);
920     }
921
922     /*
923      * Of the EntityIndex methods only get()/map()/sortedMap() are implemented
924      * here. All other methods are implemented by BasicIndex.
925      */

926
927     public E get(SK key)
928         throws DatabaseException {
929
930         return get(null, key, null);
931     }
932
933     public E get(Transaction txn, SK key, LockMode lockMode)
934         throws DatabaseException {
935
936         DatabaseEntry keyEntry = new DatabaseEntry();
937         DatabaseEntry pkeyEntry = new DatabaseEntry();
938         DatabaseEntry dataEntry = new DatabaseEntry();
939         keyBinding.objectToEntry(key, keyEntry);
940
941         OperationStatus status =
942             secDb.get(txn, keyEntry, pkeyEntry, dataEntry, lockMode);
943
944         if (status == OperationStatus.SUCCESS) {
945             return (E) entityBinding.entryToObject(pkeyEntry, dataEntry);
946         } else {
947             return null;
948         }
949     }
950
951     public Map JavaDoc<SK,E> map() {
952         return sortedMap();
953     }
954
955     public synchronized SortedMap JavaDoc<SK,E> sortedMap() {
956         if (map == null) {
957             map = new StoredSortedMap(db, keyBinding, entityBinding, true);
958         }
959         return map;
960     }
961 }
962
Popular Tags