| /*- |
| * Copyright (C) 2002, 2018, Oracle and/or its affiliates. All rights reserved. |
| * |
| * This file was distributed by Oracle as part of a version of Oracle Berkeley |
| * DB Java Edition made available at: |
| * |
| * http://www.oracle.com/technetwork/database/database-technologies/berkeleydb/downloads/index.html |
| * |
| * Please see the LICENSE file included in the top-level directory of the |
| * appropriate version of Oracle Berkeley DB Java Edition for a copy of the |
| * license and additional information. |
| */ |
| |
| package com.sleepycat.persist; |
| |
| import java.util.Map; |
| import java.util.SortedMap; |
| |
| import com.sleepycat.bind.EntityBinding; |
| import com.sleepycat.bind.EntryBinding; |
| import com.sleepycat.collections.StoredSortedMap; |
| import com.sleepycat.compat.DbCompat; |
| import com.sleepycat.je.Database; |
| import com.sleepycat.je.DatabaseConfig; |
| import com.sleepycat.je.DatabaseEntry; |
| import com.sleepycat.je.DatabaseException; |
| /* <!-- begin JE only --> */ |
| import com.sleepycat.je.DbInternal; |
| import com.sleepycat.je.Get; |
| /* <!-- end JE only --> */ |
| import com.sleepycat.je.LockMode; |
| /* <!-- begin JE only --> */ |
| import com.sleepycat.je.OperationResult; |
| /* <!-- end JE only --> */ |
| import com.sleepycat.je.OperationStatus; |
| /* <!-- begin JE only --> */ |
| import com.sleepycat.je.ReadOptions; |
| /* <!-- end JE only --> */ |
| import com.sleepycat.je.SecondaryDatabase; |
| import com.sleepycat.je.Transaction; |
| import com.sleepycat.je.TransactionConfig; |
| import com.sleepycat.persist.model.DeleteAction; |
| import com.sleepycat.persist.model.Relationship; |
| import com.sleepycat.persist.model.SecondaryKey; |
| |
| /** |
| * The secondary index for an entity class and a secondary key. |
| * |
| * <p>{@code SecondaryIndex} objects are thread-safe. Multiple threads may |
| * safely call the methods of a shared {@code SecondaryIndex} object.</p> |
| * |
| * <p>{@code SecondaryIndex} implements {@link EntityIndex} to map the |
| * secondary key type (SK) to the entity type (E). In other words, entities |
| * are accessed by secondary key values.</p> |
| * |
| * <p>The {@link SecondaryKey} annotation may be used to define a secondary key |
| * as shown in the following example.</p> |
| * |
| * <pre class="code"> |
| * {@literal @Entity} |
| * class Employee { |
| * |
| * {@literal @PrimaryKey} |
| * long id; |
| * |
| * {@literal @SecondaryKey(relate=MANY_TO_ONE)} |
| * String department; |
| * |
| * String name; |
| * |
| * private Employee() {} |
| * }</pre> |
| * |
| * <p>Before obtaining a {@code SecondaryIndex}, the {@link PrimaryIndex} must |
| * be obtained for the entity class. To obtain the {@code SecondaryIndex} call |
| * {@link EntityStore#getSecondaryIndex EntityStore.getSecondaryIndex}, passing |
| * the primary index, the secondary key class and the secondary key name. For |
| * example:</p> |
| * |
| * <pre class="code"> |
| * EntityStore store = new EntityStore(...); |
| * |
| * {@code PrimaryIndex<Long, Employee>} primaryIndex = |
| * store.getPrimaryIndex(Long.class, Employee.class); |
| * |
| * {@code SecondaryIndex<String, Long, Employee>} secondaryIndex = |
| * store.getSecondaryIndex(primaryIndex, String.class, "department");</pre> |
| * |
| * <p>Since {@code SecondaryIndex} implements the {@link EntityIndex} |
| * interface, it shares the common index methods for retrieving and deleting |
| * entities, opening cursors and using transactions. See {@link EntityIndex} |
| * for more information on these topics.</p> |
| * |
| * <p>{@code SecondaryIndex} does <em>not</em> provide methods for inserting |
| * and updating entities. That must be done using the {@link |
| * PrimaryIndex}.</p> |
| * |
| * <p>Note that a {@code SecondaryIndex} has three type parameters {@code <SK, |
| * PK, E>} or in the example {@code <String, Long, Employee>} while a {@link |
| * PrimaryIndex} has only two type parameters {@code <PK, E>} or {@code <Long, |
| * Employee>}. This is because a {@code SecondaryIndex} has an extra level of |
| * mapping: It maps from secondary key to primary key, and then from primary |
| * key to entity. For example, consider this entity:</p> |
| * |
| * <div><table class="code" border="1" summary=""> |
| * <tr><th>ID</th><th>Department</th><th>Name</th></tr> |
| * <tr><td>1</td><td>Engineering</td><td>Jane Smith</td></tr> |
| * </table></div> |
| * |
| * <p>The {@link PrimaryIndex} maps from id directly to the entity, or from |
| * primary key 1 to the "Jane Smith" entity in the example. The {@code |
| * SecondaryIndex} maps from department to id, or from secondary key |
| * "Engineering" to primary key 1 in the example, and then uses the {@code |
| * PrimaryIndex} to map from the primary key to the entity.</p> |
| * |
| * <p>Because of this extra type parameter and extra level of mapping, a {@code |
| * SecondaryIndex} can provide more than one mapping, or view, of the entities |
| * in the primary index. The main mapping of a {@code SecondaryIndex} is to |
| * map from secondary key (SK) to entity (E), or in the example, from the |
| * String department key to the Employee entity. The {@code SecondaryIndex} |
| * itself, by implementing {@code EntityIndex<SK, E>}, provides this |
| * mapping.</p> |
| * |
| * <p>The second mapping provided by {@code SecondaryIndex} is from secondary |
| * key (SK) to primary key (PK), or in the example, from the String department |
| * key to the Long id key. The {@link #keysIndex} method provides this |
| * mapping. When accessing the keys index, the primary key is returned rather |
| * than the entity. When only the primary key is needed and not the entire |
| * entity, using the keys index is less expensive than using the secondary |
| * index because the primary index does not have to be accessed.</p> |
| * |
| * <p>The third mapping provided by {@code SecondaryIndex} is from primary key |
| * (PK) to entity (E), for the subset of entities having a given secondary key |
| * (SK). This mapping is provided by the {@link #subIndex} method. A |
| * sub-index is convenient when you are interested in working with the subset |
| * of entities having a particular secondary key value, for example, all |
| * employees in a given department.</p> |
| * |
| * <p>All three mappings, along with the mapping provided by the {@link |
| * PrimaryIndex}, are shown using example data in the {@link EntityIndex} |
| * interface documentation. See {@link EntityIndex} for more information.</p> |
| * |
| * <p>Note that when using an index, keys and values are stored and retrieved |
| * by value not by reference. In other words, if an entity object is stored |
| * and then retrieved, or retrieved twice, each object will be a separate |
| * instance. For example, in the code below the assertion will always |
| * fail.</p> |
| * <pre class="code"> |
| * MyKey key = ...; |
| * MyEntity entity1 = index.get(key); |
| * MyEntity entity2 = index.get(key); |
| * assert entity1 == entity2; // always fails! |
| * </pre> |
| * |
| * <h3>One-to-One Relationships</h3> |
| * |
| * <p>A {@link Relationship#ONE_TO_ONE ONE_TO_ONE} relationship, although less |
| * common than other types of relationships, is the simplest type of |
| * relationship. A single entity is related to a single secondary key value. |
| * For example:</p> |
| * |
| * <pre class="code"> |
| * {@literal @Entity} |
| * class Employee { |
| * |
| * {@literal @PrimaryKey} |
| * long id; |
| * |
| * {@literal @SecondaryKey(relate=ONE_TO_ONE)} |
| * String ssn; |
| * |
| * String name; |
| * |
| * private Employee() {} |
| * } |
| * |
| * {@code SecondaryIndex<String, Long, Employee>} employeeBySsn = |
| * store.getSecondaryIndex(primaryIndex, String.class, "ssn");</pre> |
| * |
| * <p>With a {@link Relationship#ONE_TO_ONE ONE_TO_ONE} relationship, the |
| * secondary key must be unique; in other words, no two entities may have the |
| * same secondary key value. If an attempt is made to store an entity having |
| * the same secondary key value as another existing entity, a {@link |
| * DatabaseException} will be thrown.</p> |
| * |
| * <p>Because the secondary key is unique, it is useful to lookup entities by |
| * secondary key using {@link EntityIndex#get}. For example:</p> |
| * |
| * <pre class="code"> |
| * Employee employee = employeeBySsn.get(mySsn);</pre> |
| * |
| * <h3>Many-to-One Relationships</h3> |
| * |
| * <p>A {@link Relationship#MANY_TO_ONE MANY_TO_ONE} relationship is the most |
| * common type of relationship. One or more entities is related to a single |
| * secondary key value. For example:</p> |
| * |
| * <pre class="code"> |
| * {@literal @Entity} |
| * class Employee { |
| * |
| * {@literal @PrimaryKey} |
| * long id; |
| * |
| * {@literal @SecondaryKey(relate=MANY_TO_ONE)} |
| * String department; |
| * |
| * String name; |
| * |
| * private Employee() {} |
| * } |
| * |
| * {@code SecondaryIndex<String, Long, Employee>} employeeByDepartment = |
| * store.getSecondaryIndex(primaryIndex, String.class, "department");</pre> |
| * |
| * <p>With a {@link Relationship#MANY_TO_ONE MANY_TO_ONE} relationship, the |
| * secondary key is not required to be unique; in other words, more than one |
| * entity may have the same secondary key value. In this example, more than |
| * one employee may belong to the same department.</p> |
| * |
| * <p>The most convenient way to access the employees in a given department is |
| * by using a sub-index. For example:</p> |
| * |
| * <pre class="code"> |
| * {@code EntityIndex<Long, Entity>} subIndex = employeeByDepartment.subIndex(myDept); |
| * {@code EntityCursor<Employee>} cursor = subIndex.entities(); |
| * try { |
| * for (Employee entity : cursor) { |
| * // Do something with the entity... |
| * } |
| * } finally { |
| * cursor.close(); |
| * }</pre> |
| * |
| * <h3>One-to-Many Relationships</h3> |
| * |
| * <p>In a {@link Relationship#ONE_TO_MANY ONE_TO_MANY} relationship, a single |
| * entity is related to one or more secondary key values. For example:</p> |
| * |
| * <pre class="code"> |
| * {@literal @Entity} |
| * class Employee { |
| * |
| * {@literal @PrimaryKey} |
| * long id; |
| * |
| * {@literal @SecondaryKey(relate=ONE_TO_MANY)} |
| * {@literal Set<String> emailAddresses = new HashSet<String>;} |
| * |
| * String name; |
| * |
| * private Employee() {} |
| * } |
| * |
| * {@code SecondaryIndex<String, Long, Employee>} employeeByEmail = |
| * store.getSecondaryIndex(primaryIndex, String.class, "emailAddresses");</pre> |
| * |
| * <p>With a {@link Relationship#ONE_TO_MANY ONE_TO_MANY} relationship, the |
| * secondary key must be unique; in other words, no two entities may have the |
| * same secondary key value. In this example, no two employees may have the |
| * same email address. If an attempt is made to store an entity having the |
| * same secondary key value as another existing entity, a {@link |
| * DatabaseException} will be thrown.</p> |
| * |
| * <p>Because the secondary key is unique, it is useful to lookup entities by |
| * secondary key using {@link EntityIndex#get}. For example:</p> |
| * |
| * <pre class="code"> |
| * Employee employee = employeeByEmail.get(myEmailAddress);</pre> |
| * |
| * <p>The secondary key field for a {@link Relationship#ONE_TO_MANY |
| * ONE_TO_MANY} relationship must be an array or collection type. To access |
| * the email addresses of an employee, simply access the collection field |
| * directly. For example:</p> |
| * |
| * <pre class="code"> |
| * Employee employee = primaryIndex.get(1); // Get the entity by primary key |
| * employee.emailAddresses.add(myNewEmail); // Add an email address |
| * primaryIndex.putNoReturn(1, employee); // Update the entity</pre> |
| * |
| * <h3>Many-to-Many Relationships</h3> |
| * |
| * <p>In a {@link Relationship#MANY_TO_MANY MANY_TO_MANY} relationship, one |
| * or more entities is related to one or more secondary key values. For |
| * example:</p> |
| * |
| * <pre class="code"> |
| * {@literal @Entity} |
| * class Employee { |
| * |
| * {@literal @PrimaryKey} |
| * long id; |
| * |
| * {@literal @SecondaryKey(relate=MANY_TO_MANY)} |
| * {@literal Set<String> organizations = new HashSet<String>;} |
| * |
| * String name; |
| * |
| * private Employee() {} |
| * } |
| * |
| * {@code SecondaryIndex<String, Long, Employee>} employeeByOrganization = |
| * store.getSecondaryIndex(primaryIndex, String.class, "organizations");</pre> |
| * |
| * <p>With a {@link Relationship#MANY_TO_MANY MANY_TO_MANY} relationship, the |
| * secondary key is not required to be unique; in other words, more than one |
| * entity may have the same secondary key value. In this example, more than |
| * one employee may belong to the same organization.</p> |
| * |
| * <p>The most convenient way to access the employees in a given organization |
| * is by using a sub-index. For example:</p> |
| * |
| * <pre class="code"> |
| * {@code EntityIndex<Long, Entity>} subIndex = employeeByOrganization.subIndex(myOrg); |
| * {@code EntityCursor<Employee>} cursor = subIndex.entities(); |
| * try { |
| * for (Employee entity : cursor) { |
| * // Do something with the entity... |
| * } |
| * } finally { |
| * cursor.close(); |
| * }</pre> |
| * |
| * <p>The secondary key field for a {@link Relationship#MANY_TO_MANY |
| * MANY_TO_MANY} relationship must be an array or collection type. To access |
| * the organizations of an employee, simply access the collection field |
| * directly. For example:</p> |
| * |
| * <pre class="code"> |
| * Employee employee = primaryIndex.get(1); // Get the entity by primary key |
| * employee.organizations.remove(myOldOrg); // Remove an organization |
| * primaryIndex.putNoReturn(1, employee); // Update the entity</pre> |
| * |
| * <h3>Foreign Key Constraints for Related Entities</h3> |
| * |
| * <p>In all the examples above the secondary key is treated only as a simple |
| * value, such as a {@code String} department field. In many cases, that is |
| * sufficient. But in other cases, you may wish to constrain the secondary |
| * keys of one entity class to be valid primary keys of another entity |
| * class. For example, a Department entity may also be defined:</p> |
| * |
| * <pre class="code"> |
| * {@literal @Entity} |
| * class Department { |
| * |
| * {@literal @PrimaryKey} |
| * String name; |
| * |
| * String missionStatement; |
| * |
| * private Department() {} |
| * }</pre> |
| * |
| * <p>You may wish to constrain the department field values of the Employee |
| * class in the examples above to be valid primary keys of the Department |
| * entity class. In other words, you may wish to ensure that the department |
| * field of an Employee will always refer to a valid Department entity.</p> |
| * |
| * <p>You can implement this constraint yourself by validating the department |
| * field before you store an Employee. For example:</p> |
| * |
| * <pre class="code"> |
| * {@code PrimaryIndex<String, Department>} departmentIndex = |
| * store.getPrimaryIndex(String.class, Department.class); |
| * |
| * void storeEmployee(Employee employee) throws DatabaseException { |
| * if (departmentIndex.contains(employee.department)) { |
| * primaryIndex.putNoReturn(employee); |
| * } else { |
| * throw new IllegalArgumentException("Department does not exist: " + |
| * employee.department); |
| * } |
| * }</pre> |
| * |
| * <p>Or, instead you could define the Employee department field as a foreign |
| * key, and this validation will be done for you when you attempt to store the |
| * Employee entity. For example:</p> |
| * |
| * <pre class="code"> |
| * {@literal @Entity} |
| * class Employee { |
| * |
| * {@literal @PrimaryKey} |
| * long id; |
| * |
| * {@literal @SecondaryKey(relate=MANY_TO_ONE, relatedEntity=Department.class)} |
| * String department; |
| * |
| * String name; |
| * |
| * private Employee() {} |
| * }</pre> |
| * |
| * <p>The {@code relatedEntity=Department.class} above defines the department |
| * field as a foreign key that refers to a Department entity. Whenever a |
| * Employee entity is stored, its department field value will be checked to |
| * ensure that a Department entity exists with that value as its primary key. |
| * If no such Department entity exists, then a {@link DatabaseException} is |
| * thrown, causing the transaction to be aborted (assuming that transactions |
| * are used).</p> |
| * |
| * <p>This begs the question: What happens when a Department entity is deleted |
| * while one or more Employee entities have department fields that refer to |
| * the deleted department's primary key? If the department were allowed to be |
| * deleted, the foreign key constraint for the Employee department field would |
| * be violated, because the Employee department field would refer to a |
| * department that does not exist.</p> |
| * |
| * <p>By default, when this situation arises the system does not allow the |
| * department to be deleted. Instead, a {@link DatabaseException} is thrown, |
| * causing the transaction to be aborted. In this case, in order to delete a |
| * department, the department field of all Employee entities must first be |
| * updated to refer to a different existing department, or set to null. This |
| * is the responsibility of the application.</p> |
| * |
| * <p>There are two additional ways of handling deletion of a Department |
| * entity. These alternatives are configured using the {@link |
| * SecondaryKey#onRelatedEntityDelete} annotation property. Setting this |
| * property to {@link DeleteAction#NULLIFY} causes the Employee department |
| * field to be automatically set to null when the department they refer to is |
| * deleted. This may or may not be desirable, depending on application |
| * policies. For example:</p> |
| * |
| * <pre class="code"> |
| * {@literal @Entity} |
| * class Employee { |
| * |
| * {@literal @PrimaryKey} |
| * long id; |
| * |
| * {@code @SecondaryKey(relate=MANY_TO_ONE, relatedEntity=Department.class, |
| * onRelatedEntityDelete=NULLIFY)} |
| * String department; |
| * |
| * String name; |
| * |
| * private Employee() {} |
| * }</pre> |
| * |
| * <p>The {@link DeleteAction#CASCADE} value, on the other hand, causes the |
| * Employee entities to be automatically deleted when the department they refer |
| * to is deleted. This is probably not desirable in this particular example, |
| * but is useful for parent-child relationships. For example:</p> |
| * |
| * <pre class="code"> |
| * {@literal @Entity} |
| * class Order { |
| * |
| * {@literal @PrimaryKey} |
| * long id; |
| * |
| * String description; |
| * |
| * private Order() {} |
| * } |
| * |
| * {@literal @Entity} |
| * class OrderItem { |
| * |
| * {@literal @PrimaryKey} |
| * long id; |
| * |
| * {@code @SecondaryKey(relate=MANY_TO_ONE, relatedEntity=Order.class, |
| * onRelatedEntityDelete=CASCADE)} |
| * long orderId; |
| * |
| * String description; |
| * |
| * private OrderItem() {} |
| * }</pre> |
| * |
| * <p>The OrderItem orderId field refers to its "parent" Order entity. When an |
| * Order entity is deleted, it may be useful to automatically delete its |
| * "child" OrderItem entities.</p> |
| * |
| * <p>For more information, see {@link SecondaryKey#onRelatedEntityDelete}.</p> |
| * |
| * <h3>One-to-Many versus Many-to-One for Related Entities</h3> |
| * |
| * <p>When there is a conceptual Many-to-One relationship such as Employee to |
| * Department as illustrated in the examples above, the relationship may be |
| * implemented either as Many-to-One in the Employee class or as One-to-Many in |
| * the Department class.</p> |
| * |
| * <p>Here is the Many-to-One approach.</p> |
| * |
| * <pre class="code"> |
| * {@literal @Entity} |
| * class Employee { |
| * |
| * {@literal @PrimaryKey} |
| * long id; |
| * |
| * {@literal @SecondaryKey(relate=MANY_TO_ONE, relatedEntity=Department.class)} |
| * String department; |
| * |
| * String name; |
| * |
| * private Employee() {} |
| * } |
| * |
| * {@literal @Entity} |
| * class Department { |
| * |
| * {@literal @PrimaryKey} |
| * String name; |
| * |
| * String missionStatement; |
| * |
| * private Department() {} |
| * }</pre> |
| * |
| * <p>And here is the One-to-Many approach.</p> |
| * |
| * <pre class="code"> |
| * {@literal @Entity} |
| * class Employee { |
| * |
| * {@literal @PrimaryKey} |
| * long id; |
| * |
| * String name; |
| * |
| * private Employee() {} |
| * } |
| * |
| * {@literal @Entity} |
| * class Department { |
| * |
| * {@literal @PrimaryKey} |
| * String name; |
| * |
| * String missionStatement; |
| * |
| * {@literal @SecondaryKey(relate=ONE_TO_MANY, relatedEntity=Employee.class)} |
| * {@literal Set<Long> employees = new HashSet<Long>;} |
| * |
| * private Department() {} |
| * }</pre> |
| * |
| * <p>Which approach is best? The Many-to-One approach better handles large |
| * number of entities on the to-Many side of the relationship because it |
| * doesn't store a collection of keys as an entity field. With Many-to-One a |
| * Btree is used to store the collection of keys and the Btree can easily |
| * handle very large numbers of keys. With One-to-Many, each time a related |
| * key is added or removed the entity on the One side of the relationship, |
| * along with the complete collection of related keys, must be updated. |
| * Therefore, if large numbers of keys may be stored per relationship, |
| * Many-to-One is recommended.</p> |
| * |
| * <p>If the number of entities per relationship is not a concern, then you may |
| * wish to choose the approach that is most natural in your application data |
| * model. For example, if you think of a Department as containing employees |
| * and you wish to modify the Department object each time an employee is added |
| * or removed, then you may wish to store a collection of Employee keys in the |
| * Department object (One-to-Many).</p> |
| * |
| * <p>Note that if you have a One-to-Many relationship and there is no related |
| * entity, then you don't have a choice -- you have to use One-to-Many because |
| * there is no entity on the to-Many side of the relationship where a |
| * Many-to-One key could be defined. An example is the Employee to email |
| * addresses relationship discussed above:</p> |
| * |
| * <pre class="code"> |
| * {@literal @Entity} |
| * class Employee { |
| * |
| * {@literal @PrimaryKey} |
| * long id; |
| * |
| * {@literal @SecondaryKey(relate=ONE_TO_MANY)} |
| * {@literal Set<String> emailAddresses = new HashSet<String>;} |
| * |
| * String name; |
| * |
| * private Employee() {} |
| * }</pre> |
| * |
| * <p>For sake of argument imagine that each employee has thousands of email |
| * addresses and employees frequently add and remove email addresses. To |
| * avoid the potential performance problems associated with updating the |
| * Employee entity every time an email address is added or removed, you could |
| * create an EmployeeEmailAddress entity and use a Many-to-One relationship as |
| * shown below:</p> |
| * |
| * <pre class="code"> |
| * {@literal @Entity} |
| * class Employee { |
| * |
| * {@literal @PrimaryKey} |
| * long id; |
| * |
| * String name; |
| * |
| * private Employee() {} |
| * } |
| * |
| * {@literal @Entity} |
| * class EmployeeEmailAddress { |
| * |
| * {@literal @PrimaryKey} |
| * String emailAddress; |
| * |
| * {@literal @SecondaryKey(relate=MANY_TO_ONE, relatedEntity=Employee.class)} |
| * long employeeId; |
| * |
| * private EmployeeEmailAddress() {} |
| * }</pre> |
| * |
| * <h3>Key Placement with Many-to-Many for Related Entities</h3> |
| * |
| * <p>As discussed in the section above, one drawback of a to-Many relationship |
| * (One-to-Many was discussed above and Many-to-Many is discussed here) is that |
| * it requires storing a collection of keys in an entity. Each time a key is |
| * added or removed, the containing entity must be updated. This has potential |
| * performance problems when there are large numbers of entities on the to-Many |
| * side of the relationship, in other words, when there are large numbers of |
| * keys in each secondary key field collection.</p> |
| * |
| * <p>If you have a Many-to-Many relationship with a reasonably small number of |
| * entities on one side of the relationship and a large number of entities on |
| * the other side, you can avoid the potential performance problems by defining |
| * the secondary key field on the side with a small number of entities.</p> |
| * |
| * <p>For example, in an Employee-to-Organization relationship, the number of |
| * organizations per employee will normally be reasonably small but the number |
| * of employees per organization may be very large. Therefore, to avoid |
| * potential performance problems, the secondary key field should be defined in |
| * the Employee class as shown below.</p> |
| * |
| * <pre class="code"> |
| * {@literal @Entity} |
| * class Employee { |
| * |
| * {@literal @PrimaryKey} |
| * long id; |
| * |
| * {@literal @SecondaryKey(relate=MANY_TO_MANY, relatedEntity=Organization.class)} |
| * {@literal Set<String> organizations = new HashSet<String>;} |
| * |
| * String name; |
| * |
| * private Employee() {} |
| * } |
| * |
| * {@literal @Entity} |
| * class Organization { |
| * |
| * {@literal @PrimaryKey} |
| * String name; |
| * |
| * String description; |
| * }</pre> |
| * |
| * <p>If instead a {@code Set<Long> members} key had been defined in the |
| * Organization class, this set could potentially have a large number of |
| * elements and performance problems could result.</p> |
| * |
| * <h3>Many-to-Many Versus a Relationship Entity</h3> |
| * |
| * <p>If you have a Many-to-Many relationship with a large number of entities |
| * on <em>both</em> sides of the relationship, you can avoid the potential |
| * performance problems by using a <em>relationship entity</em>. A |
| * relationship entity defines the relationship between two other entities |
| * using two Many-to-One relationships.</p> |
| * |
| * <p>Imagine a relationship between cars and trucks indicating whenever a |
| * particular truck was passed on the road by a particular car. A given car |
| * may pass a large number of trucks and a given truck may be passed by a large |
| * number of cars. First look at a Many-to-Many relationship between these two |
| * entities:</p> |
| * |
| * <pre class="code"> |
| * {@literal @Entity} |
| * class Car { |
| * |
| * {@literal @PrimaryKey} |
| * String licenseNumber; |
| * |
| * {@literal @SecondaryKey(relate=MANY_TO_MANY, relatedEntity=Truck.class)} |
| * {@literal Set<String> trucksPassed = new HashSet<String>;} |
| * |
| * String color; |
| * |
| * private Car() {} |
| * } |
| * |
| * {@literal @Entity} |
| * class Truck { |
| * |
| * {@literal @PrimaryKey} |
| * String licenseNumber; |
| * |
| * int tons; |
| * |
| * private Truck() {} |
| * }</pre> |
| * |
| * <p>With the Many-to-Many approach above, the {@code trucksPassed} set could |
| * potentially have a large number of elements and performance problems could |
| * result.</p> |
| * |
| * <p>To apply the relationship entity approach we define a new entity class |
| * named CarPassedTruck representing a single truck passed by a single car. We |
| * remove the secondary key from the Car class and use two secondary keys in |
| * the CarPassedTruck class instead.</p> |
| * |
| * <pre class="code"> |
| * {@literal @Entity} |
| * class Car { |
| * |
| * {@literal @PrimaryKey} |
| * String licenseNumber; |
| * |
| * String color; |
| * |
| * private Car() {} |
| * } |
| * |
| * {@literal @Entity} |
| * class Truck { |
| * |
| * {@literal @PrimaryKey} |
| * String licenseNumber; |
| * |
| * int tons; |
| * |
| * private Truck() {} |
| * } |
| * |
| * {@literal @Entity} |
| * class CarPassedTruck { |
| * |
| * {@literal @PrimaryKey} |
| * long id; |
| * |
| * {@literal @SecondaryKey(relate=MANY_TO_ONE, relatedEntity=Car.class)} |
| * String carLicense; |
| * |
| * {@literal @SecondaryKey(relate=MANY_TO_ONE, relatedEntity=Truck.class)} |
| * String truckLicense; |
| * |
| * private CarPassedTruck() {} |
| * }</pre> |
| * |
| * <p>The CarPassedTruck entity can be used to access the relationship by car |
| * license or by truck license.</p> |
| * |
| * <p>You may use the relationship entity approach because of the potential |
| * performance problems mentioned above. Or, you may choose to use this |
| * approach in order to store other information about the relationship. For |
| * example, if for each car that passes a truck you wish to record how much |
| * faster the car was going than the truck, then a relationship entity is the |
| * logical place to store that property. In the example below the |
| * speedDifference property is added to the CarPassedTruck class.</p> |
| * |
| * <pre class="code"> |
| * {@literal @Entity} |
| * class CarPassedTruck { |
| * |
| * {@literal @PrimaryKey} |
| * long id; |
| * |
| * {@literal @SecondaryKey(relate=MANY_TO_ONE, relatedEntity=Car.class)} |
| * String carLicense; |
| * |
| * {@literal @SecondaryKey(relate=MANY_TO_ONE, relatedEntity=Truck.class)} |
| * String truckLicense; |
| * |
| * int speedDifference; |
| * |
| * private CarPassedTruck() {} |
| * }</pre> |
| * |
| * <p>Be aware that the relationship entity approach adds overhead compared to |
| * Many-to-Many. There is one additional entity and one additional secondary |
| * key. These factors should be weighed against its advantages and the |
| * relevant application access patterns should be considered.</p> |
| * |
| * @author Mark Hayes |
| */ |
| public class SecondaryIndex<SK, PK, E> extends BasicIndex<SK, E> { |
| |
| private SecondaryDatabase secDb; |
| private Database keysDb; |
| private PrimaryIndex<PK, E> priIndex; |
| private EntityBinding<E> entityBinding; |
| private EntityIndex<SK, PK> keysIndex; |
| private SortedMap<SK, E> map; |
| |
| /** |
| * Creates a secondary index without using an <code>EntityStore</code>. |
| * When using an {@link EntityStore}, call {@link |
| * EntityStore#getSecondaryIndex getSecondaryIndex} instead. |
| * |
| * <p>This constructor is not normally needed and is provided for |
| * applications that wish to use custom bindings along with the Direct |
| * Persistence Layer. Normally, {@link EntityStore#getSecondaryIndex |
| * getSecondaryIndex} is used instead.</p> |
| * |
| * @param database the secondary database used for all access other than |
| * via a {@link #keysIndex}. |
| * |
| * @param keysDatabase another handle on the secondary database, opened |
| * without association to the primary, and used only for access via a |
| * {@link #keysIndex}. If this argument is null and the {@link #keysIndex} |
| * method is called, then the keys database will be opened automatically; |
| * however, the user is then responsible for closing the keys database. To |
| * get the keys database in order to close it, call {@link |
| * #getKeysDatabase}. |
| * |
| * @param primaryIndex the primary index associated with this secondary |
| * index. |
| * |
| * @param secondaryKeyClass the class of the secondary key. |
| * |
| * @param secondaryKeyBinding the binding to be used for secondary keys. |
| * |
| * @throws DatabaseException the base class for all BDB exceptions. |
| */ |
| public SecondaryIndex(SecondaryDatabase database, |
| Database keysDatabase, |
| PrimaryIndex<PK, E> primaryIndex, |
| Class<SK> secondaryKeyClass, |
| EntryBinding<SK> secondaryKeyBinding) |
| throws DatabaseException { |
| |
| super(database, secondaryKeyClass, secondaryKeyBinding, |
| new EntityValueAdapter(primaryIndex.getEntityClass(), |
| primaryIndex.getEntityBinding(), |
| true)); |
| secDb = database; |
| keysDb = keysDatabase; |
| priIndex = primaryIndex; |
| entityBinding = primaryIndex.getEntityBinding(); |
| } |
| |
| /** |
| * Returns the underlying secondary database for this index. |
| * |
| * @return the secondary database. |
| */ |
| @Override |
| public SecondaryDatabase getDatabase() { |
| return secDb; |
| } |
| |
| /** |
| * Returns the underlying secondary database that is not associated with |
| * the primary database and is used for the {@link #keysIndex}. |
| * |
| * @return the keys database. |
| */ |
| public Database getKeysDatabase() { |
| return keysDb; |
| } |
| |
| /** |
| * Returns the primary index associated with this secondary index. |
| * |
| * @return the primary index. |
| */ |
| public PrimaryIndex<PK, E> getPrimaryIndex() { |
| return priIndex; |
| } |
| |
| /** |
| * Returns the secondary key class for this index. |
| * |
| * @return the class. |
| */ |
| public Class<SK> getKeyClass() { |
| return keyClass; |
| } |
| |
| /** |
| * Returns the secondary key binding for the index. |
| * |
| * @return the key binding. |
| */ |
| public EntryBinding<SK> getKeyBinding() { |
| return keyBinding; |
| } |
| |
| /** |
| * Returns a read-only keys index that maps secondary key to primary key. |
| * When accessing the keys index, the primary key is returned rather than |
| * the entity. When only the primary key is needed and not the entire |
| * entity, using the keys index is less expensive than using the secondary |
| * index because the primary index does not have to be accessed. |
| * |
| * <p>Note the following in the unusual case that you are <em>not</em> |
| * using an <code>EntityStore</code>: This method will open the keys |
| * database, a second database handle for the secondary database, if it is |
| * not already open. In this case, if you are <em>not</em> using an |
| * <code>EntityStore</code>, then you are responsible for closing the |
| * database returned by {@link #getKeysDatabase} before closing the |
| * environment. If you <em>are</em> using an <code>EntityStore</code>, the |
| * keys database will be closed automatically by {@link |
| * EntityStore#close}.</p> |
| * |
| * @return the keys index. |
| * |
| * @throws DatabaseException the base class for all BDB exceptions. |
| */ |
| public synchronized EntityIndex<SK, PK> keysIndex() |
| throws DatabaseException { |
| |
| if (keysIndex == null) { |
| if (keysDb == null) { |
| DatabaseConfig config = secDb.getConfig(); |
| config.setReadOnly(true); |
| config.setAllowCreate(false); |
| config.setExclusiveCreate(false); |
| keysDb = DbCompat.openDatabase |
| (db.getEnvironment(), null /*txn*/, |
| DbCompat.getDatabaseFile(secDb), |
| secDb.getDatabaseName(), |
| config); |
| if (keysDb == null) { |
| throw new IllegalStateException |
| ("Could not open existing DB, file: " + |
| DbCompat.getDatabaseFile(secDb) + " name: " + |
| secDb.getDatabaseName()); |
| } |
| } |
| keysIndex = new KeysIndex<SK, PK> |
| (keysDb, keyClass, keyBinding, |
| priIndex.getKeyClass(), priIndex.getKeyBinding()); |
| } |
| return keysIndex; |
| } |
| |
| /** |
| * Returns an index that maps primary key to entity for the subset of |
| * entities having a given secondary key (duplicates). A sub-index is |
| * convenient when you are interested in working with the subset of |
| * entities having a particular secondary key value. |
| * |
| * <p>When using a {@link Relationship#MANY_TO_ONE MANY_TO_ONE} or {@link |
| * Relationship#MANY_TO_MANY MANY_TO_MANY} secondary key, the sub-index |
| * represents the left (MANY) side of a relationship.</p> |
| * |
| * @param key the secondary key that identifies the entities in the |
| * sub-index. |
| * |
| * @return the sub-index. |
| * |
| * @throws DatabaseException the base class for all BDB exceptions. |
| */ |
| public EntityIndex<PK, E> subIndex(SK key) |
| throws DatabaseException { |
| |
| return new SubIndex(this, entityBinding, key); |
| } |
| |
| /* |
| * Of the EntityIndex methods only get()/map()/sortedMap() are implemented |
| * here. All other methods are implemented by BasicIndex. |
| */ |
| |
| public E get(SK key) |
| throws DatabaseException { |
| |
| return get(null, key, null); |
| } |
| |
| public E get(Transaction txn, SK key, LockMode lockMode) |
| throws DatabaseException { |
| |
| /* <!-- begin JE only --> */ |
| if (DbCompat.IS_JE) { |
| EntityResult<E> result = get( |
| txn, key, Get.SEARCH, DbInternal.getReadOptions(lockMode)); |
| return result != null ? result.value() : null; |
| } |
| /* <!-- end JE only --> */ |
| |
| DatabaseEntry keyEntry = new DatabaseEntry(); |
| DatabaseEntry pkeyEntry = new DatabaseEntry(); |
| DatabaseEntry dataEntry = new DatabaseEntry(); |
| keyBinding.objectToEntry(key, keyEntry); |
| |
| OperationStatus status = |
| secDb.get(txn, keyEntry, pkeyEntry, dataEntry, lockMode); |
| |
| if (status == OperationStatus.SUCCESS) { |
| return entityBinding.entryToObject(pkeyEntry, dataEntry); |
| } else { |
| return null; |
| } |
| } |
| |
| /* <!-- begin JE only --> */ |
| public EntityResult<E> get(Transaction txn, |
| SK key, |
| Get getType, |
| ReadOptions options) |
| throws DatabaseException { |
| |
| checkGetType(getType); |
| |
| DatabaseEntry keyEntry = new DatabaseEntry(); |
| DatabaseEntry pkeyEntry = new DatabaseEntry(); |
| DatabaseEntry dataEntry = new DatabaseEntry(); |
| keyBinding.objectToEntry(key, keyEntry); |
| |
| OperationResult result = secDb.get( |
| txn, keyEntry, pkeyEntry, dataEntry, getType, options); |
| |
| if (result != null) { |
| return new EntityResult<>( |
| entityBinding.entryToObject(pkeyEntry, dataEntry), |
| result); |
| } else { |
| return null; |
| } |
| } |
| /* <!-- end JE only --> */ |
| |
| public Map<SK, E> map() { |
| return sortedMap(); |
| } |
| |
| public synchronized SortedMap<SK, E> sortedMap() { |
| if (map == null) { |
| map = new StoredSortedMap(db, keyBinding, entityBinding, true); |
| } |
| return map; |
| } |
| |
| /** |
| * <!-- begin JE only --> |
| * @hidden |
| * <!-- end JE only --> |
| * For internal use only. |
| * |
| * Used for obtaining the auto-commit txn config from the store, which |
| * overrides this method to return it. |
| */ |
| /* <!-- begin JE only --> */ |
| protected |
| /* <!-- end JE only --> */ |
| TransactionConfig getAutoCommitTransactionConfig() { |
| return null; |
| } |
| |
| boolean isUpdateAllowed() { |
| return false; |
| } |
| } |