public synchronized void closeClass(Class entityClass) throws DatabaseException { checkOpen(); String clsName = entityClass.getName(); EntityMetadata entityMeta = checkEntityClass(clsName); PrimaryIndex priIndex = priIndexMap.get(clsName); if (priIndex != null) { /* Close the secondaries first. */ DatabaseException firstException = null; for (SecondaryKeyMetadata keyMeta : entityMeta.getSecondaryKeys().values()) { String secName = makeSecName(clsName, keyMeta.getKeyName()); SecondaryIndex secIndex = secIndexMap.get(secName); if (secIndex != null) { Database db = secIndex.getDatabase(); firstException = closeDb(db, firstException); firstException = closeDb(secIndex.getKeysDatabase(), firstException); secIndexMap.remove(secName); deferredWriteDatabases.remove(db); } } /* Close the primary last. */ Database db = priIndex.getDatabase(); firstException = closeDb(db, firstException); priIndexMap.remove(clsName); deferredWriteDatabases.remove(db); /* Throw the first exception encountered. */ if (firstException != null) { throw firstException; } } }
/** * Opens any secondary indexes defined in the given entity metadata that are not already open. * This method is called when a new entity subclass is encountered when an instance of that class * is stored, and the EntityStore.getSubclassIndex has not been previously called for that class. * [#15247] */ synchronized void openSecondaryIndexes( Transaction txn, EntityMetadata entityMeta, PrimaryOpenState priOpenState) throws DatabaseException { String entityClassName = entityMeta.getClassName(); PrimaryIndex<Object, Object> priIndex = priIndexMap.get(entityClassName); assert priIndex != null; Class<Object> entityClass = priIndex.getEntityClass(); for (SecondaryKeyMetadata secKeyMeta : entityMeta.getSecondaryKeys().values()) { String keyName = secKeyMeta.getKeyName(); String secName = makeSecName(entityClassName, keyName); SecondaryIndex<Object, Object, Object> secIndex = secIndexMap.get(secName); if (secIndex == null) { String keyClassName = getSecKeyClass(secKeyMeta); /* RawMode: should not require class. */ Class keyClass = SimpleCatalog.keyClassForName(keyClassName); openSecondaryIndex( txn, priIndex, entityClass, entityMeta, keyClass, keyClassName, secKeyMeta, makeSecName(entityClassName, secKeyMeta.getKeyName()), storeConfig.getSecondaryBulkLoad() /*doNotCreate*/, priOpenState); } } }
private String getSecKeyClass(SecondaryKeyMetadata secKeyMeta) { String clsName = secKeyMeta.getElementClassName(); if (clsName == null) { clsName = secKeyMeta.getClassName(); } return SimpleCatalog.keyClassName(clsName); }
private SecondaryConfig getSecondaryConfig( String secName, EntityMetadata entityMeta, String keyClassName, SecondaryKeyMetadata secKeyMeta) { SecondaryConfig config = secConfigMap.get(secName); if (config == null) { /* Set common properties to match the primary DB. */ DatabaseConfig priConfig = getPrimaryConfig(entityMeta); config = new SecondaryConfig(); config.setTransactional(priConfig.getTransactional()); config.setAllowCreate(!priConfig.getReadOnly()); config.setReadOnly(priConfig.getReadOnly()); DbCompat.setDeferredWrite(config, DbCompat.getDeferredWrite(priConfig)); /* Set secondary properties based on metadata. */ config.setAllowPopulate(true); Relationship rel = secKeyMeta.getRelationship(); config.setSortedDuplicates( rel == Relationship.MANY_TO_ONE || rel == Relationship.MANY_TO_MANY); setBtreeComparator(config, secKeyMeta.getClassName()); PersistKeyCreator keyCreator = new PersistKeyCreator(catalog, entityMeta, keyClassName, secKeyMeta); if (rel == Relationship.ONE_TO_MANY || rel == Relationship.MANY_TO_MANY) { config.setMultiKeyCreator(keyCreator); } else { config.setKeyCreator(keyCreator); } DeleteAction deleteAction = secKeyMeta.getDeleteAction(); if (deleteAction != null) { ForeignKeyDeleteAction baseDeleteAction; switch (deleteAction) { case ABORT: baseDeleteAction = ForeignKeyDeleteAction.ABORT; break; case CASCADE: baseDeleteAction = ForeignKeyDeleteAction.CASCADE; break; case NULLIFY: baseDeleteAction = ForeignKeyDeleteAction.NULLIFY; break; default: throw new IllegalStateException(deleteAction.toString()); } config.setForeignKeyDeleteAction(baseDeleteAction); if (deleteAction == DeleteAction.NULLIFY) { config.setForeignMultiKeyNullifier(keyCreator); } } secConfigMap.put(secName, config); } return config; }
public synchronized void truncateClass(Transaction txn, Class entityClass) throws DatabaseException { checkOpen(); /* Close primary and secondary databases. */ closeClass(entityClass); String clsName = entityClass.getName(); EntityMetadata entityMeta = checkEntityClass(clsName); /* * Truncate the primary first and let any exceptions propogate * upwards. Then truncate each secondary, only throwing the first * exception. */ String dbName = storePrefix + clsName; boolean primaryExists = true; try { env.truncateDatabase(txn, dbName, false); } catch (DatabaseNotFoundException ignored) { primaryExists = false; } if (primaryExists) { DatabaseException firstException = null; for (SecondaryKeyMetadata keyMeta : entityMeta.getSecondaryKeys().values()) { try { env.truncateDatabase( txn, storePrefix + makeSecName(clsName, keyMeta.getKeyName()), false); } catch (DatabaseNotFoundException ignored) { /* Ignore secondaries that do not exist. */ } catch (DatabaseException e) { if (firstException == null) { firstException = e; } } } if (firstException != null) { throw firstException; } } }
public Store(Environment env, String storeName, StoreConfig config, boolean rawAccess) throws DatabaseException { this.env = env; this.storeName = storeName; this.rawAccess = rawAccess; if (env == null || storeName == null) { throw new NullPointerException("env and storeName parameters must not be null"); } if (config != null) { model = config.getModel(); mutations = config.getMutations(); } if (config == null) { storeConfig = StoreConfig.DEFAULT; } else { storeConfig = config.cloneConfig(); } storePrefix = NAME_PREFIX + storeName + NAME_SEPARATOR; priIndexMap = new HashMap<String, PrimaryIndex>(); secIndexMap = new HashMap<String, SecondaryIndex>(); priConfigMap = new HashMap<String, DatabaseConfig>(); secConfigMap = new HashMap<String, SecondaryConfig>(); keyBindingMap = new HashMap<String, PersistKeyBinding>(); sequenceMap = new HashMap<String, Sequence>(); sequenceConfigMap = new HashMap<String, SequenceConfig>(); deferredWriteDatabases = new IdentityHashMap<Database, Object>(); if (rawAccess) { /* Open a read-only catalog that uses the stored model. */ if (model != null) { throw new IllegalArgumentException("A model may not be specified when opening a RawStore"); } DatabaseConfig dbConfig = new DatabaseConfig(); dbConfig.setReadOnly(true); dbConfig.setTransactional(storeConfig.getTransactional()); catalog = new PersistCatalog( null, env, storePrefix, storePrefix + CATALOG_DB, dbConfig, model, mutations, rawAccess, this); } else { /* Open the shared catalog that uses the current model. */ synchronized (catalogPool) { Map<String, PersistCatalog> catalogMap = catalogPool.get(env); if (catalogMap == null) { catalogMap = new HashMap<String, PersistCatalog>(); catalogPool.put(env, catalogMap); } catalog = catalogMap.get(storeName); if (catalog != null) { catalog.openExisting(); } else { Transaction txn = null; if (storeConfig.getTransactional() && env.getThreadTransaction() == null) { txn = env.beginTransaction(null, null); } boolean success = false; try { DatabaseConfig dbConfig = new DatabaseConfig(); dbConfig.setAllowCreate(storeConfig.getAllowCreate()); dbConfig.setReadOnly(storeConfig.getReadOnly()); dbConfig.setTransactional(storeConfig.getTransactional()); catalog = new PersistCatalog( txn, env, storePrefix, storePrefix + CATALOG_DB, dbConfig, model, mutations, rawAccess, this); catalogMap.put(storeName, catalog); success = true; } finally { if (txn != null) { if (success) { txn.commit(); } else { txn.abort(); } } } } } } /* Get the merged mutations from the catalog. */ mutations = catalog.getMutations(); /* * If there is no model parameter, use the default or stored model * obtained from the catalog. */ model = catalog.getResolvedModel(); /* * Give the model a reference to the catalog to fully initialize the * model. Only then may we initialize the Converter mutations, which * themselves may call model methods and expect the model to be fully * initialized. */ ModelInternal.setCatalog(model, catalog); for (Converter converter : mutations.getConverters()) { converter.getConversion().initialize(model); } /* * For each existing entity with a relatedEntity reference, create an * inverse map (back pointer) from the class named in the relatedEntity * to the class containing the secondary key. This is used to open the * class containing the secondary key whenever we open the * relatedEntity class, to configure foreign key constraints. Note that * we do not need to update this map as new primary indexes are * created, because opening the new index will setup the foreign key * constraints. [#15358] */ inverseRelatedEntityMap = new HashMap<String, Set<String>>(); List<Format> entityFormats = new ArrayList<Format>(); catalog.getEntityFormats(entityFormats); for (Format entityFormat : entityFormats) { EntityMetadata entityMeta = entityFormat.getEntityMetadata(); for (SecondaryKeyMetadata secKeyMeta : entityMeta.getSecondaryKeys().values()) { String relatedClsName = secKeyMeta.getRelatedEntity(); if (relatedClsName != null) { Set<String> inverseClassNames = inverseRelatedEntityMap.get(relatedClsName); if (inverseClassNames == null) { inverseClassNames = new HashSet<String>(); inverseRelatedEntityMap.put(relatedClsName, inverseClassNames); } inverseClassNames.add(entityMeta.getClassName()); } } } }
/** * Opens a secondary index with a given transaction and adds it to the secIndexMap. We assume that * the index is not already open. */ private <SK, PK, E1, E2 extends E1> SecondaryIndex<SK, PK, E2> openSecondaryIndex( Transaction txn, PrimaryIndex<PK, E1> primaryIndex, Class<E2> entityClass, EntityMetadata entityMeta, Class<SK> keyClass, String keyClassName, SecondaryKeyMetadata secKeyMeta, String secName, boolean doNotCreate, PrimaryOpenState priOpenState) throws DatabaseException { assert !secIndexMap.containsKey(secName); String dbName = storePrefix + secName; SecondaryConfig config = getSecondaryConfig(secName, entityMeta, keyClassName, secKeyMeta); Database priDb = primaryIndex.getDatabase(); DatabaseConfig priConfig = priDb.getConfig(); String relatedClsName = secKeyMeta.getRelatedEntity(); if (relatedClsName != null) { PrimaryIndex relatedIndex = getRelatedIndex(relatedClsName); config.setForeignKeyDatabase(relatedIndex.getDatabase()); } if (config.getTransactional() != priConfig.getTransactional() || DbCompat.getDeferredWrite(config) != DbCompat.getDeferredWrite(priConfig) || config.getReadOnly() != priConfig.getReadOnly()) { throw new IllegalArgumentException( "One of these properties was changed to be inconsistent" + " with the associated primary database: " + " Transactional, DeferredWrite, ReadOnly"); } PersistKeyBinding keyBinding = getKeyBinding(keyClassName); /* * doNotCreate is true when StoreConfig.getSecondaryBulkLoad is true * and we are opening a secondary as a side effect of opening a * primary, i.e., getSecondaryIndex is not being called. If * doNotCreate is true and the database does not exist, we silently * ignore the DatabaseNotFoundException and return null. When * getSecondaryIndex is subsequently called, the secondary database * will be created and populated from the primary -- a bulk load. */ SecondaryDatabase db; boolean saveAllowCreate = config.getAllowCreate(); try { if (doNotCreate) { config.setAllowCreate(false); } db = env.openSecondaryDatabase(txn, dbName, priDb, config); } catch (DatabaseNotFoundException e) { if (doNotCreate) { return null; } else { throw e; } } finally { if (doNotCreate) { config.setAllowCreate(saveAllowCreate); } } SecondaryIndex<SK, PK, E2> secIndex = new SecondaryIndex(db, null, primaryIndex, keyClass, keyBinding); /* Update index and database maps. */ secIndexMap.put(secName, secIndex); if (DbCompat.getDeferredWrite(config)) { deferredWriteDatabases.put(db, null); } if (priOpenState != null) { priOpenState.addDatabase(db); priOpenState.addSecondaryName(secName); } return secIndex; }
/** * A getSecondaryIndex with extra parameters for opening a raw store. keyClassName is used for * consistency checking and should be null for a raw store only. */ public synchronized <SK, PK, E1, E2 extends E1> SecondaryIndex<SK, PK, E2> getSecondaryIndex( PrimaryIndex<PK, E1> primaryIndex, Class<E2> entityClass, String entityClassName, Class<SK> keyClass, String keyClassName, String keyName) throws DatabaseException { assert (rawAccess && keyClassName == null) || (!rawAccess && keyClassName != null); checkOpen(); EntityMetadata entityMeta = null; SecondaryKeyMetadata secKeyMeta = null; /* Validate the subclass for a subclass index. */ if (entityClass != primaryIndex.getEntityClass()) { entityMeta = model.getEntityMetadata(entityClassName); assert entityMeta != null; secKeyMeta = checkSecKey(entityMeta, keyName); String subclassName = entityClass.getName(); String declaringClassName = secKeyMeta.getDeclaringClassName(); if (!subclassName.equals(declaringClassName)) { throw new IllegalArgumentException( "Key for subclass " + subclassName + " is declared in a different class: " + makeSecName(declaringClassName, keyName)); } } /* * Even though the primary is already open, we can't assume the * secondary is open because we don't automatically open all * secondaries when the primary is read-only. Use auto-commit (a null * transaction) since we're opening only one database. */ String secName = makeSecName(entityClassName, keyName); SecondaryIndex<SK, PK, E2> secIndex = secIndexMap.get(secName); if (secIndex == null) { if (entityMeta == null) { entityMeta = model.getEntityMetadata(entityClassName); assert entityMeta != null; } if (secKeyMeta == null) { secKeyMeta = checkSecKey(entityMeta, keyName); } /* Check metadata. */ if (keyClassName == null) { keyClassName = getSecKeyClass(secKeyMeta); } else { String expectClsName = getSecKeyClass(secKeyMeta); if (!keyClassName.equals(expectClsName)) { throw new IllegalArgumentException( "Wrong secondary key class: " + keyClassName + " Correct class is: " + expectClsName); } } secIndex = openSecondaryIndex( null, primaryIndex, entityClass, entityMeta, keyClass, keyClassName, secKeyMeta, secName, false /*doNotCreate*/, null /*priOpenState*/); } return secIndex; }