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