1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 2002, 2013 Oracle and/or its affiliates.  All rights reserved.
5  *
6  */
7 
8 package com.sleepycat.persist;
9 
10 import java.util.Map;
11 import java.util.SortedMap;
12 
13 import com.sleepycat.bind.EntityBinding;
14 import com.sleepycat.bind.EntryBinding;
15 import com.sleepycat.collections.StoredSortedMap;
16 import com.sleepycat.compat.DbCompat;
17 import com.sleepycat.db.Cursor;
18 import com.sleepycat.db.CursorConfig;
19 import com.sleepycat.db.Database;
20 import com.sleepycat.db.DatabaseEntry;
21 import com.sleepycat.db.DatabaseException;
22 import com.sleepycat.db.Environment;
23 import com.sleepycat.db.LockMode;
24 import com.sleepycat.db.OperationStatus;
25 import com.sleepycat.db.Transaction;
26 import com.sleepycat.db.TransactionConfig;
27 import com.sleepycat.persist.impl.PersistEntityBinding;
28 import com.sleepycat.persist.impl.PersistKeyAssigner;
29 import com.sleepycat.persist.model.Entity;
30 import com.sleepycat.persist.model.PrimaryKey;
31 
32 /**
33  * The primary index for an entity class and its primary key.
34  *
35  * <p>{@code PrimaryIndex} objects are thread-safe.  Multiple threads may
36  * safely call the methods of a shared {@code PrimaryIndex} object.</p>
37  *
38  * <p>{@code PrimaryIndex} implements {@link EntityIndex} to map the primary
39  * key type (PK) to the entity type (E).</p>
40  *
41  * <p>The {@link Entity} annotation may be used to define an entity class and
42  * the {@link PrimaryKey} annotation may be used to define a primary key as
43  * shown in the following example.</p>
44  *
45  * <pre class="code">
46  * {@literal @Entity}
47  * class Employee {
48  *
49  *     {@literal @PrimaryKey}
50  *     long id;
51  *
52  *     String name;
53  *
54  *     Employee(long id, String name) {
55  *         this.id = id;
56  *         this.name = name;
57  *     }
58  *
59  *     private Employee() {} // For bindings
60  * }</pre>
61  *
62  * <p>To obtain the {@code PrimaryIndex} for a given entity class, call {@link
63  * EntityStore#getPrimaryIndex EntityStore.getPrimaryIndex}, passing the
64  * primary key class and the entity class.  For example:</p>
65  *
66  * <pre class="code">
67  * EntityStore store = new EntityStore(...);
68  *
69  * {@code PrimaryIndex<Long, Employee>} primaryIndex =
70  *     store.getPrimaryIndex(Long.class, Employee.class);</pre>
71  * </pre>
72  *
73  * <p>Note that {@code Long.class} is passed as the primary key class, but the
74  * primary key field has the primitive type {@code long}.  When a primitive
75  * primary key field is used, the corresponding primitive wrapper class is used
76  * to access the primary index.  For more information on key field types, see
77  * {@link PrimaryKey}.</p>
78  *
79  * <p>The {@code PrimaryIndex} provides the primary storage and access methods
80  * for the instances of a particular entity class.  Entities are inserted and
81  * updated in the {@code PrimaryIndex} by calling a method in the family of
82  * {@link #put} methods.  The {@link #put} method will insert the entity if no
83  * entity with the same primary key already exists.  If an entity with the same
84  * primary key does exist, it will update the entity and return the existing
85  * (old) entity.  For example:</p>
86  *
87  * <pre class="code">
88  * Employee oldEntity;
89  * oldEntity = primaryIndex.put(new Employee(1, "Jane Smith"));    // Inserts an entity
90  * assert oldEntity == null;
91  * oldEntity = primaryIndex.put(new Employee(2, "Joan Smith"));    // Inserts an entity
92  * assert oldEntity == null;
93  * oldEntity = primaryIndex.put(new Employee(2, "Joan M. Smith")); // Updates an entity
94  * assert oldEntity != null;</pre>
95  *
96  * <p>The {@link #putNoReturn} method can be used to avoid the overhead of
97  * returning the existing entity, when the existing entity is not important to
98  * the application.  The return type of {@link #putNoReturn} is void.  For
99  * example:</p>
100  *
101  * <pre class="code">
102  * primaryIndex.putNoReturn(new Employee(1, "Jane Smith"));    // Inserts an entity
103  * primaryIndex.putNoReturn(new Employee(2, "Joan Smith"));    // Inserts an entity
104  * primaryIndex.putNoReturn(new Employee(2, "Joan M. Smith")); // Updates an entity</pre>
105  *
106  * <p>The {@link #putNoOverwrite} method can be used to ensure that an existing
107  * entity is not overwritten.  {@link #putNoOverwrite} returns true if the
108  * entity was inserted, or false if an existing entity exists and no action was
109  * taken.  For example:<p>
110  *
111  * <pre class="code">
112  * boolean inserted;
113  * inserted = primaryIndex.putNoOverwrite(new Employee(1, "Jane Smith"));    // Inserts an entity
114  * assert inserted;
115  * inserted = primaryIndex.putNoOverwrite(new Employee(2, "Joan Smith"));    // Inserts an entity
116  * assert inserted;
117  * inserted = primaryIndex.putNoOverwrite(new Employee(2, "Joan M. Smith")); // <strong>No action was taken!</strong>
118  * assert !inserted;</pre>
119  *
120  * <p>Primary key values must be unique, in other words, each instance of a
121  * given entity class must have a distinct primary key value.  Rather than
122  * assigning the unique primary key values yourself, a <em>sequence</em> can be
123  * used to assign sequential integer values automatically, starting with the
124  * value 1 (one).  A sequence is defined using the {@link PrimaryKey#sequence}
125  * annotation property.  For example:</p>
126  *
127  * <pre class="code">
128  * {@literal @Entity}
129  * class Employee {
130  *
131  *     {@literal @PrimaryKey(sequence="ID")}
132  *     long id;
133  *
134  *     String name;
135  *
136  *     Employee(String name) {
137  *         this.name = name;
138  *     }
139  *
140  *     private Employee() {} // For bindings
141  * }</pre>
142  *
143  * <p>The name of the sequence used above is "ID".  Any name can be used.  If
144  * the same sequence name is used in more than one entity class, the sequence
145  * will be shared by those classes, in other words, a single sequence of
146  * integers will be used for all instances of those classes.  See {@link
147  * PrimaryKey#sequence} for more information.</p>
148  *
149  * <p>Any method in the family of {@link #put} methods may be used to insert
150  * entities where the primary key is assigned from a sequence.  When the {@link
151  * #put} method returns, the primary key field of the entity object will be set
152  * to the assigned key value.  For example:</p>
153  *
154  * <pre class="code">
155  * Employee employee;
156  * employee = new Employee("Jane Smith");
157  * primaryIndex.putNoReturn(employee);    // Inserts an entity
158  * assert employee.id == 1;
159  * employee = new Employee("Joan Smith");
160  * primaryIndex.putNoReturn(employee);    // Inserts an entity
161  * assert employee.id == 2;</pre>
162  *
163  * <p>This begs the question:  How do you update an existing entity, without
164  * assigning a new primary key?  The answer is that the {@link #put} methods
165  * will only assign a new key from the sequence if the primary key field is
166  * zero or null (for reference types).  If an entity with a non-zero and
167  * non-null key field is passed to a {@link #put} method, any existing entity
168  * with that primary key value will be updated.  For example:</p>
169  *
170  * <pre class="code">
171  * Employee employee;
172  * employee = new Employee("Jane Smith");
173  * primaryIndex.putNoReturn(employee);    // Inserts an entity
174  * assert employee.id == 1;
175  * employee = new Employee("Joan Smith");
176  * primaryIndex.putNoReturn(employee);    // Inserts an entity
177  * assert employee.id == 2;
178  * employee.name = "Joan M. Smith";
179  * primaryIndex.putNoReturn(employee);    // Updates an existing entity
180  * assert employee.id == 2;</pre>
181  *
182  * <p>Since {@code PrimaryIndex} implements the {@link EntityIndex} interface,
183  * it shares the common index methods for retrieving and deleting entities,
184  * opening cursors and using transactions.  See {@link EntityIndex} for more
185  * information on these topics.</p>
186  *
187  * <p>Note that when using an index, keys and values are stored and retrieved
188  * by value not by reference.  In other words, if an entity object is stored
189  * and then retrieved, or retrieved twice, each object will be a separate
190  * instance.  For example, in the code below the assertion will always
191  * fail.</p>
192  * <pre class="code">
193  * MyKey key = ...;
194  * MyEntity entity1 = new MyEntity(key, ...);
195  * index.put(entity1);
196  * MyEntity entity2 = index.get(key);
197  * assert entity1 == entity2; // always fails!
198  * </pre>
199  *
200  * @author Mark Hayes
201  */
202 public class PrimaryIndex<PK, E> extends BasicIndex<PK, E> {
203 
204     private Class<E> entityClass;
205     private EntityBinding<E> entityBinding;
206     private SortedMap<PK, E> map;
207     private PersistKeyAssigner keyAssigner;
208 
209     /**
210      * Creates a primary index without using an <code>EntityStore</code>.
211      *
212      * <p>This constructor is not normally needed and is provided for
213      * applications that wish to use custom bindings along with the Direct
214      * Persistence Layer.  Normally, {@link EntityStore#getPrimaryIndex
215      * getPrimaryIndex} is used instead.</p>
216      *
217      * <p>Note that when this constructor is used directly, primary keys cannot
218      * be automatically assigned from a sequence.  The key assignment feature
219      * requires knowledge of the primary key field, which is only available if
220      * an <code>EntityStore</code> is used.  Of course, primary keys may be
221      * assigned from a sequence manually before calling the <code>put</code>
222      * methods in this class.</p>
223      *
224      * @param database the primary database.
225      *
226      * @param keyClass the class of the primary key.
227      *
228      * @param keyBinding the binding to be used for primary keys.
229      *
230      * @param entityClass the class of the entities stored in this index.
231      *
232      * @param entityBinding the binding to be used for entities.
233      *
234      * @throws DatabaseException the base class for all BDB exceptions.
235      */
PrimaryIndex(Database database, Class<PK> keyClass, EntryBinding<PK> keyBinding, Class<E> entityClass, EntityBinding<E> entityBinding)236     public PrimaryIndex(Database database,
237                         Class<PK> keyClass,
238                         EntryBinding<PK> keyBinding,
239                         Class<E> entityClass,
240                         EntityBinding<E> entityBinding)
241         throws DatabaseException {
242 
243         super(database, keyClass, keyBinding,
244               new EntityValueAdapter(entityClass, entityBinding, false));
245 
246         this.entityClass = entityClass;
247         this.entityBinding = entityBinding;
248 
249         if (entityBinding instanceof PersistEntityBinding) {
250             keyAssigner =
251                 ((PersistEntityBinding) entityBinding).getKeyAssigner();
252         }
253     }
254 
255     /**
256      * Returns the underlying database for this index.
257      *
258      * @return the database.
259      */
getDatabase()260     public Database getDatabase() {
261         return db;
262     }
263 
264     /**
265      * Returns the primary key class for this index.
266      *
267      * @return the key class.
268      */
getKeyClass()269     public Class<PK> getKeyClass() {
270         return keyClass;
271     }
272 
273     /**
274      * Returns the primary key binding for this index.
275      *
276      * @return the key binding.
277      */
getKeyBinding()278     public EntryBinding<PK> getKeyBinding() {
279         return keyBinding;
280     }
281 
282     /**
283      * Returns the entity class for this index.
284      *
285      * @return the entity class.
286      */
getEntityClass()287     public Class<E> getEntityClass() {
288         return entityClass;
289     }
290 
291     /**
292      * Returns the entity binding for this index.
293      *
294      * @return the entity binding.
295      */
getEntityBinding()296     public EntityBinding<E> getEntityBinding() {
297         return entityBinding;
298     }
299 
300     /**
301      * Inserts an entity and returns null, or updates it if the primary key
302      * already exists and returns the existing entity.
303      *
304      * <p>If a {@link PrimaryKey#sequence} is used and the primary key field of
305      * the given entity is null or zero, this method will assign the next value
306      * from the sequence to the primary key field of the given entity.</p>
307      *
308      * <p>Auto-commit is used implicitly if the store is transactional.</p>
309      *
310      * @param entity the entity to be inserted or updated.
311      *
312      * @return the existing entity that was updated, or null if the entity was
313      * inserted.
314      *
315      *
316      * @throws DatabaseException the base class for all BDB exceptions.
317      */
put(E entity)318     public E put(E entity)
319         throws DatabaseException {
320 
321         return put(null, entity);
322     }
323 
324     /**
325      * Inserts an entity and returns null, or updates it if the primary key
326      * already exists and returns the existing entity.
327      *
328      * <p>If a {@link PrimaryKey#sequence} is used and the primary key field of
329      * the given entity is null or zero, this method will assign the next value
330      * from the sequence to the primary key field of the given entity.</p>
331      *
332      * @param txn the transaction used to protect this operation, null to use
333      * auto-commit, or null if the store is non-transactional.
334      *
335      * @param entity the entity to be inserted or updated.
336      *
337      * @return the existing entity that was updated, or null if the entity was
338      * inserted.
339      *
340      *
341      * @throws DatabaseException the base class for all BDB exceptions.
342      */
put(Transaction txn, E entity)343     public E put(Transaction txn, E entity)
344         throws DatabaseException {
345 
346         DatabaseEntry keyEntry = new DatabaseEntry();
347         DatabaseEntry dataEntry = new DatabaseEntry();
348         assignKey(entity, keyEntry);
349 
350         boolean autoCommit = false;
351         Environment env = db.getEnvironment();
352         if (transactional &&
353             txn == null &&
354             DbCompat.getThreadTransaction(env) == null) {
355             txn = env.beginTransaction(null, getAutoCommitTransactionConfig());
356             autoCommit = true;
357         }
358 
359         CursorConfig cursorConfig = null;
360         if (concurrentDB) {
361             cursorConfig = new CursorConfig();
362             DbCompat.setWriteCursor(cursorConfig, true);
363         }
364         boolean failed = true;
365         Cursor cursor = db.openCursor(txn, cursorConfig);
366         LockMode lockMode = locking ? LockMode.RMW : null;
367         try {
368             while (true) {
369                 OperationStatus status =
370                     cursor.getSearchKey(keyEntry, dataEntry, lockMode);
371                 if (status == OperationStatus.SUCCESS) {
372                     E existing =
373                         entityBinding.entryToObject(keyEntry, dataEntry);
374                     entityBinding.objectToData(entity, dataEntry);
375                     cursor.put(keyEntry, dataEntry);
376                     failed = false;
377                     return existing;
378                 } else {
379                     entityBinding.objectToData(entity, dataEntry);
380                     status = cursor.putNoOverwrite(keyEntry, dataEntry);
381                     if (status != OperationStatus.KEYEXIST) {
382                         failed = false;
383                         return null;
384                     }
385                 }
386             }
387         } finally {
388             cursor.close();
389             if (autoCommit) {
390                 if (failed) {
391                     txn.abort();
392                 } else {
393                     txn.commit();
394                 }
395             }
396         }
397     }
398 
399     /**
400      * Inserts an entity, or updates it if the primary key already exists (does
401      * not return the existing entity).  This method may be used instead of
402      * {@link #put(Object)} to save the overhead of returning the existing
403      * entity.
404      *
405      * <p>If a {@link PrimaryKey#sequence} is used and the primary key field of
406      * the given entity is null or zero, this method will assign the next value
407      * from the sequence to the primary key field of the given entity.</p>
408      *
409      * <p>Auto-commit is used implicitly if the store is transactional.</p>
410      *
411      * @param entity the entity to be inserted or updated.
412      *
413      *
414      * @throws DatabaseException the base class for all BDB exceptions.
415      */
putNoReturn(E entity)416     public void putNoReturn(E entity)
417         throws DatabaseException {
418 
419         putNoReturn(null, entity);
420     }
421 
422     /**
423      * Inserts an entity, or updates it if the primary key already exists (does
424      * not return the existing entity).  This method may be used instead of
425      * {@link #put(Transaction,Object)} to save the overhead of returning the
426      * existing entity.
427      *
428      * <p>If a {@link PrimaryKey#sequence} is used and the primary key field of
429      * the given entity is null or zero, this method will assign the next value
430      * from the sequence to the primary key field of the given entity.</p>
431      *
432      * @param txn the transaction used to protect this operation, null to use
433      * auto-commit, or null if the store is non-transactional.
434      *
435      * @param entity the entity to be inserted or updated.
436      *
437      *
438      * @throws DatabaseException the base class for all BDB exceptions.
439      */
putNoReturn(Transaction txn, E entity)440     public void putNoReturn(Transaction txn, E entity)
441         throws DatabaseException {
442 
443         DatabaseEntry keyEntry = new DatabaseEntry();
444         DatabaseEntry dataEntry = new DatabaseEntry();
445         assignKey(entity, keyEntry);
446         entityBinding.objectToData(entity, dataEntry);
447 
448         db.put(txn, keyEntry, dataEntry);
449     }
450 
451     /**
452      * Inserts an entity and returns true, or returns false if the primary key
453      * already exists.
454      *
455      * <p>If a {@link PrimaryKey#sequence} is used and the primary key field of
456      * the given entity is null or zero, this method will assign the next value
457      * from the sequence to the primary key field of the given entity.</p>
458      *
459      * <p>Auto-commit is used implicitly if the store is transactional.</p>
460      *
461      * @param entity the entity to be inserted.
462      *
463      * @return true if the entity was inserted, or false if an entity with the
464      * same primary key is already present.
465      *
466      *
467      * @throws DatabaseException the base class for all BDB exceptions.
468      */
putNoOverwrite(E entity)469     public boolean putNoOverwrite(E entity)
470         throws DatabaseException {
471 
472         return putNoOverwrite(null, entity);
473     }
474 
475     /**
476      * Inserts an entity and returns true, or returns false if the primary key
477      * already exists.
478      *
479      * <p>If a {@link PrimaryKey#sequence} is used and the primary key field of
480      * the given entity is null or zero, this method will assign the next value
481      * from the sequence to the primary key field of the given entity.</p>
482      *
483      * @param txn the transaction used to protect this operation, null to use
484      * auto-commit, or null if the store is non-transactional.
485      *
486      * @param entity the entity to be inserted.
487      *
488      * @return true if the entity was inserted, or false if an entity with the
489      * same primary key is already present.
490      *
491      *
492      * @throws DatabaseException the base class for all BDB exceptions.
493      */
putNoOverwrite(Transaction txn, E entity)494     public boolean putNoOverwrite(Transaction txn, E entity)
495         throws DatabaseException {
496 
497         DatabaseEntry keyEntry = new DatabaseEntry();
498         DatabaseEntry dataEntry = new DatabaseEntry();
499         assignKey(entity, keyEntry);
500         entityBinding.objectToData(entity, dataEntry);
501 
502         OperationStatus status = db.putNoOverwrite(txn, keyEntry, dataEntry);
503 
504         return (status == OperationStatus.SUCCESS);
505     }
506 
507     /**
508      * If we are assigning primary keys from a sequence, assign the next key
509      * and set the primary key field.
510      */
assignKey(E entity, DatabaseEntry keyEntry)511     private void assignKey(E entity, DatabaseEntry keyEntry)
512         throws DatabaseException {
513 
514         if (keyAssigner != null) {
515             if (!keyAssigner.assignPrimaryKey(entity, keyEntry)) {
516                 entityBinding.objectToKey(entity, keyEntry);
517             }
518         } else {
519             entityBinding.objectToKey(entity, keyEntry);
520         }
521     }
522 
523     /*
524      * Of the EntityIndex methods only get()/map()/sortedMap() are implemented
525      * here.  All other methods are implemented by BasicIndex.
526      */
527 
get(PK key)528     public E get(PK key)
529         throws DatabaseException {
530 
531         return get(null, key, null);
532     }
533 
get(Transaction txn, PK key, LockMode lockMode)534     public E get(Transaction txn, PK key, LockMode lockMode)
535         throws DatabaseException {
536 
537         DatabaseEntry keyEntry = new DatabaseEntry();
538         DatabaseEntry dataEntry = new DatabaseEntry();
539         keyBinding.objectToEntry(key, keyEntry);
540 
541         OperationStatus status = db.get(txn, keyEntry, dataEntry, lockMode);
542 
543         if (status == OperationStatus.SUCCESS) {
544             if (entityBinding instanceof PersistEntityBinding) {
545                 return (E)((PersistEntityBinding) entityBinding).
546                            entryToObjectWithPriKey(key, dataEntry);
547             } else {
548                 return entityBinding.entryToObject(keyEntry, dataEntry);
549             }
550         } else {
551             return null;
552         }
553     }
554 
map()555     public Map<PK, E> map() {
556         return sortedMap();
557     }
558 
sortedMap()559     public synchronized SortedMap<PK, E> sortedMap() {
560         if (map == null) {
561             map = new StoredSortedMap(db, keyBinding, entityBinding, true);
562         }
563         return map;
564     }
565 
566     /**
567      * @hidden
568      * For internal use only.
569      *
570      * Used for obtaining the auto-commit txn config from the store, which
571      * overrides this method to return it.
572      */
getAutoCommitTransactionConfig()573     TransactionConfig getAutoCommitTransactionConfig() {
574         return null;
575     }
576 
isUpdateAllowed()577     boolean isUpdateAllowed() {
578         return true;
579     }
580 }
581