/** * Opens a primary index related via a foreign key (relatedEntity). Related indexes are not opened * in the same transaction used by the caller to open a primary or secondary. It is OK to leave * the related index open when the caller's transaction aborts. It is only important to open a * primary and its secondaries atomically. */ private PrimaryIndex getRelatedIndex(String relatedClsName) throws DatabaseException { PrimaryIndex relatedIndex = priIndexMap.get(relatedClsName); if (relatedIndex == null) { EntityMetadata relatedEntityMeta = checkEntityClass(relatedClsName); Class relatedKeyCls; String relatedKeyClsName; Class relatedCls; if (rawAccess) { relatedCls = RawObject.class; relatedKeyCls = Object.class; relatedKeyClsName = null; } else { try { relatedCls = EntityModel.classForName(relatedClsName); } catch (ClassNotFoundException e) { throw new IllegalArgumentException("Related entity class not found: " + relatedClsName); } relatedKeyClsName = SimpleCatalog.keyClassName(relatedEntityMeta.getPrimaryKey().getClassName()); relatedKeyCls = SimpleCatalog.keyClassForName(relatedKeyClsName); } /* * Cycles are prevented here by adding primary indexes to the * priIndexMap as soon as they are created, before opening related * indexes. */ relatedIndex = getPrimaryIndex( relatedKeyCls, relatedKeyClsName, relatedCls, relatedClsName); } return relatedIndex; }
/** * 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); }
Class getFieldClass() { if (cls == null) { try { cls = SimpleCatalog.classForName(className); } catch (ClassNotFoundException e) { throw new IllegalStateException(e); } } return cls; }
private void setBtreeComparator(DatabaseConfig config, String clsName) { if (!rawAccess) { ClassMetadata meta = model.getClassMetadata(clsName); if (meta != null) { List<FieldMetadata> compositeKeyFields = meta.getCompositeKeyFields(); if (compositeKeyFields != null) { Class keyClass = SimpleCatalog.keyClassForName(clsName); if (Comparable.class.isAssignableFrom(keyClass)) { Comparator<Object> cmp = new PersistComparator(clsName, compositeKeyFields, getKeyBinding(clsName)); config.setBtreeComparator(cmp); } } } } }
/** * Read-only catalog used by a PersistComparator to return simple formats plus reconstituted enum * formats. * * @author Mark Hayes */ class ComparatorCatalog implements Catalog { private final Map<String, Format> formatMap; private final Catalog simpleCatalog = SimpleCatalog.getInstance(); ComparatorCatalog(final Map<String, Format> formatMap) { this.formatMap = formatMap; } public int getInitVersion(final Format format, final boolean forReader) { return simpleCatalog.getInitVersion(format, forReader); } public Format getFormat(final int formatId, final boolean expectStored) { return simpleCatalog.getFormat(formatId, expectStored); } public Format getFormat(final Class cls, final boolean checkEntitySubclassIndexes) { return simpleCatalog.getFormat(cls, checkEntitySubclassIndexes); } public Format getFormat(final String className) { if (formatMap != null) { final Format f = formatMap.get(className); if (f != null) { return f; } } return simpleCatalog.getFormat(className); } public Format createFormat(final String clsName, final Map<String, Format> newFormats) { return simpleCatalog.createFormat(clsName, newFormats); } public Format createFormat(final Class type, final Map<String, Format> newFormats) { return simpleCatalog.createFormat(type, newFormats); } public boolean isRawAccess() { return simpleCatalog.isRawAccess(); } public Object convertRawObject(final RawObject o, final IdentityHashMap converted) { return simpleCatalog.convertRawObject(o, converted); } }
Object checkAndConvert(Object o, Format declaredFormat) { if (o == null) { if (declaredFormat.isPrimitive()) { throw new IllegalArgumentException( "A primitive type may not be null or missing: " + declaredFormat.getClassName()); } } else if (declaredFormat.isSimple()) { if (declaredFormat.isPrimitive()) { if (o.getClass() != declaredFormat.getWrapperFormat().getType()) { throw new IllegalArgumentException( "Raw value class: " + o.getClass().getName() + " must be the wrapper class for a primitive type: " + declaredFormat.getClassName()); } } else { if (o.getClass() != declaredFormat.getType()) { throw new IllegalArgumentException( "Raw value class: " + o.getClass().getName() + " must be the declared class for a simple type: " + declaredFormat.getClassName()); } } } else { if (o instanceof RawObject) { Object o2 = null; if (!rawAccess) { if (converted != null) { o2 = converted.get(o); } else { converted = new IdentityHashMap(); } } if (o2 != null) { o = o2; } else { if (!rawAccess) { o = catalog.convertRawObject((RawObject) o, converted); } } } else { if (!SimpleCatalog.isSimpleType(o.getClass())) { throw new IllegalArgumentException( "Raw value class: " + o.getClass().getName() + " must be RawObject a simple type"); } } if (rawAccess) { checkRawType(catalog, o, declaredFormat); } else { if (!declaredFormat.getType().isAssignableFrom(o.getClass())) { throw new IllegalArgumentException( "Raw value class: " + o.getClass().getName() + " is not assignable to type: " + declaredFormat.getClassName()); } } } return o; }
/** * A getPrimaryIndex with extra parameters for opening a raw store. primaryKeyClass and * entityClass are used for generic typing; for a raw store, these should always be Object.class * and RawObject.class. primaryKeyClassName is used for consistency checking and should be null * for a raw store only. entityClassName is used to identify the store and may not be null. */ public synchronized <PK, E> PrimaryIndex<PK, E> getPrimaryIndex( Class<PK> primaryKeyClass, String primaryKeyClassName, Class<E> entityClass, String entityClassName) throws DatabaseException { assert (rawAccess && entityClass == RawObject.class) || (!rawAccess && entityClass != RawObject.class); assert (rawAccess && primaryKeyClassName == null) || (!rawAccess && primaryKeyClassName != null); checkOpen(); PrimaryIndex<PK, E> priIndex = priIndexMap.get(entityClassName); if (priIndex == null) { /* Check metadata. */ EntityMetadata entityMeta = checkEntityClass(entityClassName); PrimaryKeyMetadata priKeyMeta = entityMeta.getPrimaryKey(); if (primaryKeyClassName == null) { primaryKeyClassName = priKeyMeta.getClassName(); } else { String expectClsName = SimpleCatalog.keyClassName(priKeyMeta.getClassName()); if (!primaryKeyClassName.equals(expectClsName)) { throw new IllegalArgumentException( "Wrong primary key class: " + primaryKeyClassName + " Correct class is: " + expectClsName); } } /* Create bindings. */ PersistEntityBinding entityBinding = new PersistEntityBinding(catalog, entityClassName, rawAccess); PersistKeyBinding keyBinding = getKeyBinding(primaryKeyClassName); /* If not read-only, get the primary key sequence. */ String seqName = priKeyMeta.getSequenceName(); if (!storeConfig.getReadOnly() && seqName != null) { entityBinding.keyAssigner = new PersistKeyAssigner(keyBinding, entityBinding, getSequence(seqName)); } /* * Use a single transaction for opening the primary DB and its * secondaries. If opening any secondary fails, abort the * transaction and undo the changes to the state of the store. * Also support undo if the store is non-transactional. */ Transaction txn = null; DatabaseConfig dbConfig = getPrimaryConfig(entityMeta); if (dbConfig.getTransactional() && env.getThreadTransaction() == null) { txn = env.beginTransaction(null, null); } PrimaryOpenState priOpenState = new PrimaryOpenState(entityClassName); boolean success = false; try { /* Open the primary database. */ String dbName = storePrefix + entityClassName; Database db = env.openDatabase(txn, dbName, dbConfig); priOpenState.addDatabase(db); /* Create index object. */ priIndex = new PrimaryIndex(db, primaryKeyClass, keyBinding, entityClass, entityBinding); /* Update index and database maps. */ priIndexMap.put(entityClassName, priIndex); if (DbCompat.getDeferredWrite(dbConfig)) { deferredWriteDatabases.put(db, null); } /* If not read-only, open all associated secondaries. */ if (!dbConfig.getReadOnly()) { openSecondaryIndexes(txn, entityMeta, priOpenState); /* * To enable foreign key contratints, also open all primary * indexes referring to this class via a relatedEntity * property in another entity. [#15358] */ Set<String> inverseClassNames = inverseRelatedEntityMap.get(entityClassName); if (inverseClassNames != null) { for (String relatedClsName : inverseClassNames) { getRelatedIndex(relatedClsName); } } } success = true; } finally { if (success) { if (txn != null) { txn.commit(); } } else { if (txn != null) { txn.abort(); } else { priOpenState.closeDatabases(); } priOpenState.undoState(); } } } return priIndex; }
private void evolveIndex(Format format, EvolveEvent event, EvolveListener listener) throws DatabaseException { Class entityClass = format.getType(); String entityClassName = format.getClassName(); EntityMetadata meta = model.getEntityMetadata(entityClassName); String keyClassName = meta.getPrimaryKey().getClassName(); keyClassName = SimpleCatalog.keyClassName(keyClassName); DatabaseConfig dbConfig = getPrimaryConfig(meta); PrimaryIndex<Object, Object> index = getPrimaryIndex(Object.class, keyClassName, entityClass, entityClassName); Database db = index.getDatabase(); EntityBinding binding = index.getEntityBinding(); DatabaseEntry key = new DatabaseEntry(); DatabaseEntry data = new DatabaseEntry(); Cursor readCursor = db.openCursor(null, CursorConfig.READ_UNCOMMITTED); try { while (readCursor.getNext(key, data, null) == OperationStatus.SUCCESS) { if (evolveNeeded(key, data, binding)) { Transaction txn = null; if (dbConfig.getTransactional()) { boolean success = false; txn = env.beginTransaction(null, null); } boolean doCommit = false; Cursor writeCursor = null; try { writeCursor = db.openCursor(txn, null); if (writeCursor.getSearchKey(key, data, LockMode.RMW) == OperationStatus.SUCCESS) { boolean written = false; if (evolveNeeded(key, data, binding)) { writeCursor.putCurrent(data); written = true; } if (listener != null) { EvolveInternal.updateEvent(event, entityClassName, 1, written ? 1 : 0); if (!listener.evolveProgress(event)) { break; } } } } finally { if (writeCursor != null) { writeCursor.close(); } if (txn != null) { if (doCommit) { txn.commit(); } else { txn.abort(); } } } } } } finally { readCursor.close(); } }