All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jsimpledb.JObject Maven / Gradle / Ivy

The newest version!

/*
 * Copyright (C) 2015 Archie L. Cobbs. All rights reserved.
 */

package org.jsimpledb;

import com.google.common.base.Preconditions;

import java.util.NavigableSet;

import org.jsimpledb.core.ObjId;
import org.jsimpledb.core.util.ObjIdSet;
import org.jsimpledb.util.NavigableSets;

/**
 * Interface implemented by {@link JSimpleDB} Java model objects.
 *
 * 

* All {@link JSimpleDB} database objects are instances of runtime-generated sub-classes of user-provided Java model types. * These generated subclasses will always implement this interface, providing convenient access to database operations. * Therefore, it is conveninent to declare Java model classes {@code abstract} and {@code implements JObject}. * However, this is not strictly necessary; all of the methods declared here ultimately delegate to one of the * {@link JTransaction} support methods. * *

Object Identity and State

* *

* Every {@link JObject} has a unique 64-bit object identifier, represented as an {@link ObjId}. * All {@link JObject} instances are permanently {@linkplain #getTransaction associated} with a specific * {@linkplain JTransaction transaction}, and are the unique representatives for their corresponding {@link ObjId} * in that transaction. All field state derives from the associated transaction. * *

* There are two types of transactions: normal transactions reflecting an open transaction on the underlying * key/value database, and {@linkplain SnapshotJTransaction snapshot transactions}, which are in-memory containers * of object data. Snapshot transactions are fully functional, supporting index queries, object versioning, etc. * *

Copying Objects

* *

* This interface provides methods for copying a graph of objects between transactions, for example, from an open * key/value database transaction into an in-memory snapshot transaction ("copy out"), or vice-versa ("copy in"). * A graph of objects can be copied by specifying a starting object and either a list of reference paths, or * a cascade name (see {@link org.jsimpledb.annotation.JField @JField}). Object ID's can be remapped during * the copy if necessary, e.g., to ensure existing objects are not overwritten (see {@link CopyState}). * * @see JTransaction */ public interface JObject { /** * Get this instance's unique JSimpleDB object identifier. * *

* This method always succeeds, even if the object does not {@linkplain #exists exist}. * * @return unique database identifier for this instance */ ObjId getObjId(); /** * Get this instance's current schema version. Does not change this instance's schema version. * * @return the schema version of this instance * @throws org.jsimpledb.core.DeletedObjectException * if this object does not exist in the {@link JTransaction} associated with this instance * @throws org.jsimpledb.core.StaleTransactionException * if the transaction {@linkplain #getTransaction associated with this instance} is no longer usable */ default int getSchemaVersion() { return this.getTransaction().getSchemaVersion(this.getObjId()); } /** * Get this instance's associated {@link JTransaction}. * * @return the {@link JTransaction} that contains this instance's field state */ JTransaction getTransaction(); /** * Delete this instance, if it exists, in this instance's associated transaction. * *

* See {@link org.jsimpledb.core.Transaction#delete Transaction.delete()} for details on secondary deletions from * {@link org.jsimpledb.core.DeleteAction#DELETE} and {@link org.jsimpledb.annotation.JField#cascadeDelete}. * * @return true if instance was deleted, false if it did not exist * @throws org.jsimpledb.core.StaleTransactionException * if the transaction associated with the current thread is no longer usable * @throws org.jsimpledb.core.ReferencedObjectException if the object is referenced by some other object * through a reference field configured for {@link org.jsimpledb.core.DeleteAction#EXCEPTION} */ default boolean delete() { return this.getTransaction().delete(this); } /** * Determine whether this instance still exists in its associated transaction. * * @return true if instance exists, otherwise false * @throws org.jsimpledb.core.StaleTransactionException * if the transaction {@linkplain #getTransaction associated with this instance} is no longer usable */ default boolean exists() { return this.getTransaction().exists(this.getObjId()); } /** * Determine whether this instance is a normal instance or is an in-memory "snapshot" instance associated * with a {@link SnapshotJTransaction}. * *

* Equvialent to {@code getTransaction().isSnapshot()}. * * @return true if instance is a snapshot instance */ default boolean isSnapshot() { return this.getTransaction().isSnapshot(); } /** * Recreate a deleted instance, if it does not exist, in its associated transaction. * The fields of a recreated object are set to their initial values. If the object already exists, nothing changes. * * @return true if instance was recreated, false if it already existed * @throws org.jsimpledb.core.StaleTransactionException * if the transaction {@linkplain #getTransaction associated with this instance} is no longer usable */ default boolean recreate() { return this.getTransaction().recreate(this); } /** * Add this instance to the validation queue for validation in its associated transaction. * *

* The actual validation will occur either during {@link JTransaction#commit} * or at the next invocation of {@link JTransaction#validate}, whichever occurs first. * The specified validation groups, if any, will be used. * *

* If the associated transaction was opened with {@link ValidationMode#DISABLED}, no validation will be performed. * * @param groups validation group(s) to use for validation; if empty, {@link javax.validation.groups.Default} is assumed * @throws org.jsimpledb.core.DeletedObjectException * if this object does not exist in the {@link JTransaction} associated with this instance * @throws IllegalStateException if transaction commit is already in progress * @throws org.jsimpledb.core.StaleTransactionException * if the transaction {@linkplain #getTransaction associated with this instance} is no longer usable * @throws NullPointerException if {@code groups} is null */ default void revalidate(Class... groups) { this.getTransaction().revalidate(this.getObjId(), groups); } /** * Update the schema version of this instance, if necessary, so that it matches the schema version * of its associated transaction. * *

* If a version change occurs, matching {@link org.jsimpledb.annotation.OnVersionChange @OnVersionChange} * methods will be invoked prior to this method returning. * * @return true if the object's schema version was changed, false if it was already updated * @throws org.jsimpledb.core.DeletedObjectException * if this object does not exist in the {@link JTransaction} associated with this instance * @throws org.jsimpledb.core.StaleTransactionException * if the transaction {@linkplain #getTransaction associated with this instance} is no longer usable */ default boolean upgrade() { return this.getTransaction().updateSchemaVersion(this); } /** * Copy this instance, and other instances it references through the specified reference paths, into the * specified destination transaction. * *

* This is a more general method; see {@link #copyIn copyIn()} and {@link #copyOut copyOut()} * for more common and specific use cases. * *

* This method copies this object and all other objects reachable from this instance through any of the specified * {@linkplain ReferencePath reference paths} (including intermediate objects visited). * *

* This instance will first be {@link #upgrade}ed if necessary. If any copied object already exists in {@code dest}, * it will have its schema version updated first, if necessary, then be overwritten. * Any {@link org.jsimpledb.annotation.OnCreate @OnVersionChange}, {@link org.jsimpledb.annotation.OnCreate @OnCreate}, * and {@link org.jsimpledb.annotation.OnCreate @OnChange} methods will be notified accordingly as usual (in {@code dest}); * however, for {@link org.jsimpledb.annotation.OnCreate @OnCreate} and * {@link org.jsimpledb.annotation.OnCreate @OnChange}, the annotation must have {@code snapshotTransactions = true} * if {@code dest} is a {@link SnapshotJTransaction}. * *

* The two transactions must be compatible in that for any schema versions encountered, those schema versions * must be identical in both transactions. * *

* Circular references are handled properly: if an object is encountered more than once, it is not copied again. * The {@code copyState} tracks which objects have already been copied and/or traversed along some reference path. * For a "fresh" copy operation, pass a newly created {@link CopyState}; for a copy operation that is a continuation * of a previous copy, reuse the previous {@code copyState}. The {@link CopyState} may also be configured to remap object ID's. * *

* Warning: if two threads attempt to copy objects between the same two transactions at the same time * but in opposite directions, deadlock could result. * * @param dest destination transaction for copies * @param copyState tracks which indirectly referenced objects have already been copied * @param refPaths zero or more reference paths that refer to additional objects to be copied * @return the copied version of this instance in {@code dest} * @throws org.jsimpledb.core.DeletedObjectException * if this object does not exist in the {@link JTransaction} associated with this instance * (no exception is thrown however if an indirectly referenced object does not exist unless it is traversed) * @throws org.jsimpledb.core.DeletedObjectException if any object to be copied does not actually exist * @throws org.jsimpledb.core.DeletedObjectException if any copied object ends up with a reference to an object * that does not exist in {@code dest} through a reference field configured to disallow deleted assignment * @throws org.jsimpledb.core.SchemaMismatchException * if the schema corresponding to any copied object is not identical in both the {@link JTransaction} * associated with this instance and {@code dest} * @throws IllegalArgumentException if any parameter is null * @throws IllegalArgumentException if any path in {@code refPaths} is invalid * @see #copyIn copyIn() * @see #copyOut copyOut() * @see JTransaction#copyTo(JTransaction, JObject, CopyState, String[]) JTransaction.copyTo() * @see ReferencePath */ default JObject copyTo(JTransaction dest, CopyState copyState, String... refPaths) { return this.getTransaction().copyTo(dest, this, copyState, refPaths); } /** * Copy this instance and other instances it references through the specified reference paths (if any) * into the associated in-memory snapshot transaction. * *

* Normally this method would only be invoked on a regular database {@link JObject}. * The returned object will always be a snapshot {@link JObject}. * *

* This is a convenience method, and is equivalent to invoking: *

* this.copyTo(this.getTransaction().getSnapshotTransaction(), new CopyState(), refPaths); *
* * @param refPaths zero or more reference paths that refer to additional objects to be copied * @return the snapshot {@link JObject} copy of this instance * @throws org.jsimpledb.core.DeletedObjectException if any copied object ends up with a reference to an object * that does not exist in {@code dest} through a reference field * {@linkplain org.jsimpledb.annotation.JField#allowDeletedSnapshot configured} to disallow deleted assignment * in snapshot transactions * @throws IllegalArgumentException if this instance is a {@linkplain #isSnapshot snapshot instance} * @throws IllegalArgumentException if any path in {@code refPaths} is invalid * @see #copyIn copyIn() */ default JObject copyOut(String... refPaths) { return this.copyTo(this.getTransaction().getSnapshotTransaction(), new CopyState(), refPaths); } /** * Copy this instance, and other instances it references through the specified {@code refPaths} (if any), * into the transaction associated with the current thread. * *

* Normally this method would only be invoked on a snapshot {@link JObject}. * The returned object will be a {@link JObject} in the currently open transaction. * *

* This is a convenience method, and is equivalent to invoking: *

* this.copyTo(JTransaction.getCurrent(), new CopyState(), refPaths) *
* * @param refPaths zero or more reference paths that refer to additional objects to be copied * @return the regular database copy of this instance * @throws org.jsimpledb.core.DeletedObjectException if any copied object ends up with a reference to an object * that does not exist in {@code dest} through a reference field * {@linkplain org.jsimpledb.annotation.JField#allowDeletedSnapshot configured} to disallow deleted assignment * @throws org.jsimpledb.core.SchemaMismatchException * if the schema corresponding to this object's version is not identical in both transactions * @throws IllegalArgumentException if any path in {@code refPaths} is invalid * @see #copyOut copyOut() */ default JObject copyIn(String... refPaths) { return this.copyTo(JTransaction.getCurrent(), new CopyState(), refPaths); } /** * Copy this instance and all objects reachable from it via the specified cascade into the specified destination transaction. * *

* This is a convenience method, and is equivalent to invoking: *

* this.cascadeCopyTo(dest, cascadeName, -1, clone); *
* * @param dest destination transaction for copies * @param cascadeName cascade name, or null for no cascade (i.e., copy only this instance) * @param clone true to clone objects, i.e., assign the copies new, unused object ID's in {@code dest}, * or false to preserve the same object ID's, overwriting any existing objects in {@code dest} * @return the copied version of this instance in {@code dest} * @throws org.jsimpledb.core.DeletedObjectException if any object to be copied does not exist * @throws org.jsimpledb.core.DeletedObjectException if any copied object ends up with a reference to an object * that does not exist in {@code dest} through a reference field configured to disallow deleted assignment * @throws org.jsimpledb.core.SchemaMismatchException * if the schema version corresponding to any copied object is not identical in both transactions * @throws IllegalArgumentException if {@code dest} is null * @see #cascadeCopyTo(JTransaction, String, int, boolean) */ default JObject cascadeCopyTo(JTransaction dest, String cascadeName, boolean clone) { return this.cascadeCopyTo(dest, cascadeName, -1, clone); } /** * Copy this instance and all objects reachable from it via the specified cascade into the specified destination transaction. * *

* This is a more general method; see {@link #cascadeCopyIn cascadeCopyIn()} and {@link #cascadeCopyOut cascadeCopyOut()} * for more common and specific use cases. * *

* This method finds and copies all objects reachable from this object based on * {@link org.jsimpledb.annotation.JField#cascades @JField.cascades()} and * {@link org.jsimpledb.annotation.JField#inverseCascades @JField.inverseCascades()} annotation properties on * reference fields: a reference field is traversed in the forward or inverse direction if {@code cascadeName} is * specified in the corresponding annotation property. See {@link org.jsimpledb.annotation.JField @JField} for details. * *

* The {@code recursionLimit} parameter can be used to limit the maximum distance of any copied object, * measured in the number of reference field "hops" from this object. * *

* This instance will first be {@link #upgrade}ed if necessary. If any copied object already exists in {@code dest}, * it will have its schema version updated first, if necessary, then be overwritten. * Any {@link org.jsimpledb.annotation.OnCreate @OnVersionChange}, {@link org.jsimpledb.annotation.OnCreate @OnCreate}, * and {@link org.jsimpledb.annotation.OnCreate @OnChange} methods will be notified accordingly as usual (in {@code dest}); * however, for {@link org.jsimpledb.annotation.OnCreate @OnCreate} and * {@link org.jsimpledb.annotation.OnCreate @OnChange}, the annotation must have {@code snapshotTransactions = true} * if {@code dest} is a {@link SnapshotJTransaction}. * *

* The two transactions must be compatible in that for any schema versions encountered, those schema versions * must be identical in both transactions. * *

* Circular references are handled properly: if an object is encountered more than once, it is not copied again. * The {@code copyState} tracks which objects have already been copied and traversed. * For a "fresh" copy operation, pass a newly created {@link CopyState}; for a copy operation that is a continuation * of a previous copy, reuse the previous {@code copyState}. The {@link CopyState} may also be configured to remap object ID's. * *

* Warning: if two threads attempt to copy objects between the same two transactions at the same time * but in opposite directions, deadlock could result. * * @param dest destination transaction for copies * @param cascadeName cascade name, or null for no cascade (i.e., copy only this instance) * @param recursionLimit the maximum number of references to hop through, or -1 for infinity * @param clone true to clone objects, i.e., assign the copies new, unused object ID's in {@code dest}, * or false to preserve the same object ID's, overwriting any existing objects in {@code dest} * @return the copied version of this instance in {@code dest} * @throws org.jsimpledb.core.DeletedObjectException if any object to be copied does not exist * @throws org.jsimpledb.core.DeletedObjectException if any copied object ends up with a reference to an object * that does not exist in {@code dest} through a reference field configured to disallow deleted assignment * @throws org.jsimpledb.core.SchemaMismatchException * if the schema version corresponding to any copied object is not identical in both transactions * @throws IllegalArgumentException if {@code recursionLimit} is less that -1 * @throws IllegalArgumentException if {@code dest} is null * @see #cascadeCopyIn cascadeCopyIn() * @see #cascadeCopyOut cascadeCopyOut() * @see JTransaction#cascadeFindAll JTransaction.cascadeFindAll() */ default JObject cascadeCopyTo(JTransaction dest, String cascadeName, int recursionLimit, boolean clone) { Preconditions.checkArgument(dest != null, "null dest"); final ObjId id = this.getObjId(); final JTransaction jtx = this.getTransaction(); final ObjIdSet ids = jtx.cascadeFindAll(id, cascadeName, recursionLimit); final CopyState copyState = clone ? new CopyState(dest.createClones(ids)) : new CopyState(); jtx.copyTo(dest, copyState, ids); return dest.get(copyState.getDestinationId(id)); } /** * Copy this instance and all objects reachable from it via the specified cascade * into the associated in-memory snapshot transaction. * *

* Normally this method would only be invoked on a regular database {@link JObject}. * The returned object will always be a snapshot {@link JObject}. * *

* This is a convenience method, and is equivalent to invoking: *

* this.cascadeCopyTo(this.getTransaction().getSnapshotTransaction(), cascadeName, clone); *
* * @param cascadeName cascade name, or null for no cascade (i.e., copy only this instance) * @param clone true to clone objects, i.e., assign the copies new, unused object ID's in the snapshot transaction, * or false to preserve the same object ID's, overwriting any existing objects * @return the snapshot {@link JObject} copy of this instance * @throws org.jsimpledb.core.DeletedObjectException if any object to be copied does not exist * @throws org.jsimpledb.core.DeletedObjectException if any copied object ends up with a reference to an object * that does not exist in {@code dest} through a reference field * {@linkplain org.jsimpledb.annotation.JField#allowDeletedSnapshot configured} to disallow deleted assignment * in snapshot transactions * @throws org.jsimpledb.core.SchemaMismatchException * if the schema version corresponding to any copied object is not identical in both transactions * @throws IllegalArgumentException if this instance is a {@linkplain #isSnapshot snapshot instance} * @see #cascadeCopyIn cascadeCopyIn() * @see #cascadeCopyTo cascadeCopyTo() */ default JObject cascadeCopyOut(String cascadeName, boolean clone) { return this.cascadeCopyTo(this.getTransaction().getSnapshotTransaction(), cascadeName, clone); } /** * Copy this instance and all objects reachable from it via the specified cascade * into the transaction associated with the current thread. * *

* Normally this method would only be invoked on a snapshot {@link JObject}. * The returned object will be a {@link JObject} in the currently open transaction. * *

* This is a convenience method, and is equivalent to invoking: *

* this.cascadeCopyTo(JTransaction.getCurrent(), cascadeName, clone); *
* * @param cascadeName cascade name, or null for no cascade (i.e., copy only this instance) * @param clone true to clone objects, i.e., assign the copies new, unused object ID's in the database transaction, * or false to preserve the same object ID's, overwriting any existing objects * @return the regular database copy of this instance * @throws org.jsimpledb.core.DeletedObjectException if any object to be copied does not exist * @throws org.jsimpledb.core.DeletedObjectException if any copied object ends up with a reference to an object * that does not exist in {@code dest} through a reference field * {@linkplain org.jsimpledb.annotation.JField#allowDeletedSnapshot configured} to disallow deleted assignment * @throws org.jsimpledb.core.SchemaMismatchException * if the schema version corresponding to any copied object is not identical in both transactions * @see #cascadeCopyOut cascadeCopyOut() * @see #cascadeCopyTo cascadeCopyTo() */ default JObject cascadeCopyIn(String cascadeName, boolean clone) { return this.cascadeCopyTo(JTransaction.getCurrent(), cascadeName, clone); } /** * Reset cached simple field values. * *

* {@link JObject}s instances may cache simple field values after they have been read from the underlying * key/value store for efficiency. This method causes any such cached values to be forgotten, so they will * be re-read from the underlying key/value store on the next read of the field. * *

* Normally this method does not need to be used. It may be needed to maintain consistency * in exotic situations, for example, where the underlying key/value store is being modified directly. */ void resetCachedFieldValues(); /** * Find all objects of the given type referring to this object through the specified reference field. * *

* The {@code fieldName} can be the name of a simple reference field (e.g., {@code "teacher"}) * or a sub-field of a complex field (e.g., {@code "students.element"}). * * @param type type of referring objects * @param fieldName name of reference field * @param type of referring objects * @return all objects of the specified type referring to this object through the named field * @throws org.jsimpledb.kv.StaleTransactionException if the transaction associated with this instance is no longer open * @throws IllegalArgumentException if either parameter is null */ default NavigableSet findReferring(Class type, String fieldName) { final NavigableSet set = this.getTransaction().queryIndex(type, fieldName, Object.class).asMap().get(this); return set != null ? set : NavigableSets.empty(); } /** * Get the {@link JClass} of which this {@link JObject} is an instance. * * @return associated {@link JClass} */ default JClass getJClass() { return this.getTransaction().getJSimpleDB().getJClass(this.getObjId()); } /** * Get the original Java model class of which this {@link JObject} is an instance. * * @return Java model class */ Class getModelClass(); }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy