
com.sleepycat.persist.impl.PersistEntityBinding Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of je Show documentation
Show all versions of je Show documentation
Berkeley DB Java Edition is a open source, transactional storage solution for Java applications. The Direct Persistence Layer (DPL) API is faster and easier to develop, deploy, and manage than serialized object files or ORM-based Java persistence solutions. The Collections API enhances the standard java.util.collections classes allowing them to be persisted to a local file system and accessed concurrently while protected by ACID transactions. Data is stored by serializing objects and managing class and instance data separately so as not to waste space. Berkeley DB Java Edition is the reliable drop-in solution for complex, fast, and scalable storage. Source for this release is in 'je-4.0.92-sources.jar', the Javadoc is located at 'http://download.oracle.com/berkeley-db/docs/je/4.0.92/'.
/*-
* Copyright (C) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
*
* This file was distributed by Oracle as part of a version of Oracle Berkeley
* DB Java Edition made available at:
*
* http://www.oracle.com/technetwork/database/database-technologies/berkeleydb/downloads/index.html
*
* Please see the LICENSE file included in the top-level directory of the
* appropriate version of Oracle Berkeley DB Java Edition for a copy of the
* license and additional information.
*/
package com.sleepycat.persist.impl;
import com.sleepycat.bind.EntityBinding;
import com.sleepycat.bind.tuple.TupleBase;
import com.sleepycat.compat.DbCompat;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.persist.raw.RawObject;
/**
* A persistence entity binding for a given entity class.
*
* @author Mark Hayes
*/
public class PersistEntityBinding implements EntityBinding {
/* See Store.refresh for an explanation of the use of volatile fields. */
volatile PersistCatalog catalog;
volatile Format entityFormat;
final boolean rawAccess;
PersistKeyAssigner keyAssigner;
/**
* Creates a key binding for a given entity class.
*/
public PersistEntityBinding(final PersistCatalog catalogParam,
final String entityClassName,
final boolean rawAccess) {
catalog = catalogParam;
try {
entityFormat = getOrCreateFormat(catalog, entityClassName,
rawAccess);
} catch (RefreshException e) {
/* Must assign catalog field in constructor. */
catalog = e.refresh();
try {
entityFormat = getOrCreateFormat(catalog, entityClassName,
rawAccess);
} catch (RefreshException e2) {
throw DbCompat.unexpectedException(e2);
}
}
if (!entityFormat.isEntity()) {
throw new IllegalArgumentException
("Not an entity class: " + entityClassName);
}
this.rawAccess = rawAccess;
}
public PersistKeyAssigner getKeyAssigner() {
return keyAssigner;
}
public Object entryToObject(final DatabaseEntry key,
final DatabaseEntry data) {
try {
return entryToObjectInternal(key, null, data);
} catch (RefreshException e) {
e.refresh();
try {
return entryToObjectInternal(key, null, data);
} catch (RefreshException e2) {
throw DbCompat.unexpectedException(e2);
}
}
}
/**
* This method will be used in PrimaryIndex.get, where the primary key is
* known to DPL. This method will force to call readEntityWithPriKey to
* directly assign primary key to the de-serialized object.
*/
public Object entryToObjectWithPriKey(final Object priKey,
final DatabaseEntry data) {
try {
if (priKey == null) {
throw new
IllegalArgumentException("Primary key cannot be null.");
}
return entryToObjectInternal(null, priKey, data);
} catch (RefreshException e) {
e.refresh();
try {
return entryToObjectInternal(null, priKey, data);
} catch (RefreshException e2) {
throw DbCompat.unexpectedException(e2);
}
}
}
/**
* This method is always called after reading an entity. If a refresh is
* needed, we detect that in PersistCatalog.getFormat(int).
*/
private Object entryToObjectInternal(final DatabaseEntry key,
final Object priKey,
final DatabaseEntry data)
throws RefreshException {
return readEntity(catalog, key, priKey, data, rawAccess);
}
/**
* Creates the instance, reads the entity key first to track visited
* entities correctly, then reads the data and returns the entity.
*
* This is a special case of EntityInput.readObject for a top level entity.
* Special treatments are:
* - The formatId must be >= 0; since this is the top level instance, it
* cannot refer to a visited object nor be a null reference.
* - The entity is not checked for existence in the visited object set;
* entities cannot be referenced by another entity.
* - Reader.readPriKey must be called prior to calling Reader.readObject.
*/
static Object readEntity(Catalog useCatalog,
DatabaseEntry key,
Object priKey,
DatabaseEntry data,
boolean rawAccess)
throws RefreshException {
RecordInput dataInput = new RecordInput
(useCatalog, rawAccess, null, 0,
data.getData(), data.getOffset(), data.getSize());
int initialOffset = dataInput.getBufferOffset();
int formatId = dataInput.readPackedInt();
Format format = useCatalog.getFormat(formatId, true /*expectStored*/);
dataInput.registerEntityFormat(format);
Reader reader = format.getReader();
Object entity = reader.newInstance(dataInput, rawAccess);
if (priKey == null) {
/* If priKey is null, need to deserialize the primary key. */
RecordInput keyInput = new RecordInput
(useCatalog, rawAccess, null, 0,
key.getData(), key.getOffset(), key.getSize());
reader.readPriKey(entity, keyInput, rawAccess);
} else {
/*
* If priKey is not null, directly assign it to the primary key
* field. [#19248]
*/
Accessor accessor =
reader.getAccessor(entity instanceof RawObject ?
true :
rawAccess);
if (accessor == null) {
accessor = format.getLatestVersion().getReader().
getAccessor(entity instanceof RawObject ?
true :
rawAccess);
}
accessor.setPriField(entity, priKey);
}
dataInput.registerEntity(entity, initialOffset);
entity = reader.readObject(entity, dataInput, rawAccess);
return entity;
}
public void objectToData(final Object entity, final DatabaseEntry data) {
try {
objectToDataInternal(entity, data);
} catch (RefreshException e) {
e.refresh();
try {
objectToDataInternal(entity, data);
} catch (RefreshException e2) {
throw DbCompat.unexpectedException(e2);
}
}
}
/**
* This method is always called before writing an entity. If a refresh is
* needed, we detect that here.
*/
private void objectToDataInternal(final Object entity,
final DatabaseEntry data)
throws RefreshException {
Format format = getValidFormat(entity);
/* Before a write, check whether a refresh is needed. [#16655] */
catalog.checkWriteInReplicaUpgradeMode();
writeEntity(format, catalog, entity, data, rawAccess);
}
/**
* Writes the formatId and object, and returns the bytes.
*
* This is a special case of EntityOutput.writeObject for a top level
* entity. Special treatments are:
* - The entity may not be null.
* - The entity is not checked for existence in the visited object set;
* entities cannot be referenced by another entity.
*/
static void writeEntity(Format format,
Catalog catalog,
Object entity,
DatabaseEntry data,
boolean rawAccess)
throws RefreshException {
RecordOutput output = new RecordOutput(catalog, rawAccess);
output.registerEntity(entity);
output.writePackedInt(format.getId());
format.writeObject(entity, output, rawAccess);
TupleBase.outputToEntry(output, data);
}
public void objectToKey(final Object entity, final DatabaseEntry key) {
try {
objectToKeyInternal(entity, key);
} catch (RefreshException e) {
e.refresh();
try {
objectToKeyInternal(entity, key);
} catch (RefreshException e2) {
throw DbCompat.unexpectedException(e2);
}
}
}
private void objectToKeyInternal(final Object entity,
final DatabaseEntry key)
throws RefreshException {
/*
* Write the primary key field as a special case since the output
* format is for a key binding, not entity data.
*/
Format format = getValidFormat(entity);
RecordOutput output = new RecordOutput(catalog, rawAccess);
/* Write the primary key and return the bytes. */
format.writePriKey(entity, output, rawAccess);
TupleBase.outputToEntry(output, key);
}
/**
* Returns the format for the given entity and validates it, throwing an
* exception if it is invalid for this binding.
*/
private Format getValidFormat(Object entity)
throws RefreshException {
/* A null entity is not allowed. */
if (entity == null) {
throw new IllegalArgumentException("An entity may not be null");
}
/*
* Get the format. getFormat throws IllegalArgumentException if the
* class is not persistent.
*/
Format format;
if (rawAccess) {
if (!(entity instanceof RawObject)) {
throw new IllegalArgumentException
("Entity must be a RawObject");
}
format = (Format) ((RawObject) entity).getType();
} else {
format = catalog.getFormat
(entity.getClass(), true /*checkEntitySubclassIndexes*/);
}
/* Check that the entity class/subclass is valid for this binding. */
if (format.getEntityFormat() != entityFormat) {
throw new IllegalArgumentException
("The entity class (" + format.getClassName() +
") must be this entity class or a subclass of it: " +
entityFormat.getClassName());
}
return format;
}
/**
* Utility method for getting or creating a format as appropriate for
* bindings and key creators.
*/
static Format getOrCreateFormat(Catalog useCatalog,
String clsName,
boolean rawAccess)
throws RefreshException {
if (rawAccess) {
Format format = useCatalog.getFormat(clsName);
if (format == null) {
throw new IllegalArgumentException
("Not a persistent class: " + clsName);
}
return format;
} else {
Class cls = useCatalog.resolveKeyClass(clsName);
return useCatalog.getFormat(cls,
true /*checkEntitySubclassIndexes*/);
}
}
/**
* See Store.refresh.
*/
void refresh(final PersistCatalog newCatalog) {
catalog = newCatalog;
entityFormat = newCatalog.getFormat(entityFormat.getClassName());
if (keyAssigner != null) {
keyAssigner.refresh(newCatalog);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy