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