001: /*-
002: * See the file LICENSE for redistribution information.
003: *
004: * Copyright (c) 2002,2008 Oracle. All rights reserved.
005: *
006: * $Id: PrimaryIndex.java,v 1.17.2.2 2008/01/07 15:14:18 cwl Exp $
007: */
008:
009: package com.sleepycat.persist;
010:
011: import java.util.Map;
012: import java.util.SortedMap;
013:
014: import com.sleepycat.bind.EntityBinding;
015: import com.sleepycat.bind.EntryBinding;
016: import com.sleepycat.collections.StoredSortedMap;
017: import com.sleepycat.je.Cursor;
018: import com.sleepycat.je.Database;
019: import com.sleepycat.je.DatabaseEntry;
020: import com.sleepycat.je.DatabaseException;
021: import com.sleepycat.je.Environment;
022: import com.sleepycat.je.LockMode;
023: import com.sleepycat.je.OperationStatus;
024: import com.sleepycat.je.Transaction;
025: import com.sleepycat.persist.impl.PersistEntityBinding;
026: import com.sleepycat.persist.impl.PersistKeyAssigner;
027: import com.sleepycat.persist.model.Entity;
028: import com.sleepycat.persist.model.PrimaryKey;
029:
030: /**
031: * The primary index for an entity class and its primary key.
032: *
033: * <p>{@code PrimaryIndex} objects are thread-safe. Multiple threads may
034: * safely call the methods of a shared {@code PrimaryIndex} object.</p>
035: *
036: * <p>{@code PrimaryIndex} implements {@link EntityIndex} to map the primary
037: * key type (PK) to the entity type (E).</p>
038: *
039: * <p>The {@link Entity} annotation may be used to define an entity class and
040: * the {@link PrimaryKey} annotation may be used to define a primary key as
041: * shown in the following example.</p>
042: *
043: * <pre class="code">
044: * {@literal @Entity}
045: * class Employee {
046: *
047: * {@literal @PrimaryKey}
048: * long id;
049: *
050: * String name;
051: *
052: * Employee(long id, String name) {
053: * this.id = id;
054: * this.name = name;
055: * }
056: *
057: * private Employee() {} // For bindings
058: * }</pre>
059: *
060: * <p>To obtain the {@code PrimaryIndex} for a given entity class, call {@link
061: * EntityStore#getPrimaryIndex EntityStore.getPrimaryIndex}, passing the
062: * primary key class and the entity class. For example:</p>
063: *
064: * <pre class="code">
065: * EntityStore store = new EntityStore(...);
066: *
067: * {@code PrimaryIndex<Long,Employee>} primaryIndex =
068: * store.getPrimaryIndex(Long.class, Employee.class);</pre>
069: * </pre>
070: *
071: * <p>Note that {@code Long.class} is passed as the primary key class, but the
072: * primary key field has the primitive type {@code long}. When a primitive
073: * primary key field is used, the corresponding primitive wrapper class is used
074: * to access the primary index. For more information on key field types, see
075: * {@link PrimaryKey}.</p>
076: *
077: * <p>The {@code PrimaryIndex} provides the primary storage and access methods
078: * for the instances of a particular entity class. Entities are inserted and
079: * updated in the {@code PrimaryIndex} by calling a method in the family of
080: * {@link #put} methods. The {@link #put} method will insert the entity if no
081: * entity with the same primary key already exists. If an entity with the same
082: * primary key does exist, it will update the entity and return the existing
083: * (old) entity. For example:</p>
084: *
085: * <pre class="code">
086: * Employee oldEntity;
087: * oldEntity = primaryIndex.put(new Employee(1, "Jane Smith")); // Inserts an entity
088: * assert oldEntity == null;
089: * oldEntity = primaryIndex.put(new Employee(2, "Joan Smith")); // Inserts an entity
090: * assert oldEntity == null;
091: * oldEntity = primaryIndex.put(new Employee(2, "Joan M. Smith")); // Updates an entity
092: * assert oldEntity != null;</pre>
093: *
094: * <p>The {@link #putNoReturn} method can be used to avoid the overhead of
095: * returning the existing entity, when the existing entity is not important to
096: * the application. The return type of {@link #putNoReturn} is void. For
097: * example:</p>
098: *
099: * <pre class="code">
100: * primaryIndex.putNoReturn(new Employee(1, "Jane Smith")); // Inserts an entity
101: * primaryIndex.putNoReturn(new Employee(2, "Joan Smith")); // Inserts an entity
102: * primaryIndex.putNoReturn(new Employee(2, "Joan M. Smith")); // Updates an entity</pre>
103: *
104: * <p>The {@link #putNoOverwrite} method can be used to ensure that an existing
105: * entity is not overwritten. {@link #putNoOverwrite} returns true if the
106: * entity was inserted, or false if an existing entity exists and no action was
107: * taken. For example:<p>
108: *
109: * <pre class="code">
110: * boolean inserted;
111: * inserted = primaryIndex.putNoOverwrite(new Employee(1, "Jane Smith")); // Inserts an entity
112: * assert inserted;
113: * inserted = primaryIndex.putNoOverwrite(new Employee(2, "Joan Smith")); // Inserts an entity
114: * assert inserted;
115: * inserted = primaryIndex.putNoOverwrite(new Employee(2, "Joan M. Smith")); // <strong>No action was taken!</strong>
116: * assert !inserted;</pre>
117: *
118: * <p>Primary key values must be unique, in other words, each instance of a
119: * given entity class must have a distinct primary key value. Rather than
120: * assigning the unique primary key values yourself, a <em>sequence</em> can be
121: * used to assign sequential integer values automatically, starting with the
122: * value 1 (one). A sequence is defined using the {@link PrimaryKey#sequence}
123: * annotation property. For example:</p>
124: *
125: * <pre class="code">
126: * {@literal @Entity}
127: * class Employee {
128: *
129: * {@literal @PrimaryKey(sequence="ID")}
130: * long id;
131: *
132: * String name;
133: *
134: * Employee(String name) {
135: * this.name = name;
136: * }
137: *
138: * private Employee() {} // For bindings
139: * }</pre>
140: *
141: * <p>The name of the sequence used above is "ID". Any name can be used. If
142: * the same sequence name is used in more than one entity class, the sequence
143: * will be shared by those classes, in other words, a single sequence of
144: * integers will be used for all instances of those classes. See {@link
145: * PrimaryKey#sequence} for more information.</p>
146: *
147: * <p>Any method in the family of {@link #put} methods may be used to insert
148: * entities where the primary key is assigned from a sequence. When the {@link
149: * #put} method returns, the primary key field of the entity object will be set
150: * to the assigned key value. For example:</p>
151: *
152: * <pre class="code">
153: * Employee employee;
154: * employee = new Employee("Jane Smith");
155: * primaryIndex.putNoReturn(employee); // Inserts an entity
156: * assert employee.id == 1;
157: * employee = new Employee("Joan Smith");
158: * primaryIndex.putNoReturn(employee); // Inserts an entity
159: * assert employee.id == 2;</pre>
160: *
161: * <p>This begs the question: How do you update an existing entity, without
162: * assigning a new primary key? The answer is that the {@link #put} methods
163: * will only assign a new key from the sequence if the primary key field is
164: * zero or null (for reference types). If an entity with a non-zero and
165: * non-null key field is passed to a {@link #put} method, any existing entity
166: * with that primary key value will be updated. For example:</p>
167: *
168: * <pre class="code">
169: * Employee employee;
170: * employee = new Employee("Jane Smith");
171: * primaryIndex.putNoReturn(employee); // Inserts an entity
172: * assert employee.id == 1;
173: * employee = new Employee("Joan Smith");
174: * primaryIndex.putNoReturn(employee); // Inserts an entity
175: * assert employee.id == 2;
176: * employee.name = "Joan M. Smith";
177: * primaryIndex.putNoReturn(employee); // Updates an existing entity
178: * assert employee.id == 2;</pre>
179: *
180: * <p>Since {@code PrimaryIndex} implements the {@link EntityIndex} interface,
181: * it shares the common index methods for retrieving and deleting entities,
182: * opening cursors and using transactions. See {@link EntityIndex} for more
183: * information on these topics.</p>
184: *
185: * <p>Note that when using an index, keys and values are stored and retrieved
186: * by value not by reference. In other words, if an entity object is stored
187: * and then retrieved, or retrieved twice, each object will be a separate
188: * instance. For example, in the code below the assertion will always
189: * fail.</p>
190: * <pre class="code">
191: * MyKey key = ...;
192: * MyEntity entity1 = new MyEntity(key, ...);
193: * index.put(entity1);
194: * MyEntity entity2 = index.get(key);
195: * assert entity1 == entity2; // always fails!
196: * </pre>
197: *
198: * @author Mark Hayes
199: */
200: public class PrimaryIndex<PK, E> extends BasicIndex<PK, E> {
201:
202: private Class<E> entityClass;
203: private EntityBinding entityBinding;
204: private SortedMap<PK, E> map;
205: private PersistKeyAssigner keyAssigner;
206:
207: /**
208: * Creates a primary index without using an <code>EntityStore</code>.
209: *
210: * <p>This constructor is not normally needed and is provided for
211: * applications that wish to use custom bindings along with the Direct
212: * Persistence Layer. Normally, {@link EntityStore#getPrimaryIndex
213: * getPrimaryIndex} is used instead.</p>
214: *
215: * <p>Note that when this constructor is used directly, primary keys cannot
216: * be automatically assigned from a sequence. The key assignment feature
217: * requires knowledge of the primary key field, which is only available if
218: * an <code>EntityStore</code> is used. Of course, primary keys may be
219: * assigned from a sequence manually before calling the <code>put</code>
220: * methods in this class.</p>
221: *
222: * @param database the primary database.
223: *
224: * @param keyClass the class of the primary key.
225: *
226: * @param keyBinding the binding to be used for primary keys.
227: *
228: * @param entityClass the class of the entities stored in this index.
229: *
230: * @param entityBinding the binding to be used for entities.
231: */
232: public PrimaryIndex(Database database, Class<PK> keyClass,
233: EntryBinding keyBinding, Class<E> entityClass,
234: EntityBinding entityBinding) throws DatabaseException {
235:
236: super (database, keyClass, keyBinding, new EntityValueAdapter(
237: entityClass, entityBinding, false));
238:
239: this .entityClass = entityClass;
240: this .entityBinding = entityBinding;
241:
242: if (entityBinding instanceof PersistEntityBinding) {
243: keyAssigner = ((PersistEntityBinding) entityBinding)
244: .getKeyAssigner();
245: }
246: }
247:
248: /**
249: * Returns the underlying database for this index.
250: *
251: * @return the database.
252: */
253: public Database getDatabase() {
254: return db;
255: }
256:
257: /**
258: * Returns the primary key class for this index.
259: *
260: * @return the key class.
261: */
262: public Class<PK> getKeyClass() {
263: return keyClass;
264: }
265:
266: /**
267: * Returns the primary key binding for this index.
268: *
269: * @return the key binding.
270: */
271: public EntryBinding getKeyBinding() {
272: return keyBinding;
273: }
274:
275: /**
276: * Returns the entity class for this index.
277: *
278: * @return the entity class.
279: */
280: public Class<E> getEntityClass() {
281: return entityClass;
282: }
283:
284: /**
285: * Returns the entity binding for this index.
286: *
287: * @return the entity binding.
288: */
289: public EntityBinding getEntityBinding() {
290: return entityBinding;
291: }
292:
293: /**
294: * Inserts an entity and returns null, or updates it if the primary key
295: * already exists and returns the existing entity.
296: *
297: * <p>If a {@link PrimaryKey#sequence} is used and the primary key field of
298: * the given entity is null or zero, this method will assign the next value
299: * from the sequence to the primary key field of the given entity.</p>
300: *
301: * <p>Auto-commit is used implicitly if the store is transactional.</p>
302: *
303: * @param entity the entity to be inserted or updated.
304: *
305: * @return the existing entity that was updated, or null if the entity was
306: * inserted.
307: */
308: public E put(E entity) throws DatabaseException {
309:
310: return put(null, entity);
311: }
312:
313: /**
314: * Inserts an entity and returns null, or updates it if the primary key
315: * already exists and returns the existing entity.
316: *
317: * <p>If a {@link PrimaryKey#sequence} is used and the primary key field of
318: * the given entity is null or zero, this method will assign the next value
319: * from the sequence to the primary key field of the given entity.</p>
320: *
321: * @param txn the transaction used to protect this operation, null to use
322: * auto-commit, or null if the store is non-transactional.
323: *
324: * @param entity the entity to be inserted or updated.
325: *
326: * @return the existing entity that was updated, or null if the entity was
327: * inserted.
328: */
329: public E put(Transaction txn, E entity) throws DatabaseException {
330:
331: DatabaseEntry keyEntry = new DatabaseEntry();
332: DatabaseEntry dataEntry = new DatabaseEntry();
333: assignKey(entity, keyEntry);
334:
335: boolean autoCommit = false;
336: Environment env = db.getEnvironment();
337: if (transactional && txn == null
338: && env.getThreadTransaction() == null) {
339: txn = env.beginTransaction(null, null);
340: autoCommit = true;
341: }
342:
343: boolean failed = true;
344: Cursor cursor = db.openCursor(txn, null);
345: try {
346: while (true) {
347: OperationStatus status = cursor.getSearchKey(keyEntry,
348: dataEntry, LockMode.RMW);
349: if (status == OperationStatus.SUCCESS) {
350: E existing = (E) entityBinding.entryToObject(
351: keyEntry, dataEntry);
352: entityBinding.objectToData(entity, dataEntry);
353: cursor.put(keyEntry, dataEntry);
354: failed = false;
355: return existing;
356: } else {
357: entityBinding.objectToData(entity, dataEntry);
358: status = cursor.putNoOverwrite(keyEntry, dataEntry);
359: if (status != OperationStatus.KEYEXIST) {
360: failed = false;
361: return null;
362: }
363: }
364: }
365: } finally {
366: cursor.close();
367: if (autoCommit) {
368: if (failed) {
369: txn.abort();
370: } else {
371: txn.commit();
372: }
373: }
374: }
375: }
376:
377: /**
378: * Inserts an entity, or updates it if the primary key already exists (does
379: * not return the existing entity). This method may be used instead of
380: * {@link #put(Object)} to save the overhead of returning the existing
381: * entity.
382: *
383: * <p>If a {@link PrimaryKey#sequence} is used and the primary key field of
384: * the given entity is null or zero, this method will assign the next value
385: * from the sequence to the primary key field of the given entity.</p>
386: *
387: * <p>Auto-commit is used implicitly if the store is transactional.</p>
388: *
389: * @param entity the entity to be inserted or updated.
390: */
391: public void putNoReturn(E entity) throws DatabaseException {
392:
393: putNoReturn(null, entity);
394: }
395:
396: /**
397: * Inserts an entity, or updates it if the primary key already exists (does
398: * not return the existing entity). This method may be used instead of
399: * {@link #put(Transaction,Object)} to save the overhead of returning the
400: * existing entity.
401: *
402: * <p>If a {@link PrimaryKey#sequence} is used and the primary key field of
403: * the given entity is null or zero, this method will assign the next value
404: * from the sequence to the primary key field of the given entity.</p>
405: *
406: * @param txn the transaction used to protect this operation, null to use
407: * auto-commit, or null if the store is non-transactional.
408: *
409: * @param entity the entity to be inserted or updated.
410: */
411: public void putNoReturn(Transaction txn, E entity)
412: throws DatabaseException {
413:
414: DatabaseEntry keyEntry = new DatabaseEntry();
415: DatabaseEntry dataEntry = new DatabaseEntry();
416: assignKey(entity, keyEntry);
417: entityBinding.objectToData(entity, dataEntry);
418:
419: db.put(txn, keyEntry, dataEntry);
420: }
421:
422: /**
423: * Inserts an entity and returns true, or returns false if the primary key
424: * already exists.
425: *
426: * <p>If a {@link PrimaryKey#sequence} is used and the primary key field of
427: * the given entity is null or zero, this method will assign the next value
428: * from the sequence to the primary key field of the given entity.</p>
429: *
430: * <p>Auto-commit is used implicitly if the store is transactional.</p>
431: *
432: * @param entity the entity to be inserted.
433: *
434: * @return true if the entity was inserted, or false if an entity with the
435: * same primary key is already present.
436: */
437: public boolean putNoOverwrite(E entity) throws DatabaseException {
438:
439: return putNoOverwrite(null, entity);
440: }
441:
442: /**
443: * Inserts an entity and returns true, or returns false if the primary key
444: * already exists.
445: *
446: * <p>If a {@link PrimaryKey#sequence} is used and the primary key field of
447: * the given entity is null or zero, this method will assign the next value
448: * from the sequence to the primary key field of the given entity.</p>
449: *
450: * @param txn the transaction used to protect this operation, null to use
451: * auto-commit, or null if the store is non-transactional.
452: *
453: * @param entity the entity to be inserted.
454: *
455: * @return true if the entity was inserted, or false if an entity with the
456: * same primary key is already present.
457: */
458: public boolean putNoOverwrite(Transaction txn, E entity)
459: throws DatabaseException {
460:
461: DatabaseEntry keyEntry = new DatabaseEntry();
462: DatabaseEntry dataEntry = new DatabaseEntry();
463: assignKey(entity, keyEntry);
464: entityBinding.objectToData(entity, dataEntry);
465:
466: OperationStatus status = db.putNoOverwrite(txn, keyEntry,
467: dataEntry);
468:
469: return (status == OperationStatus.SUCCESS);
470: }
471:
472: /**
473: * If we are assigning primary keys from a sequence, assign the next key
474: * and set the primary key field.
475: */
476: private void assignKey(E entity, DatabaseEntry keyEntry)
477: throws DatabaseException {
478:
479: if (keyAssigner != null) {
480: if (!keyAssigner.assignPrimaryKey(entity, keyEntry)) {
481: entityBinding.objectToKey(entity, keyEntry);
482: }
483: } else {
484: entityBinding.objectToKey(entity, keyEntry);
485: }
486: }
487:
488: /*
489: * Of the EntityIndex methods only get()/map()/sortedMap() are implemented
490: * here. All other methods are implemented by BasicIndex.
491: */
492:
493: public E get(PK key) throws DatabaseException {
494:
495: return get(null, key, null);
496: }
497:
498: public E get(Transaction txn, PK key, LockMode lockMode)
499: throws DatabaseException {
500:
501: DatabaseEntry keyEntry = new DatabaseEntry();
502: DatabaseEntry dataEntry = new DatabaseEntry();
503: keyBinding.objectToEntry(key, keyEntry);
504:
505: OperationStatus status = db.get(txn, keyEntry, dataEntry,
506: lockMode);
507:
508: if (status == OperationStatus.SUCCESS) {
509: return (E) entityBinding.entryToObject(keyEntry, dataEntry);
510: } else {
511: return null;
512: }
513: }
514:
515: public Map<PK, E> map() {
516: return sortedMap();
517: }
518:
519: public synchronized SortedMap<PK, E> sortedMap() {
520: if (map == null) {
521: map = new StoredSortedMap(db, keyBinding, entityBinding,
522: true);
523: }
524: return map;
525: }
526: }
|