1/*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 2002-2009 Oracle. All rights reserved. 5 * 6 * $Id$ 7 */ 8 9package com.sleepycat.persist; 10 11import java.util.Set; 12 13import com.sleepycat.db.Database; // for javadoc 14import com.sleepycat.db.DatabaseConfig; 15import com.sleepycat.db.DatabaseException; 16import com.sleepycat.db.Environment; 17import com.sleepycat.db.SecondaryConfig; 18import com.sleepycat.db.Sequence; 19import com.sleepycat.db.SequenceConfig; 20import com.sleepycat.db.Transaction; 21import com.sleepycat.persist.evolve.EvolveConfig; 22import com.sleepycat.persist.evolve.EvolveStats; 23import com.sleepycat.persist.evolve.IncompatibleClassException; 24import com.sleepycat.persist.evolve.Mutations; 25import com.sleepycat.persist.impl.Store; 26import com.sleepycat.persist.model.DeleteAction; 27import com.sleepycat.persist.model.Entity; // for javadoc 28import com.sleepycat.persist.model.EntityModel; 29import com.sleepycat.persist.model.PrimaryKey; 30import com.sleepycat.persist.model.SecondaryKey; 31 32/** 33 * A store for managing persistent entity objects. 34 * 35 * <p>{@code EntityStore} objects are thread-safe. Multiple threads may safely 36 * call the methods of a shared {@code EntityStore} object.</p> 37 * 38 * <p>See the {@link <a href="package-summary.html#example">package 39 * summary example</a>} for an example of using an {@code EntityStore}.</p> 40 * 41 * <p>Before creating an <code>EntityStore</code> you must create an {@link 42 * Environment} object using the Berkeley DB engine API. The environment may 43 * contain any number of entity stores and their associated databases, as well 44 * as other databases not associated with an entity store.</p> 45 * 46 * <p>An entity store is based on an {@link EntityModel}: a data model which 47 * defines persistent classes (<em>entity classes</em>), primary keys, 48 * secondary keys, and relationships between entities. A primary index is 49 * created for each entity class. An associated secondary index is created for 50 * each secondary key. The {@link Entity}, {@link PrimaryKey} and {@link 51 * SecondaryKey} annotations may be used to define entities and keys.</p> 52 * 53 * <p>To use an <code>EntityStore</code>, first obtain {@link PrimaryIndex} and 54 * {@link SecondaryIndex} objects by calling {@link #getPrimaryIndex 55 * getPrimaryIndex} and {@link #getSecondaryIndex getSecondaryIndex}. Then use 56 * these indices to store and access entity records by key.</p> 57 * 58 * <p>Although not normally needed, you can also use the entity store along 59 * with the {@link com.sleepycat.db Base API}. Methods in the {@link 60 * PrimaryIndex} and {@link SecondaryIndex} classes may be used to obtain 61 * databases and bindings. The databases may be used directly for accessing 62 * entity records. The bindings should be called explicitly to translate 63 * between {@link com.sleepycat.db.DatabaseEntry} objects and entity model 64 * objects.</p> 65 * 66 * <p>Each primary and secondary index is associated internally with a {@link 67 * Database}. With any of the above mentioned use cases, methods are provided 68 * that may be used for database performance tuning. The {@link 69 * #setPrimaryConfig setPrimaryConfig} and {@link #setSecondaryConfig 70 * setSecondaryConfig} methods may be called anytime before a database is 71 * opened via {@link #getPrimaryIndex getPrimaryIndex} or {@link 72 * #getSecondaryIndex getSecondaryIndex}. The {@link #setSequenceConfig 73 * setSequenceConfig} method may be called anytime before {@link #getSequence 74 * getSequence} is called or {@link #getPrimaryIndex getPrimaryIndex} is called 75 * for a primary index associated with that sequence.</p> 76 * 77 * 78 * @author Mark Hayes 79 */ 80public class EntityStore { 81 82 private Store store; 83 84 /** 85 * Opens an entity store in a given environment. 86 * 87 * @param env an open Berkeley DB Environment. 88 * 89 * @param storeName the name of the entity store within the given 90 * environment. An empty string is allowed. Named stores may be used to 91 * distinguish multiple sets of persistent entities for the same entity 92 * classes in a single environment. Underlying database names are prefixed 93 * with the store name. 94 * 95 * @param config the entity store configuration, or null to use default 96 * configuration properties. 97 * 98 * @throws IncompatibleClassException if an incompatible class change has 99 * been made and mutations are not configured for handling the change. See 100 * {@link com.sleepycat.persist.evolve Class Evolution} for more 101 * information. 102 */ 103 public EntityStore(Environment env, String storeName, StoreConfig config) 104 throws StoreExistsException, 105 StoreNotFoundException, 106 IncompatibleClassException, 107 DatabaseException { 108 109 store = new Store(env, storeName, config, false /*rawAccess*/); 110 } 111 112 /** 113 * Returns the environment associated with this store. 114 * 115 * @return the environment. 116 */ 117 public Environment getEnvironment() { 118 return store.getEnvironment(); 119 } 120 121 /** 122 * Returns a copy of the entity store configuration. 123 * 124 * @return the config. 125 */ 126 public StoreConfig getConfig() { 127 return store.getConfig(); 128 } 129 130 /** 131 * Returns the name of this store. 132 * 133 * @return the name. 134 */ 135 public String getStoreName() { 136 return store.getStoreName(); 137 } 138 139 140 /** 141 * Returns the current entity model for this store. The current model is 142 * derived from the configured entity model and the live entity class 143 * definitions. 144 * 145 * @return the model. 146 */ 147 public EntityModel getModel() { 148 return store.getModel(); 149 } 150 151 /** 152 * Returns the set of mutations that were configured when the store was 153 * opened, or if none were configured, the set of mutations that were 154 * configured and stored previously. 155 * 156 * @return the mutations. 157 */ 158 public Mutations getMutations() { 159 return store.getMutations(); 160 } 161 162 /** 163 * Returns the primary index for a given entity class, opening it if 164 * necessary. 165 * 166 * <p>If they are not already open, the primary and secondary databases for 167 * the entity class are created/opened together in a single internal 168 * transaction. When the secondary indices are opened, that can cascade to 169 * open other related primary indices.</p> 170 * 171 * @param primaryKeyClass the class of the entity's primary key field, or 172 * the corresponding primitive wrapper class if the primary key field type 173 * is a primitive. 174 * 175 * @param entityClass the entity class for which to open the primary index. 176 * 177 * @return the primary index. 178 * 179 * @throws IllegalArgumentException if the entity class or classes 180 * referenced by it are not persistent, or the primary key class does not 181 * match the entity's primary key field, or if metadata for the entity or 182 * primary key is invalid. 183 */ 184 public <PK,E> PrimaryIndex<PK,E> getPrimaryIndex(Class<PK> primaryKeyClass, 185 Class<E> entityClass) 186 throws DatabaseException { 187 188 return store.getPrimaryIndex 189 (primaryKeyClass, primaryKeyClass.getName(), 190 entityClass, entityClass.getName()); 191 } 192 193 /** 194 * Returns a secondary index for a given primary index and secondary key, 195 * opening it if necessary. 196 * 197 * <p><em>NOTE:</em> If the secondary key field is declared in a subclass 198 * of the entity class, use {@link #getSubclassIndex} instead.</p> 199 * 200 * <p>If a {@link SecondaryKey#relatedEntity} is used and the primary index 201 * for the related entity is not already open, it will be opened by this 202 * method. That will, in turn, open its secondary indices, which can 203 * cascade to open other primary indices.</p> 204 * 205 * @param primaryIndex the primary index associated with the returned 206 * secondary index. The entity class of the primary index, or one of its 207 * superclasses, must contain a secondary key with the given secondary key 208 * class and key name. 209 * 210 * @param keyClass the class of the secondary key field, or the 211 * corresponding primitive wrapper class if the secondary key field type is 212 * a primitive. 213 * 214 * @param keyName the name of the secondary key field, or the {@link 215 * SecondaryKey#name} if this name annotation property was specified. 216 * 217 * @return the secondary index. 218 * 219 * @throws IllegalArgumentException if the entity class or one of its 220 * superclasses does not contain a key field of the given key class and key 221 * name, or if the metadata for the secondary key is invalid. 222 */ 223 public <SK,PK,E> SecondaryIndex<SK,PK,E> 224 getSecondaryIndex(PrimaryIndex<PK,E> primaryIndex, 225 Class<SK> keyClass, 226 String keyName) 227 throws DatabaseException { 228 229 return store.getSecondaryIndex 230 (primaryIndex, primaryIndex.getEntityClass(), 231 primaryIndex.getEntityClass().getName(), 232 keyClass, keyClass.getName(), keyName); 233 } 234 235 /** 236 * Returns a secondary index for a secondary key in an entity subclass, 237 * opening it if necessary. 238 * 239 * <p>If a {@link SecondaryKey#relatedEntity} is used and the primary index 240 * for the related entity is not already open, it will be opened by this 241 * method. That will, in turn, open its secondary indices, which can 242 * cascade to open other primary indices.</p> 243 * 244 * @param primaryIndex the primary index associated with the returned 245 * secondary index. The entity class of the primary index, or one of its 246 * superclasses, must contain a secondary key with the given secondary key 247 * class and key name. 248 * 249 * @param entitySubclass a subclass of the entity class for the primary 250 * index. The entity subclass must contain a secondary key with the given 251 * secondary key class and key name. 252 * 253 * @param keyClass the class of the secondary key field, or the 254 * corresponding primitive wrapper class if the secondary key field type is 255 * a primitive. 256 * 257 * @param keyName the name of the secondary key field, or the {@link 258 * SecondaryKey#name} if this name annotation property was specified. 259 * 260 * @return the secondary index. 261 * 262 * @throws IllegalArgumentException if the given entity subclass does not 263 * contain a key field of the given key class and key name, or if the 264 * metadata for the secondary key is invalid. 265 */ 266 public <SK,PK,E1,E2 extends E1> SecondaryIndex<SK,PK,E2> 267 getSubclassIndex(PrimaryIndex<PK,E1> primaryIndex, 268 Class<E2> entitySubclass, 269 Class<SK> keyClass, 270 String keyName) 271 throws DatabaseException { 272 273 /* Make subclass metadata available before getting the index. */ 274 getModel().getClassMetadata(entitySubclass.getName()); 275 276 return store.getSecondaryIndex 277 (primaryIndex, entitySubclass, 278 primaryIndex.getEntityClass().getName(), 279 keyClass, keyClass.getName(), keyName); 280 } 281 282 /** 283 * Performs conversion of unevolved objects in order to reduce lazy 284 * conversion overhead. Evolution may be performed concurrently with 285 * normal access to the store. 286 * 287 * <p>Conversion is performed one entity class at a time. An entity class 288 * is converted only if it has {@link Mutations} associated with it via 289 * {@link StoreConfig#setMutations StoreConfig.setMutations}.</p> 290 * 291 * <p>Conversion of an entity class is performed by reading each entity, 292 * converting it if necessary, and updating it if conversion was performed. 293 * When all instances of an entity class are converted, references to the 294 * appropriate {@link Mutations} are deleted. Therefore, if this method is 295 * called twice successfully without changing class definitions, the second 296 * call will do nothing.</p> 297 * 298 * @see com.sleepycat.persist.evolve Class Evolution 299 */ 300 public EvolveStats evolve(EvolveConfig config) 301 throws DatabaseException { 302 303 return store.evolve(config); 304 } 305 306 /** 307 * Deletes all instances of this entity class and its (non-entity) 308 * subclasses. 309 * 310 * <p>The primary database for the given entity class will be truncated and 311 * all secondary databases will be removed. The primary and secondary 312 * databases associated with the entity class must not be open except by 313 * this store, since database truncation/removal is only possible when the 314 * database is not open. The databases to be truncated/removed will be 315 * closed before performing this operation, if they were previously opened 316 * by this store.</p> 317 * 318 * <p>Auto-commit is used implicitly if the store is transactional.</p> 319 * 320 * @param entityClass the entity class whose instances are to be deleted. 321 */ 322 public void truncateClass(Class entityClass) 323 throws DatabaseException { 324 325 store.truncateClass(null, entityClass); 326 } 327 328 /** 329 * Deletes all instances of this entity class and its (non-entity) 330 * subclasses. 331 * 332 * <p>The primary database for the given entity class will be truncated and 333 * all secondary databases will be removed. The primary and secondary 334 * databases associated with the entity class must not be open except by 335 * this store, since database truncation/removal is only possible when the 336 * database is not open. The databases to be truncated/removed will be 337 * closed before performing this operation, if they were previously opened 338 * by this store.</p> 339 * 340 * @param txn the transaction used to protect this operation, null to use 341 * auto-commit, or null if the store is non-transactional. 342 * 343 * @param entityClass the entity class whose instances are to be deleted. 344 */ 345 public void truncateClass(Transaction txn, Class entityClass) 346 throws DatabaseException { 347 348 store.truncateClass(txn, entityClass); 349 } 350 351 352 /** 353 * Closes the primary and secondary databases for the given entity class 354 * that were opened via this store. The caller must ensure that the 355 * primary and secondary indices obtained from this store are no longer in 356 * use. 357 * 358 * @param entityClass the entity class whose databases are to be closed. 359 */ 360 public void closeClass(Class entityClass) 361 throws DatabaseException { 362 363 store.closeClass(entityClass); 364 } 365 366 /** 367 * Closes all databases and sequences that were opened via this store. The 368 * caller must ensure that no databases opened via this store are in use. 369 */ 370 public void close() 371 throws DatabaseException { 372 373 store.close(); 374 } 375 376 /** 377 * Returns a named sequence for using Berkeley DB engine API directly, 378 * opening it if necessary. 379 * 380 * @param name the sequence name, which is normally defined using the 381 * {@link PrimaryKey#sequence} annotation property. 382 * 383 * @return the open sequence for the given sequence name. 384 */ 385 public Sequence getSequence(String name) 386 throws DatabaseException { 387 388 return store.getSequence(name); 389 } 390 391 /** 392 * Returns the default Berkeley DB engine API configuration for a named key 393 * sequence. 394 * 395 * </p>The returned configuration is as follows. All other properties have 396 * default values.</p> 397 * <ul> 398 * <li>The {@link SequenceConfig#setInitialValue InitialValue} is one.</li> 399 * <li>The {@link SequenceConfig#setRange Range} minimum is one.</li> 400 * <li>The {@link SequenceConfig#setCacheSize CacheSize} is 100.</li> 401 * <li>{@link SequenceConfig#setAutoCommitNoSync AutoCommitNoSync} is 402 * true.</li> 403 * <li>{@link SequenceConfig#setAllowCreate AllowCreate} is set to the 404 * inverse of the store {@link StoreConfig#setReadOnly ReadOnly}. 405 * setting.</li> 406 * </ul> 407 * 408 * @param name the sequence name, which is normally defined using the 409 * {@link PrimaryKey#sequence} annotation property. 410 * 411 * @return the default configuration for the given sequence name. 412 */ 413 public SequenceConfig getSequenceConfig(String name) { 414 return store.getSequenceConfig(name); 415 } 416 417 /** 418 * Configures a named key sequence using the Berkeley DB engine API. 419 * 420 * <p>To be compatible with the entity model and the Direct Persistence 421 * Layer, the configuration should be retrieved using {@link 422 * #getSequenceConfig getSequenceConfig}, modified, and then passed to this 423 * method. The following configuration properties may not be changed:</p> 424 * <ul> 425 * <li>{@link SequenceConfig#setExclusiveCreate ExclusiveCreate}</li> 426 * </ul> 427 * <p>In addition, {@link SequenceConfig#setAllowCreate AllowCreate} must be 428 * the inverse of {@code ReadOnly}</p> 429 * 430 * <p>If the range is changed to include the value zero, see {@link 431 * PrimaryKey} for restrictions.</p> 432 * 433 * @param name the sequence name, which is normally defined using the 434 * {@link PrimaryKey#sequence} annotation property. 435 * 436 * @param config the configuration to use for the given sequence name. 437 * 438 * @throws IllegalArgumentException if the configuration is incompatible 439 * with the entity model or the Direct Persistence Layer. 440 * 441 * @throws IllegalStateException if the sequence has already been opened. 442 */ 443 public void setSequenceConfig(String name, SequenceConfig config) { 444 store.setSequenceConfig(name, config); 445 } 446 447 /** 448 * Returns the default primary database Berkeley DB engine API 449 * configuration for an entity class. 450 * 451 * </p>The returned configuration is as follows. All other properties have 452 * default values.</p> 453 * <ul> 454 * <li>{@link DatabaseConfig#setTransactional Transactional} is set to 455 * match {@link StoreConfig#setTransactional StoreConfig}.</li> 456 * <li>{@link DatabaseConfig#setAllowCreate AllowCreate} is set to the 457 * inverse of the store {@link StoreConfig#setReadOnly ReadOnly}. 458 * setting.</li> 459 * <li>{@link DatabaseConfig#setReadOnly ReadOnly} is set to match 460 * {@link StoreConfig#setReadOnly StoreConfig}.</li> 461 * <li>{@link DatabaseConfig#setBtreeComparator BtreeComparator} is set to 462 * an internal class if a key comparator is used.</li> 463 * </ul> 464 * 465 * @param entityClass the entity class identifying the primary database. 466 * 467 * @return the default configuration for the given entity class. 468 */ 469 public DatabaseConfig getPrimaryConfig(Class entityClass) { 470 return store.getPrimaryConfig(entityClass); 471 } 472 473 /** 474 * Configures the primary database for an entity class using the Berkeley 475 * DB engine API. 476 * 477 * <p>To be compatible with the entity model and the Direct Persistence 478 * Layer, the configuration should be retrieved using {@link 479 * #getPrimaryConfig getPrimaryConfig}, modified, and then passed to this 480 * method. The following configuration properties may not be changed:</p> 481 * <ul> 482 * <li>{@link DatabaseConfig#setExclusiveCreate ExclusiveCreate}</li> 483 * <li>{@link DatabaseConfig#setSortedDuplicates SortedDuplicates}</li> 484 * <li>{@link DatabaseConfig#setBtreeComparator BtreeComparator}</li> 485 * </ul> 486 * <p>In addition, {@link DatabaseConfig#setAllowCreate AllowCreate} must be 487 * the inverse of {@code ReadOnly}</p> 488 * 489 * @param entityClass the entity class identifying the primary database. 490 * 491 * @param config the configuration to use for the given entity class. 492 * 493 * @throws IllegalArgumentException if the configuration is incompatible 494 * with the entity model or the Direct Persistence Layer. 495 * 496 * @throws IllegalStateException if the database has already been opened. 497 */ 498 public void setPrimaryConfig(Class entityClass, DatabaseConfig config) { 499 store.setPrimaryConfig(entityClass, config); 500 } 501 502 /** 503 * Returns the default secondary database Berkeley DB engine API 504 * configuration for an entity class and key name. 505 * 506 * </p>The returned configuration is as follows. All other properties have 507 * default values.</p> 508 * <ul> 509 * <li>{@link DatabaseConfig#setTransactional Transactional} is set to 510 * match the primary database.</li> 511 * <li>{@link DatabaseConfig#setAllowCreate AllowCreate} is set to the 512 * inverse of the primary database {@link DatabaseConfig#setReadOnly 513 * ReadOnly} setting.</li> 514 * <li>{@link DatabaseConfig#setReadOnly ReadOnly} is set to match 515 * the primary database.</li> 516 * <li>{@link DatabaseConfig#setBtreeComparator BtreeComparator} is set to 517 * an internal class if a key comparator is used.</li> 518 * <li>{@link DatabaseConfig#setSortedDuplicates SortedDuplicates} is set 519 * according to {@link SecondaryKey#relate}.</p> 520 * <li>{@link SecondaryConfig#setAllowPopulate AllowPopulate} is set to 521 * true when a secondary key is added to an existing primary index.</li> 522 * <li>{@link SecondaryConfig#setKeyCreator KeyCreator} or {@link 523 * SecondaryConfig#setMultiKeyCreator MultiKeyCreator} is set to an 524 * internal instance.</p> 525 * <li>{@link SecondaryConfig#setForeignMultiKeyNullifier 526 * ForeignMultiKeyNullifier} is set to an internal instance if {@link 527 * SecondaryKey#onRelatedEntityDelete} is {@link DeleteAction#NULLIFY}.</li> 528 * </ul> 529 * 530 * @param entityClass the entity class containing the given secondary key 531 * name. 532 * 533 * @param keyName the name of the secondary key field, or the {@link 534 * SecondaryKey#name} if this name annotation property was specified. 535 * 536 * @return the default configuration for the given secondary key. 537 */ 538 public SecondaryConfig getSecondaryConfig(Class entityClass, 539 String keyName) { 540 return store.getSecondaryConfig(entityClass, keyName); 541 } 542 543 /** 544 * Configures a secondary database for an entity class and key name using 545 * the Berkeley DB engine API. 546 * 547 * <p>To be compatible with the entity model and the Direct Persistence 548 * Layer, the configuration should be retrieved using {@link 549 * #getSecondaryConfig getSecondaryConfig}, modified, and then passed to 550 * this method. The following configuration properties may not be 551 * changed:</p> 552 * <ul> 553 * <li>{@link DatabaseConfig#setExclusiveCreate ExclusiveCreate}</li> 554 * <li>{@link DatabaseConfig#setSortedDuplicates SortedDuplicates}</li> 555 * <li>{@link DatabaseConfig#setBtreeComparator BtreeComparator}</li> 556 * <li>{@link DatabaseConfig#setDuplicateComparator 557 * DuplicateComparator}</li> 558 * <li>{@link SecondaryConfig#setAllowPopulate AllowPopulate}</li> 559 * <li>{@link SecondaryConfig#setKeyCreator KeyCreator}</li> 560 * <li>{@link SecondaryConfig#setMultiKeyCreator MultiKeyCreator}</li> 561 * <li>{@link SecondaryConfig#setForeignKeyNullifier 562 * ForeignKeyNullifier}</li> 563 * <li>{@link SecondaryConfig#setForeignMultiKeyNullifier 564 * ForeignMultiKeyNullifier}</li> 565 * <li>{@link SecondaryConfig#setForeignKeyDeleteAction 566 * ForeignKeyDeleteAction}</li> 567 * <li>{@link SecondaryConfig#setForeignKeyDatabase 568 * ForeignKeyDatabase}</li> 569 * </ul> 570 * <p>In addition, {@link DatabaseConfig#setAllowCreate AllowCreate} must be 571 * the inverse of {@code ReadOnly}</p> 572 * 573 * @param entityClass the entity class containing the given secondary key 574 * name. 575 * 576 * @param keyName the name of the secondary key field, or the {@link 577 * SecondaryKey#name} if this name annotation property was specified. 578 * 579 * @param config the configuration to use for the given secondary key. 580 * 581 * @throws IllegalArgumentException if the configuration is incompatible 582 * with the entity model or the Direct Persistence Layer. 583 * 584 * @throws IllegalStateException if the database has already been opened. 585 */ 586 public void setSecondaryConfig(Class entityClass, 587 String keyName, 588 SecondaryConfig config) { 589 store.setSecondaryConfig(entityClass, keyName, config); 590 } 591} 592