/*- * See the file LICENSE for redistribution information. * * Copyright (c) 2002, 2015 Oracle and/or its affiliates. All rights reserved. * */ 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.db.Database; import com.sleepycat.db.DatabaseConfig; import com.sleepycat.db.DatabaseEntry; import com.sleepycat.db.DatabaseException; import com.sleepycat.db.LockMode; import com.sleepycat.db.OperationStatus; import com.sleepycat.db.SecondaryDatabase; import com.sleepycat.db.Transaction; import com.sleepycat.db.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> * * <p><table class="code" border="1"> * <tr><th>ID</th><th>Department</th><th>Name</th></tr> * <tr><td>1</td><td>Engineering</td><td>Jane Smith</td></tr> * </table></p> * * <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 { 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; } } 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; } /** * For internal use only. * * Used for obtaining the auto-commit txn config from the store, which * overrides this method to return it. */ TransactionConfig getAutoCommitTransactionConfig() { return null; } boolean isUpdateAllowed() { return false; } }