summaryrefslogtreecommitdiff
path: root/src/main/java/com/amazon/carbonado/Storable.java
blob: d6f0c1c087607279965243e38064e9ef51a419b4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
/*
 * 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.
 *
 * <p>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<S extends Storable<S>> {
    /**
     * 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.
     *
     * <p>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.
     *
     * <p>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.
     *
     * <p>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.
     *
     * <p>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.
     *
     * <p>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.
     *
     * <p>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.
     *
     * <p>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.
     *
     * <p>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.
     *
     * <p>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.
     *
     * <p>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.
     *
     * <p>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.
     *
     * <p>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.
     *
     * <p>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.
     *
     * <p>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.
     *
     * <p>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.
     *
     * <p>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.
     *
     * <p><i>Design note: the name "getStorableType" is avoided, so as not to
     * conflict with a user defined property of "storableType"</i>
     */
    Class<S> storableType();

    /**
     * Copies all supported properties, skipping any that are uninitialized.
     * Specifically, calls "target.set&lt;property&gt;" 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&lt;property&gt; 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&lt;property&gt;" 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&lt;property&gt; 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&lt;property&gt;" 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&lt;property&gt; method
     */
    void copyVersionProperty(S target);

    /**
     * Copies all supported non-primary key properties which are unequal,
     * skipping any that are uninitialized. Specifically, calls
     * "target.get&lt;property&gt;", and if the value thus retrieved differs
     * from the local value, "target.set&lt;property&gt;" 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&lt;property&gt; methods
     */
    void copyUnequalProperties(S target);

    /**
     * Copies all supported non-primary key properties which are
     * dirty. Specifically, calls "target.set&lt;property&gt;" 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&lt;property&gt; 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<String, Object> 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.
     *
     * <p>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();
}