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