/* * Copyright 2006 Amazon Technologies, Inc. or its affiliates. * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks * of Amazon Technologies, Inc. or its affiliates. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.amazon.carbonado; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Map; /** * A data access object in a {@link Repository}. User defined storables must * either extend or implement this interface via an interface or abstract * class. Abstract bean properties defined in the storable are persisted into * the repository. At least one property must be annotated as the {@link * PrimaryKey}. At most one property may be annotated as being the {@link * Version} property. * *

Storable instances are mutable, but they must be thread-safe. Although * race conditions are possible if multiple threads are mutating the Storable, * the Storable instance will not get into a corrupt state. * * @author Brian S O'Neill * @author Don Schneider * * @see com.amazon.carbonado.Alias * @see com.amazon.carbonado.Indexes * @see com.amazon.carbonado.Join * @see com.amazon.carbonado.Nullable * @see com.amazon.carbonado.PrimaryKey * @see com.amazon.carbonado.Version */ public interface Storable> { /** * Loads or reloads this object from the storage layer by a primary or * alternate key. All properties of a key must be initialized for it to be * chosen. The primary key is examined first, and if not fully initialized, * alternate keys are examined in turn. * *

If load is successful, altering the primary key is no longer allowed * unless a call to delete succeeds. Attempting to alter the primary key in * this state results in an {@link IllegalStateException}. Alternate keys * may always be modified, however. * *

Note: This method differs from {@link #tryLoad} only in that it * throws an exception if no matching record was found, instead of returning * false. This may indicate that the underlying record was deleted between * a load and reload. When a FetchNoneException is thrown, this object's * state will be the same as if the delete method was called on it. * * @throws FetchNoneException if no matching record found * @throws FetchException if storage layer throws an exception * @throws IllegalStateException if the state of this instance suggests * that any primary keys are unspecified */ void load() throws FetchNoneException, FetchException; /** * Loads or reloads this object from the storage layer by a primary or * alternate key. All properties of a key must be initialized for it to be * chosen. The primary key is examined first, and if not fully initialized, * alternate keys are examined in turn. * *

If load is successful, altering the primary key is no longer allowed * unless a call to delete succeeds. Attempting to alter the primary key in * this state results in an {@link IllegalStateException}. Alternate keys * may always be modified, however. * *

Note: This method differs from {@link #load} only in that it returns * false if no matching record was found, instead of throwing an exception. * This may indicate that the underlying record was deleted between a load * and reload. When false is returned, this object's state will be the same * as if the delete method was called on it. * * @return true if found and loaded, false otherwise * @throws FetchException if storage layer throws an exception * @throws IllegalStateException if the state of this instance suggests * that any primary keys are unspecified */ boolean tryLoad() throws FetchException; /** * Inserts a new persistent value for this object. If successful, altering * the primary key is no longer allowed unless a call to delete succeeds. * Attempting to alter the primary key in this state results in an {@link * IllegalStateException}. Alternate keys may always be modified, however. * *

Insert requires that all primary key properties be specified. If not, * an {@link IllegalStateException} is thrown. Also, repository * implementations usually require that properties which are not {@link * Nullable} also be specified. Otherwise, a {@link ConstraintException} * may be thrown. * *

Note: This method differs from {@link #tryInsert} only in that it may * throw a UniqueConstraintException, instead of returning false. * * @throws UniqueConstraintException if it is absolutely known that a key * of inserted object matches an existing one * @throws ConstraintException if any required properties are unspecified * @throws PersistException if storage layer throws an exception * @throws IllegalStateException if the state of this instance suggests * that any primary keys are unspecified */ void insert() throws PersistException; /** * Inserts a new persistent value for this object. If successful, altering * the primary key is no longer allowed unless a call to delete succeeds. * Attempting to alter the primary key in this state results in an {@link * IllegalStateException}. Alternate keys may always be modified, however. * *

Insert requires that all primary key properties be specified. If not, * an {@link IllegalStateException} is thrown. Also, repository * implementations usually require that properties which are not {@link * Nullable} also be specified. Otherwise, a {@link ConstraintException} * may be thrown. * *

Note: This method differs from {@link #insert} only in that it * returns false, instead of throwing a UniqueConstraintException. * * @return false if it is absolutely known that a key of inserted object * matches an existing one * @throws ConstraintException if any required properties are unspecified * @throws PersistException if storage layer throws an exception * @throws IllegalStateException if the state of this instance suggests * that any primary keys are unspecified */ boolean tryInsert() throws PersistException; /** * Updates the persistent value of this object, regardless of whether this * object has actually been loaded or not. If successful, altering the * primary key is no longer allowed unless a call to delete succeeds. * Attempting to alter the primary key in this state results in an {@link * IllegalStateException}. Alternate keys may always be modified, however. * *

If this object has a {@link Version version} property defined, then * the update logic is a bit more strict. Updates of any storable require * that the primary keys be specified; if a version is present, the version * must be specified as well. If any of the primary key or version * properties are unspecified, an {@link IllegalStateException} will be * thrown; if they are fully specified and the version doesn't match the * current record, an {@link OptimisticLockException} is thrown. * *

Not all properties need to be set on this object when calling * update. Setting a subset results in a partial update. After a successful * update, all properties are set to the actual values in the storage * layer. Put another way, the object is automatically reloaded after a * successful update. * *

If PersistNoneException is thrown, this indicates that the underlying * record was deleted. When this happens, this object's state will be the * same as if the delete method was called on it. * * @throws PersistNoneException if record is missing and no update occurred * @throws PersistException if storage layer throws an exception * @throws OptimisticLockException if a version property exists and the * optimistic lock failed * @throws IllegalStateException if the state of this instance suggests * that any primary keys are unspecified, or if a version property is unspecified */ void update() throws PersistException; /** * Updates the persistent value of this object, regardless of whether this * object has actually been loaded or not. If successful, altering the * primary key is no longer allowed unless a call to delete succeeds. * Attempting to alter the primary key in this state results in an {@link * IllegalStateException}. Alternate keys may always be modified, however. * *

If this object has a {@link Version version} property defined, then * the update logic is a bit more strict. Updates of any storable require * that the primary keys be specified; if a version is present, the version * must be specified as well. If any of the primary key or version * properties are unspecified, an {@link IllegalStateException} will be * thrown; if they are fully specified and the version doesn't match the * current record, an {@link OptimisticLockException} is thrown. * *

Not all properties need to be set on this object when calling * update. Setting a subset results in a partial update. After a successful * update, all properties are set to the actual values in the storage * layer. Put another way, the object is automatically reloaded after a * successful update. * *

A return value of false indicates that the underlying record was * deleted. When this happens, this object's state will be the same as if * the delete method was called on it. * * @return true if record likely exists and was updated, or false if record * absolutely no longer exists and no update occurred * @throws PersistException if storage layer throws an exception * @throws OptimisticLockException if a version property exists and the * optimistic lock failed * @throws IllegalStateException if the state of this instance suggests * that any primary keys are unspecified, or if a version property is unspecified */ boolean tryUpdate() throws PersistException; /** * Deletes this object from the storage layer by its primary key, * regardless of whether this object has actually been loaded or not. * Calling delete does not prevent this object from being used again. All * property values are still valid, including the primary key. Once * deleted, the insert operation is permitted again. * *

Note: This method differs from {@link #tryDelete} only in that it may * throw a PersistNoneException, instead of returning false. * * @throws PersistNoneException if record is missing and nothing was * deleted * @throws PersistException if storage layer throws an exception * @throws IllegalStateException if the state of this instance suggests * that any primary keys are unspecified */ void delete() throws PersistException; /** * Deletes this object from the storage layer by its primary key, * regardless of whether this object has actually been loaded or not. * Calling delete does not prevent this object from being used again. All * property values are still valid, including the primary key. Once * deleted, the insert operation is permitted again. * *

Note: This method differs from {@link #delete} only in that it * returns false, instead of throwing a PersistNoneException. * * @return true if record likely existed and was deleted, or false if record * absolutely no longer exists and no delete was necessary * @throws PersistException if storage layer throws an exception * @throws IllegalStateException if the state of this instance suggests * that any primary keys are unspecified */ boolean tryDelete() throws PersistException; /** * Returns the class or interface from which this storable was * generated. This represents the data class for the storable. * *

Design note: the name "getStorableType" is avoided, so as not to * conflict with a user defined property of "storableType" */ Class storableType(); /** * Copies all supported properties, skipping any that are uninitialized. * Specifically, calls "target.set<property>" for all supported * properties in this storable, passing the value of the property from this * object. Unsupported {@link Independent independent} properties in this * or the target are not copied. * * @param target storable on which to call set<property> methods * @throws IllegalStateException if any primary key properties of target * cannot be altered */ void copyAllProperties(S target); /** * Copies all supported primary key properties, skipping any that are * uninitialized. Specifically, calls "target.set<property>" for all * supported properties which participate in the primary key, passing the * value of the property from this object. Unsupported {@link Independent * independent} properties in this or the target are not copied. * * @param target storable on which to call set<property> methods * @throws IllegalStateException if any primary key properties of target * cannot be altered */ void copyPrimaryKeyProperties(S target); /** * Copies the optional version property, unless it is uninitialized. * Specifically, calls "target.set<property>" for the version * property (if supported), passing the value of the property from this * object. If no version property is defined, then this method does * nothing. Unsupported {@link Independent independent} properties in this * or the target are not copied. * * @param target storable on which to call set<property> method */ void copyVersionProperty(S target); /** * Copies all supported non-primary key properties which are unequal, * skipping any that are uninitialized. Specifically, calls * "target.get<property>", and if the value thus retrieved differs * from the local value, "target.set<property>" is called for that * property. Unsupported {@link Independent independent} properties in this * or the target are not copied. * * @param target storable on which to call set<property> methods */ void copyUnequalProperties(S target); /** * Copies all supported non-primary key properties which are * dirty. Specifically, calls "target.set<property>" for any * non-primary key property which is dirty, passing the value of the * property from this object. A property is considered dirty when set * before a load or persist operation is called. Unsupported {@link * Independent independent} properties in this or the target are not * copied. * * @param target storable on which to call set<property> methods */ void copyDirtyProperties(S target); /** * Returns true if any non-primary key properties in this object are * dirty. A property is considered dirty when set before a load or persist * operation is called. A property becomes clean after a successful load, * insert, or update operation. */ boolean hasDirtyProperties(); /** * Marks all dirty properties as clean. Uninitialized properties remain so. * As a side-effect, initialized primary keys may no longer be altered. */ void markPropertiesClean(); /** * Marks all properties as clean, including uninitialized properties. * As a side-effect, primary keys may no longer be altered. */ void markAllPropertiesClean(); /** * Marks all clean properties as dirty. Uninitialized properties remain so. * As a side-effect, primary keys can be altered. */ void markPropertiesDirty(); /** * Marks all properties as dirty, including uninitialized properties. * As a side-effect, primary keys can be altered. */ void markAllPropertiesDirty(); /** * Returns true if the given property of this Storable has never been * loaded or set. * * @param propertyName name of property to interrogate * @throws IllegalArgumentException if property is unknown, is a join or is derived */ boolean isPropertyUninitialized(String propertyName); /** * Returns true if the given property of this Storable has been set, but no * load or store operation has been performed yet. * * @param propertyName name of property to interrogate * @throws IllegalArgumentException if property is unknown, is a join or is derived */ boolean isPropertyDirty(String propertyName); /** * Returns true if the given property of this Storable is clean. All * properties are clean after a successful load or store operation. * * @param propertyName name of property to interrogate * @throws IllegalArgumentException if property is unknown, is a join or is derived */ boolean isPropertyClean(String propertyName); /** * Returns true if the given property exists and is supported. If a * Storable has an {@link Independent} property which is not supported by * the repository, then this method returns false. * * @param propertyName name of property to check */ boolean isPropertySupported(String propertyName); /** * Returns a Storable property value by name. * * @param propertyName name of property to get value of * @return property value, which is boxed if property type is primitive * @throws IllegalArgumentException if property is unknown or if accessor * method declares throwing any checked exceptions * @throws UnsupportedOperationException if property is independent but unsupported * @throws NullPointerException if property name is null * @since 1.2 */ Object getPropertyValue(String propertyName); /** * Sets a Storable property value by name. Call insert or update to persist * the change. * * @param propertyName name of property to set value to * @param value new value for property * @throws IllegalArgumentException if property is unknown, or if value is * unsupported due to a constraint, or if mutator method declares throwing * any checked exceptions * @throws UnsupportedOperationException if property is independent but unsupported * @throws ClassCastException if value is of wrong type * @throws NullPointerException if property name is null or if primitive * value is required but value is null * @since 1.2 */ void setPropertyValue(String propertyName, Object value); /** * Returns a fixed-size map view of this Storable's properties. Properties * which declare throwing any checked exceptions are excluded from the * map. Removing and adding of map entries is unsupported. * * @return map of property name to property value; primitive property * values are boxed * @since 1.2 */ //Map propertyMap(); /** * Returns an exact shallow copy of this object, including the state. */ S copy(); /** * Serializes property values and states for temporary storage or for * network transfer. Call {@link #readFrom} to restore. Derived and join * properties are not serialized. * *

The encoding used by this method is much simpler than what is * provided by standard object serialization. It does not encode class info * or property names, which is why it is not suitable for long term * storage. * * @throws IOException if exception from stream * @throws SupportException if Storable cannot be serialized * @since 1.2 */ void writeTo(OutputStream out) throws IOException, SupportException; /** * Restores property values and states as encoded by {@link #writeTo}. * Derived properties are not directly modified, but all other properties * not restored are reset to their initial state. * * @throws IOException if exception from stream * @throws SupportException if Storable cannot be serialized * @since 1.2 */ void readFrom(InputStream in) throws IOException, SupportException; int hashCode(); /** * True if all properties and fields are equal, but ignoring the state. * * @param obj object to compare to for equality */ boolean equals(Object obj); /** * True if the supported properties which participate in the primary key * are equal. This is useful to cheaply investigate if two storables refer * to the same entity, regardless of the state of object (specifically the * non-key properties). Unsupported {@link Independent independent} * properties in this or the target are not compared. * * @param obj object to compare to for equality */ boolean equalPrimaryKeys(Object obj); /** * True if all supported properties for this object are equal. Unsupported * {@link Independent independent} properties in this or the target are not * compared. * * @param obj object to compare to for equality */ boolean equalProperties(Object obj); /** * Returns a string for debugging purposes that contains all supported * property names and values for this object. Uninitialized and unsupported * {@link Independent independent} properties are not included. */ String toString(); /** * Returns a string for debugging purposes that contains supported key * property names and values for this object. Uninitialized and unsupported * {@link Independent independent} properties are not included. */ String toStringKeyOnly(); }