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