1/*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 2002,2008 Oracle. All rights reserved. 5 * 6 * $Id: EntityStore.java,v 1.1 2008/02/07 17:12:26 mark Exp $ 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 DatabaseException, IncompatibleClassException { 105 106 store = new Store(env, storeName, config, false /*rawAccess*/); 107 } 108 109 /** 110 * Only public for debugging. 111 */ 112 void dumpCatalog() { 113 store.dumpCatalog(); 114 } 115 116 /** 117 * Returns the environment associated with this store. 118 * 119 * @return the environment. 120 */ 121 public Environment getEnvironment() { 122 return store.getEnvironment(); 123 } 124 125 /** 126 * Returns a copy of the entity store configuration. 127 * 128 * @return the config. 129 */ 130 public StoreConfig getConfig() { 131 return store.getConfig(); 132 } 133 134 /** 135 * Returns the name of this store. 136 * 137 * @return the name. 138 */ 139 public String getStoreName() { 140 return store.getStoreName(); 141 } 142 143 144 /** 145 * Returns the current entity model for this store. The current model is 146 * derived from the configured entity model and the live entity class 147 * definitions. 148 * 149 * @return the model. 150 */ 151 public EntityModel getModel() { 152 return store.getModel(); 153 } 154 155 /** 156 * Returns the set of mutations that were configured when the store was 157 * opened, or if none were configured, the set of mutations that were 158 * configured and stored previously. 159 * 160 * @return the mutations. 161 */ 162 public Mutations getMutations() { 163 return store.getMutations(); 164 } 165 166 /** 167 * Returns the primary index for a given entity class, opening it if 168 * necessary. 169 * 170 * <p>If they are not already open, the primary and secondary databases for 171 * the entity class are created/opened together in a single internal 172 * transaction. When the secondary indices are opened, that can cascade to 173 * open other related primary indices.</p> 174 * 175 * @param primaryKeyClass the class of the entity's primary key field, or 176 * the corresponding primitive wrapper class if the primary key field type 177 * is a primitive. 178 * 179 * @param entityClass the entity class for which to open the primary index. 180 * 181 * @return the primary index. 182 * 183 * @throws IllegalArgumentException if the entity class or classes 184 * referenced by it are not persistent, or the primary key class does not 185 * match the entity's primary key field, or if metadata for the entity or 186 * primary key is invalid. 187 */ 188 public <PK,E> PrimaryIndex<PK,E> getPrimaryIndex(Class<PK> primaryKeyClass, 189 Class<E> entityClass) 190 throws DatabaseException { 191 192 return store.getPrimaryIndex 193 (primaryKeyClass, primaryKeyClass.getName(), 194 entityClass, entityClass.getName()); 195 } 196 197 /** 198 * Returns a secondary index for a given primary index and secondary key, 199 * opening it if necessary. 200 * 201 * <p><em>NOTE:</em> If the secondary key field is declared in a subclass 202 * of the entity class, use {@link #getSubclassIndex} instead.</p> 203 * 204 * <p>If a {@link SecondaryKey#relatedEntity} is used and the primary index 205 * for the related entity is not already open, it will be opened by this 206 * method. That will, in turn, open its secondary indices, which can 207 * cascade to open other primary indices.</p> 208 * 209 * @param primaryIndex the primary index associated with the returned 210 * secondary index. The entity class of the primary index, or one of its 211 * superclasses, must contain a secondary key with the given secondary key 212 * class and key name. 213 * 214 * @param keyClass the class of the secondary key field, or the 215 * corresponding primitive wrapper class if the secondary key field type is 216 * a primitive. 217 * 218 * @param keyName the name of the secondary key field, or the {@link 219 * SecondaryKey#name} if this name annotation property was specified. 220 * 221 * @return the secondary index. 222 * 223 * @throws IllegalArgumentException if the entity class or one of its 224 * superclasses does not contain a key field of the given key class and key 225 * name, or if the metadata for the secondary key is invalid. 226 */ 227 public <SK,PK,E> SecondaryIndex<SK,PK,E> 228 getSecondaryIndex(PrimaryIndex<PK,E> primaryIndex, 229 Class<SK> keyClass, 230 String keyName) 231 throws DatabaseException { 232 233 return store.getSecondaryIndex 234 (primaryIndex, primaryIndex.getEntityClass(), 235 primaryIndex.getEntityClass().getName(), 236 keyClass, keyClass.getName(), keyName); 237 } 238 239 /** 240 * Returns a secondary index for a secondary key in an entity subclass, 241 * opening it if necessary. 242 * 243 * <p>If a {@link SecondaryKey#relatedEntity} is used and the primary index 244 * for the related entity is not already open, it will be opened by this 245 * method. That will, in turn, open its secondary indices, which can 246 * cascade to open other primary indices.</p> 247 * 248 * @param primaryIndex the primary index associated with the returned 249 * secondary index. The entity class of the primary index, or one of its 250 * superclasses, must contain a secondary key with the given secondary key 251 * class and key name. 252 * 253 * @param entitySubclass a subclass of the entity class for the primary 254 * index. The entity subclass must contain a secondary key with the given 255 * secondary key class and key name. 256 * 257 * @param keyClass the class of the secondary key field, or the 258 * corresponding primitive wrapper class if the secondary key field type is 259 * a primitive. 260 * 261 * @param keyName the name of the secondary key field, or the {@link 262 * SecondaryKey#name} if this name annotation property was specified. 263 * 264 * @return the secondary index. 265 * 266 * @throws IllegalArgumentException if the given entity subclass does not 267 * contain a key field of the given key class and key name, or if the 268 * metadata for the secondary key is invalid. 269 */ 270 public <SK,PK,E1,E2 extends E1> SecondaryIndex<SK,PK,E2> 271 getSubclassIndex(PrimaryIndex<PK,E1> primaryIndex, 272 Class<E2> entitySubclass, 273 Class<SK> keyClass, 274 String keyName) 275 throws DatabaseException { 276 277 /* Make subclass metadata available before getting the index. */ 278 getModel().getClassMetadata(entitySubclass.getName()); 279 280 return store.getSecondaryIndex 281 (primaryIndex, entitySubclass, 282 primaryIndex.getEntityClass().getName(), 283 keyClass, keyClass.getName(), keyName); 284 } 285 286 /** 287 * Performs conversion of unevolved objects in order to reduce lazy 288 * conversion overhead. Evolution may be performed concurrently with 289 * normal access to the store. 290 * 291 * <p>Conversion is performed one entity class at a time. An entity class 292 * is converted only if it has {@link Mutations} associated with it via 293 * {@link StoreConfig#setMutations StoreConfig.setMutations}.</p> 294 * 295 * <p>Conversion of an entity class is performed by reading each entity, 296 * converting it if necessary, and updating it if conversion was performed. 297 * When all instances of an entity class are converted, references to the 298 * appropriate {@link Mutations} are deleted. Therefore, if this method is 299 * called twice successfully without changing class definitions, the second 300 * call will do nothing.</p> 301 * 302 * @see com.sleepycat.persist.evolve Class Evolution 303 */ 304 public EvolveStats evolve(EvolveConfig config) 305 throws DatabaseException { 306 307 return store.evolve(config); 308 } 309 310 /** 311 * Deletes all instances of this entity class and its (non-entity) 312 * subclasses. 313 * 314 * <p>The primary database and all secondary databases for the given entity 315 * class will be truncated. The primary and secondary databases associated 316 * with the entity class must not be open except by this store, since 317 * database truncation is only possible when the database is not open. The 318 * databases to be truncated will be closed before performing this 319 * operation, if they were previously opened by this store.</p> 320 * 321 * <p>Auto-commit is used implicitly if the store is transactional.</p> 322 * 323 * @param entityClass the entity class whose instances are to be deleted. 324 */ 325 public void truncateClass(Class entityClass) 326 throws DatabaseException { 327 328 store.truncateClass(null, entityClass); 329 } 330 331 /** 332 * Deletes all instances of this entity class and its (non-entity) 333 * subclasses. 334 * 335 * <p>The primary database and all secondary databases for the given entity 336 * class will be truncated. The primary and secondary databases associated 337 * with the entity class must not be open except by this store, since 338 * database truncation is only possible when the database is not open. The 339 * databases to be truncated will be closed before performing this 340 * operation, if they were previously opened by this store.</p> 341 * 342 * @param txn the transaction used to protect this operation, null to use 343 * auto-commit, or null if the store is non-transactional. 344 * 345 * @param entityClass the entity class whose instances are to be deleted. 346 */ 347 public void truncateClass(Transaction txn, Class entityClass) 348 throws DatabaseException { 349 350 store.truncateClass(txn, entityClass); 351 } 352 353 354 /** 355 * Closes the primary and secondary databases for the given entity class 356 * that were opened via this store. The caller must ensure that the 357 * primary and secondary indices obtained from this store are no longer in 358 * use. 359 * 360 * @param entityClass the entity class whose databases are to be closed. 361 */ 362 public void closeClass(Class entityClass) 363 throws DatabaseException { 364 365 store.closeClass(entityClass); 366 } 367 368 /** 369 * Closes all databases and sequences that were opened via this store. The 370 * caller must ensure that no databases opened via this store are in use. 371 */ 372 public void close() 373 throws DatabaseException { 374 375 store.close(); 376 } 377 378 /** 379 * Returns a named sequence for using Berkeley DB engine API directly, 380 * opening it if necessary. 381 * 382 * @param name the sequence name, which is normally defined using the 383 * {@link PrimaryKey#sequence} annotation property. 384 * 385 * @return the open sequence for the given sequence name. 386 */ 387 public Sequence getSequence(String name) 388 throws DatabaseException { 389 390 return store.getSequence(name); 391 } 392 393 /** 394 * Returns the default Berkeley DB engine API configuration for a named key 395 * sequence. 396 * 397 * </p>The returned configuration is as follows. All other properties have 398 * default values.</p> 399 * <ul> 400 * <li>The {@link SequenceConfig#setInitialValue InitialValue} is one.</li> 401 * <li>The {@link SequenceConfig#setRange Range} minimum is one.</li> 402 * <li>The {@link SequenceConfig#setCacheSize CacheSize} is 100.</li> 403 * <li>{@link SequenceConfig#setAutoCommitNoSync AutoCommitNoSync} is 404 * true.</li> 405 * <li>{@link SequenceConfig#setAllowCreate AllowCreate} is set to true 406 * if the store is not {@link StoreConfig#setReadOnly ReadOnly}.</li> 407 * </ul> 408 * 409 * @param name the sequence name, which is normally defined using the 410 * {@link PrimaryKey#sequence} annotation property. 411 * 412 * @return the default configuration for the given sequence name. 413 */ 414 public SequenceConfig getSequenceConfig(String name) { 415 return store.getSequenceConfig(name); 416 } 417 418 /** 419 * Configures a named key sequence using the Berkeley DB engine API. 420 * 421 * <p>To be compatible with the entity model and the Direct Persistence 422 * Layer, the configuration should be retrieved using {@link 423 * #getSequenceConfig getSequenceConfig}, modified, and then passed to this 424 * method.</p> 425 * 426 * <p>If the range is changed to include the value zero, see {@link 427 * PrimaryKey} for restrictions.</p> 428 * 429 * @param name the sequence name, which is normally defined using the 430 * {@link PrimaryKey#sequence} annotation property. 431 * 432 * @param config the configuration to use for the given sequence name. 433 * 434 * @throws IllegalArgumentException if the configuration is incompatible 435 * with the entity model or the Direct Persistence Layer. 436 * 437 * @throws IllegalStateException if the sequence has already been opened. 438 */ 439 public void setSequenceConfig(String name, SequenceConfig config) { 440 store.setSequenceConfig(name, config); 441 } 442 443 /** 444 * Returns the default primary database Berkeley DB engine API 445 * configuration for an entity class. 446 * 447 * </p>The returned configuration is as follows. All other properties have 448 * default values.</p> 449 * <ul> 450 * <li>{@link DatabaseConfig#setTransactional Transactional} is set to 451 * match {@link StoreConfig#setTransactional StoreConfig}.</li> 452 * <li>{@link DatabaseConfig#setAllowCreate AllowCreate} is set to true 453 * if the store is not {@link StoreConfig#setReadOnly ReadOnly}.</li> 454 * <li>{@link DatabaseConfig#setReadOnly ReadOnly} is set to match 455 * {@link StoreConfig#setReadOnly StoreConfig}.</li> 456 * <li>{@link DatabaseConfig#setBtreeComparator BtreeComparator} is set to 457 * an internal class if a key comparator is used.</li> 458 * </ul> 459 * 460 * @param entityClass the entity class identifying the primary database. 461 * 462 * @return the default configuration for the given entity class. 463 */ 464 public DatabaseConfig getPrimaryConfig(Class entityClass) { 465 return store.getPrimaryConfig(entityClass); 466 } 467 468 /** 469 * Configures the primary database for an entity class using the Berkeley 470 * DB engine API. 471 * 472 * <p>To be compatible with the entity model and the Direct Persistence 473 * Layer, the configuration should be retrieved using {@link 474 * #getPrimaryConfig getPrimaryConfig}, modified, and then passed to this 475 * method. The following configuration properties may not be changed:</p> 476 * <ul> 477 * <li>{@link DatabaseConfig#setSortedDuplicates SortedDuplicates}</li> 478 * <li>{@link DatabaseConfig#setBtreeComparator BtreeComparator}</li> 479 * </ul> 480 * 481 * @param entityClass the entity class identifying the primary database. 482 * 483 * @param config the configuration to use for the given entity class. 484 * 485 * @throws IllegalArgumentException if the configuration is incompatible 486 * with the entity model or the Direct Persistence Layer. 487 * 488 * @throws IllegalStateException if the database has already been opened. 489 */ 490 public void setPrimaryConfig(Class entityClass, DatabaseConfig config) { 491 store.setPrimaryConfig(entityClass, config); 492 } 493 494 /** 495 * Returns the default secondary database Berkeley DB engine API 496 * configuration for an entity class and key name. 497 * 498 * </p>The returned configuration is as follows. All other properties have 499 * default values.</p> 500 * <ul> 501 * <li>{@link DatabaseConfig#setTransactional Transactional} is set to 502 * match the primary database.</li> 503 * <li>{@link DatabaseConfig#setAllowCreate AllowCreate} is set to true 504 * if the primary database is not {@link StoreConfig#setReadOnly 505 * ReadOnly}.</li> 506 * <li>{@link DatabaseConfig#setReadOnly ReadOnly} is set to match 507 * the primary database.</li> 508 * <li>{@link DatabaseConfig#setBtreeComparator BtreeComparator} is set to 509 * an internal class if a key comparator is used.</li> 510 * <li>{@link DatabaseConfig#setSortedDuplicates SortedDuplicates} is set 511 * according to {@link SecondaryKey#relate}.</p> 512 * <li>{@link SecondaryConfig#setAllowPopulate AllowPopulate} is set to 513 * true when a secondary key is added to an existing primary index.</li> 514 * <li>{@link SecondaryConfig#setKeyCreator KeyCreator} or {@link 515 * SecondaryConfig#setMultiKeyCreator MultiKeyCreator} is set to an 516 * internal instance.</p> 517 * <li>{@link SecondaryConfig#setForeignMultiKeyNullifier 518 * ForeignMultiKeyNullifier} is set to an internal instance if {@link 519 * SecondaryKey#onRelatedEntityDelete} is {@link DeleteAction#NULLIFY}.</li> 520 * </ul> 521 * 522 * @param entityClass the entity class containing the given secondary key 523 * name. 524 * 525 * @param keyName the name of the secondary key field, or the {@link 526 * SecondaryKey#name} if this name annotation property was specified. 527 * 528 * @return the default configuration for the given secondary key. 529 */ 530 public SecondaryConfig getSecondaryConfig(Class entityClass, 531 String keyName) { 532 return store.getSecondaryConfig(entityClass, keyName); 533 } 534 535 /** 536 * Configures a secondary database for an entity class and key name using 537 * the Berkeley DB engine API. 538 * 539 * <p>To be compatible with the entity model and the Direct Persistence 540 * Layer, the configuration should be retrieved using {@link 541 * #getSecondaryConfig getSecondaryConfig}, modified, and then passed to 542 * this method. The following configuration properties may not be 543 * changed:</p> 544 * <ul> 545 * <li>{@link DatabaseConfig#setSortedDuplicates SortedDuplicates}</li> 546 * <li>{@link DatabaseConfig#setBtreeComparator BtreeComparator}</li> 547 * <li>{@link DatabaseConfig#setDuplicateComparator 548 * DuplicateComparator}</li> 549 * <li>{@link SecondaryConfig#setAllowPopulate AllowPopulate}</li> 550 * <li>{@link SecondaryConfig#setKeyCreator KeyCreator}</li> 551 * <li>{@link SecondaryConfig#setMultiKeyCreator MultiKeyCreator}</li> 552 * <li>{@link SecondaryConfig#setForeignKeyNullifier 553 * ForeignKeyNullifier}</li> 554 * <li>{@link SecondaryConfig#setForeignMultiKeyNullifier 555 * ForeignMultiKeyNullifier}</li> 556 * <li>{@link SecondaryConfig#setForeignKeyDeleteAction 557 * ForeignKeyDeleteAction}</li> 558 * <li>{@link SecondaryConfig#setForeignKeyDatabase 559 * ForeignKeyDatabase}</li> 560 * </ul> 561 * 562 * @param entityClass the entity class containing the given secondary key 563 * name. 564 * 565 * @param keyName the name of the secondary key field, or the {@link 566 * SecondaryKey#name} if this name annotation property was specified. 567 * 568 * @param config the configuration to use for the given secondary key. 569 * 570 * @throws IllegalArgumentException if the configuration is incompatible 571 * with the entity model or the Direct Persistence Layer. 572 * 573 * @throws IllegalStateException if the database has already been opened. 574 */ 575 public void setSecondaryConfig(Class entityClass, 576 String keyName, 577 SecondaryConfig config) { 578 store.setSecondaryConfig(entityClass, keyName, config); 579 } 580} 581