public abstract class BaseRecord extends java.lang.Object implements DmoState, Persistable
Access to the internal state of this class is not thread-safe. Thus, instances are not intended to be accessed from more than one thread.
Modifier and Type | Field and Description |
---|---|
private RecordBuffer |
activeBuffer
The currently active buffer when state is changing
|
protected int |
activeOffset
The offset of the property currently being modified, if any (else
-1 ) |
private java.lang.Object |
activePropPreviousValue
The most recent value of the property currently being modified, for rollback purposes
|
private ChangeSet |
changeSet
Object which tracks change to enable rollback of the record's data and state
|
protected java.lang.Object[] |
data
The data represented by this record, in it's low-level (database friendly) form
|
private java.util.BitSet |
dirtyProps
Map of properties touched, each bit corresponds with a property in the data array
|
protected java.lang.Long |
id
Surrogate primary key of the record
|
private IndexState |
indexState
Tracker to manage which indices have been updated by property updates
|
private java.util.BitSet |
nullProps
Map of properties currently set to null, indicating unknown value
|
static java.lang.reflect.Method |
PK_GETTER
The cached getter method for the PK property.
|
private RecordIdentifier<java.lang.String> |
recid
Record identifier (lock variant, which uses table name) for this record (created lazily)
|
protected int |
references
Count of references to this DMO; while positive, it must remain in the current session cache
|
private java.util.concurrent.atomic.AtomicIntegerArray |
sharedVersion
Shared version data structure, for version updates and stale checks
|
protected RecordState |
state
Current state of the record
|
private java.util.BitSet |
unvalidated
Map of properties currently requiring validation
|
private int |
version
Current version number, for stale checks
|
Modifier | Constructor and Description |
---|---|
protected |
BaseRecord()
Default constructor which can only be invoked by subclasses.
|
Modifier and Type | Method and Description |
---|---|
protected abstract RecordMeta |
_recordMeta()
Get the static record metadata for the concrete subclass.
|
private void |
bulkDataChanged(java.util.List<java.lang.Integer> offsets,
java.util.List<java.lang.Object> datums)
Multiple property values have changed.
|
boolean |
checkState(int flags)
Checks whether a set of record state flags are set for this record.
|
(package private) void |
clearDirtyState(Session session)
Reset record and property state after database is synchronized with the record's data
(i.e., the most recent set of changes have been persisted).
|
(package private) void |
commit()
The current application transaction has been committed.
|
void |
copy(BaseRecord from)
Makes this a copy of a source
BaseRecord object. |
static boolean |
copy(BaseRecord to,
BaseRecord from,
boolean b4reserved)
Copy the source record to the destination record.
|
private void |
dataChanged(int offset,
java.lang.Object datum)
A property's value has changed.
|
BaseRecord |
deepCopy()
Copy this record's data and record state, including its primary key.
|
(package private) int |
getActiveOffset()
Get the offset of the property currently being updated, if any.
|
java.lang.Object[] |
getActiveUpdateDiffs()
Get a two element array representing the old and new values of the property currently being changed.
|
protected java.lang.String |
getCodePage(int offset)
Obtain the codepage of the
clob object at position offset . |
java.lang.Object[] |
getData(PropertyMapper pm)
This accessor allows direct access to private
data array only to
instances of PropertyMapper . |
java.lang.Object[] |
getData(RecordBuffer rb)
This accessor allows direct access to private
data array only to
instances of RecordBuffer . |
protected java.lang.Object |
getDatum(int offset)
Get the field's value on the specified offset.
|
private java.util.BitSet |
getDirtyIndices(java.util.BitSet[] indices,
int dirtyOffset,
java.util.BitSet filter,
boolean full)
Get a bit set representing dirty unique or non-unique indices, subject to the conditions described by
the parameters.
|
java.util.BitSet |
getDirtyIndices(int dirtyOffset,
boolean unique,
boolean remaining)
Get a bit set representing dirty unique or non-unique indices, subject to the conditions described by
the parameters.
|
java.util.BitSet |
getDirtyProps() |
IndexState |
getIndexState()
Get the update state of this record's indices.
|
(package private) java.util.BitSet |
getNullProps()
Get a bit set indicating the positions of data elements which currently are set to
null . |
RecordIdentifier<java.lang.String> |
getRecordLockIdentifier()
Get the unique identifier for this record, comprised of its table name and primary key.
|
private java.lang.String |
getTable()
Get the name of the primary table associated with this record.
|
(package private) java.util.BitSet |
getUnvalidatedIndices(int dirtyOffset,
boolean allUnvalidated)
Get a bit set representing the unique indices, if any, which are either fully or partially updated
and which have not already been validated.
|
(package private) void |
indexUpdated(int idxUid)
An index was updated.
|
void |
initialize(boolean setDefaults)
Initialize this record instance, by setting all fields to their default values and also initializing
the
indexState , unvalidated and nullProps states. |
void |
initialize(Session session,
boolean setDefaults)
Populate the record's data with its default, initial values and update the record and
property states accordingly.
|
boolean |
isAnyIndexFullyDirty()
Indicate whether all index components for any index on this record are dirty (i.e., have been touched,
not necessarily changed..
|
(package private) boolean |
isAnyIndexFullyDirty(java.util.BitSet[] indices)
Compare the bitsets in the given array with the bitset of dirty properties for this record
and indicate whether all index components for any index in the array are dirty.
|
boolean |
isDirty()
Indicate whether any property in the record currently is dirty (i.e., touched, not
necessarily changed).
|
boolean |
isIndexed()
Report whether the table associated with this record has any (legacy) index.
|
boolean |
isInUse()
Indicate whether this DMO is "in use"; that is, whether buffers reference it or it is being tracked
for UNDO processing.
|
(package private) boolean |
isPropertyDirty(int offset)
Indicate whether the property at the given offset in the DMO's data array is dirty (i.e., has been
touched, but not necessarily changed).
|
(package private) boolean |
isStale()
Indicate whether this record's data is stale, based on changes committed by other user contexts.
|
boolean |
isValid()
Indicate whether the record has passed validation.
|
private boolean |
lockForUpdate()
Obtain an exclusive lock for the current record on behalf of the active buffer, if any.
|
(package private) void |
markValidated()
Mark all dirty properties as having been validated.
|
(package private) void |
markValidated(int bit)
Mark a single property as having been validated.
|
boolean |
needsValidation()
Indicate whether any of this record's validatable properties are dirty and have not been validated.
|
private int |
prepareChangeSet(Session session)
If this is the first update to this record since an application transaction began, prepare a
change set to track updates made to the record during the transaction, to be used for full or
sub-transaction rollback, if needed.
|
java.lang.Long |
primaryKey()
Get the database identifier for this persistable object.
|
void |
primaryKey(java.lang.Long id)
Set the database identifier for this persistable object (i.e., surrogate primary key).
|
(package private) int |
readProperty(int propOffset,
java.sql.ResultSet rs,
int rsOffset)
Read a property directly from a
ResultSet . |
(package private) void |
rollback(int txLevel)
Perform a full or sub-transaction level rollback of this record to its state as it was immediately
before the block of the specified transaction nesting level was entered.
|
java.lang.Runnable |
setActiveBuffer(RecordBuffer buffer)
Set the record buffer which is currently active for this record.
|
protected boolean |
setAllData(BaseRecord source)
Set all datum in the data array.
|
protected void |
setDatum(int offset,
java.lang.Object datum)
Set a single datum into the data array, and update the record and property state accordingly.
|
java.lang.String |
toString()
Return a string representation of this object, primarily for debug purposes.
|
java.lang.String |
toString(boolean includeData)
Return a string representation of this object, primarily for debug purposes, optionally including the
record's data in the output.
|
void |
undoActive() |
private void |
updateState(int flags,
boolean set)
Updates the set of state flags for this record.
|
void |
updateState(Session session,
int flags,
boolean set)
Updates the set of state flags for this record.
|
(package private) void |
version(java.util.concurrent.atomic.AtomicIntegerArray sharedVersion)
Set this record's version to the canonical, shared version number for this record.
|
public static final java.lang.reflect.Method PK_GETTER
protected java.lang.Long id
protected final java.lang.Object[] data
protected final RecordState state
protected int references
private final java.util.BitSet dirtyProps
private final java.util.BitSet unvalidated
private final java.util.BitSet nullProps
private final IndexState indexState
private RecordIdentifier<java.lang.String> recid
private RecordBuffer activeBuffer
protected int activeOffset
-1
)private java.lang.Object activePropPreviousValue
private ChangeSet changeSet
private int version
private transient java.util.concurrent.atomic.AtomicIntegerArray sharedVersion
protected BaseRecord()
initialize(Session, boolean)
method should be invoked after construction.public static boolean copy(BaseRecord to, BaseRecord from, boolean b4reserved)
The records are checked first to decide whether they are compatible (I.e. the number of properties
matches and also the type for each field, including the optional extent attribute). If not, the method
returns false
and not copy operation is executed.
If the records pass validation, all properties are copied from source (from
) to destination
to
). At the same time, the status flags are updated so that the destination record can be
correctly saved.
to
- The destination record.from
- The source record.b4reserved
- If true
the before-table specific attributes are copied, too. Otherwise only the normal
properties are processed.true
if the copy was performed (record's tables match).public void initialize(Session session, boolean setDefaults)
This method should be invoked only once, immediately after the object is constructed. It is not invoked directly from the constructor, because the only use case which requires the initialization performed here is a new data record being created on behalf of application logic.
This is done separately from construction because there are use cases where an instance of this class is needed, but we do not need the overhead of initializing the data with default values, as they will be overwritten anyway. For instance, if the object is read from the database or a copy/snapshot is being made, the data array will be populated with the values retrieved from the database or copied from the other record, respectively.
It is not invoked optionally from the constructor, because a default constructor called from subclasses is preferable, as the subclasses are often constructed by reflection.
This operation updates the not-null bitset, based on the values of non-nullable columns.
session
- Database session, which is needed if this record must have its changes tracked for
UNDO/rollback purposes; otherwise, may be null
.setDefaults
- If true
, set all default property values; if false
, leave data array
filled with null values.public void initialize(boolean setDefaults)
indexState
, unvalidated
and nullProps
states.
This method should be invoked only once, immediately after the object is constructed. It is not invoked directly from the constructor, because the only use case which requires the initialization performed here is a new data record being created on behalf of application logic.
This will be used by, JSON deserialization
when creating a new record for an
instance associated with the web service request (either request or response).
setDefaults
- If true
, set all default property values; if false
, leave data array
filled with null values.public boolean isInUse()
true
if in use by the above definition, else false
.public IndexState getIndexState()
public final java.lang.Long primaryKey()
null
for DMO instances which have
not yet been persisted to the database.primaryKey
in interface Persistable
public final void primaryKey(java.lang.Long id)
primaryKey
in interface Persistable
id
- Unique, database identifier to be associated with this object.public BaseRecord deepCopy()
public void copy(BaseRecord from)
BaseRecord
object. For permanent databases, the
internal data
and the id
of the BaseRecord
are copied.
The only state copied is that of which properties are dirty (all of them after the copy). Thus, this method is for specialized, internal use only, such as creating a new record in order to copy data from one temp-table buffer to another.
from
- The source to copy data from.public boolean checkState(int flags)
flags
- One or more RecordState
flags, bitwise OR'd together.true
if ALL flags are set.RecordState
public boolean isValid()
true
if this record has passed validation and has not since had any of its
properties changed, else false
.public void updateState(Session session, int flags, boolean set)
set
parameter, the
flags can be set (when true
) or removed (when false
). If one flag is already
set and set == true
that flag remains unchanged. The same applies for un-setting an
inactive flag.session
- Database session (used for UNDO purposes).flags
- A set of OR-ed RecordState
flags to be updated.set
- If true
the flags will be set, otherwise they will be unset.RecordState
public RecordIdentifier<java.lang.String> getRecordLockIdentifier()
public boolean isIndexed()
true
if there is at least one legacy index defined in the table of this record.public java.lang.Runnable setActiveBuffer(RecordBuffer buffer)
A call to this method must be bracketed with a call to run
the Runnable
returned by
this method. This allows nested invocations and ensures that the active buffer and related state are
reset properly after the update work is done.
buffer
- The buffer which is considered the "active" buffer for a subsequent unit of update
work.Runnable
which MUST be executed in a finally
block when the update work
for which the active buffer was set is finished.public boolean isDirty()
true
if any property was touched, else false
.public boolean isAnyIndexFullyDirty()
true
if any index is fully dirty.public void undoActive()
public java.lang.String toString()
toString
in class java.lang.Object
public java.lang.String toString(boolean includeData)
includeData
- true
to include the record's data. If false
, all other state still will be
included.public java.lang.Object[] getData(PropertyMapper pm)
data
array only to
instances of PropertyMapper
. This is needed because the these instances need direct
and fast access the data
and avoid normal status management of a record when the
properties are accessed using the Record
's setters and getters. Instances of
PropertyMapper
are created only when the import is run so that management is not
required nor needed.pm
- The PropertyMapper
which requests access.data
array if the passed parameter is a
PropertyMapper
java.lang.NullPointerException
- If the passed in parameter is null
.public java.lang.Object[] getData(RecordBuffer rb)
data
array only to
instances of RecordBuffer
.rb
- The RecordBuffer
which requests access.data
array if the passed parameter is a
RecordBuffer
java.lang.NullPointerException
- If the passed in parameter is null
.public boolean needsValidation()
true
if record needs validation, else false
.public java.util.BitSet getDirtyIndices(int dirtyOffset, boolean unique, boolean remaining)
dirtyOffset
- Optional offset of a dirty property which must be a component of the indices returned. If
less than 0, all dirty properties are considered. If a non-negative offset is provided,
but the corresponding property is not dirty, an empty bit set is returned.unique
- true
to return dirty unique indices; false
to return dirty non-unique indices.remaining
- If true
, only unvalidated indices are returned. Otherwise, validation is not
considered.public java.lang.Object[] getActiveUpdateDiffs()
null
is returned.
Note: this method will only return a non-null result while a property update is
active; that is, while a DMO property is being updated via the RecordBuffer
invocation handler.
It is only at that moment that the previous value for the property being changed is guaranteed to
contain meaningful data.
public java.util.BitSet getDirtyProps()
protected void setDatum(int offset, java.lang.Object datum)
offset
- Zero-based offset of the datum in the data array.datum
- Value to be set into the data array.protected boolean setAllData(BaseRecord source)
source
- A record from which the data will be copied to this data array.true
if a valid copying could be done; so the properties match in type and extent and
the number of properties match (except the reserved ones).protected java.lang.Object getDatum(int offset)
offset
- Zero-based offset of the datum in the data array.protected abstract RecordMeta _recordMeta()
protected java.lang.String getCodePage(int offset)
clob
object at position offset
.
Note: in order for this method to work, the activeBuffer
must be set in advance. After the
method was invoked, it is recommended to clear its value.
offset
- The position for clob
field.clob
field at position offset
. If a codepage is
not specified or if the field is not a clob
, null
is returned.void indexUpdated(int idxUid)
idxUid
- Index identifier.boolean isAnyIndexFullyDirty(java.util.BitSet[] indices)
indices
- Array of bitsets, where each set bit represents a component of an index.true
if all of the components of any index represented in the array are
dirty, else false
.void markValidated()
void markValidated(int bit)
bit
- 0-based index of bit representing the property which has been validated.java.util.BitSet getUnvalidatedIndices(int dirtyOffset, boolean allUnvalidated)
dirtyOffset
- Optional offset of a dirty property which must be a component of the indices returned. If
less than 0, all dirty properties are considered. If a non-negative offset is provided,
but the corresponding property is not dirty, an empty bit set is returned.allUnvalidated
- If true
, then any unique index containing any property not previously validated is
included in the returned bit set. If false
, then only indices containing properties
that have been explicitly made dirty are included. Setting this to true
effectively
considers all properties with untouched, default values in the check.int getActiveOffset()
The active state may be reset by this method. This should only be done from a Validation
object.
-1
if no offset is
currently being updated.void clearDirtyState(Session session) throws PersistenceException
This method does not turn off the INVALID
flag.
PersistenceException
boolean isPropertyDirty(int offset)
offset
- Zero-based offset of the target property.true
if the property is dirty, else false
.java.util.BitSet getNullProps()
null
.final int readProperty(int propOffset, java.sql.ResultSet rs, int rsOffset) throws PersistenceException
ResultSet
.propOffset
- The index of the property to be read.rs
- The result set to read from.rsOffset
- The index in ResultSet
to read from. If the property is a multiple column
then more than one column will ve read from ResultSet
.ResultSet
.PersistenceException
void version(java.util.concurrent.atomic.AtomicIntegerArray sharedVersion)
sharedVersion
- DMO version information.boolean isStale()
Always returns false
for temp-table records.
true
if this record's version does not match the canonical, shared version of the
record known to the FWD runtime; false
if the versions match or if versioning is
disabled for this record.void commit()
void rollback(int txLevel) throws PersistenceException
txLevel
- The nesting level of sub-transactions of the block being rolled back, where 0 represents
the full transaction block, and a positive number represents that number of nested
sub-transactions.PersistenceException
private java.util.BitSet getDirtyIndices(java.util.BitSet[] indices, int dirtyOffset, java.util.BitSet filter, boolean full)
indices
- The array of bit sets representing the indices to be checked. This must be either the set of
unique indices or non-unique indices associated with the record. Only one or the other can be
checked by a call to this method.dirtyOffset
- Optional offset of a dirty property which must be a component of the indices returned. If
less than 0, all dirty properties are considered. If a non-negative offset is provided,
but the corresponding property is not dirty, an empty bit set is returned.filter
- An inclusive filter to apply to the results. If non-empty, each bit in the filter's bit set
represents the position of an index to be checked in the indices
parameter. If empty,
the returned bit set will be empty.full
- If true
, ALL index components must be dirty for an index to be considered dirty.
If false
, one or more index components must be dirty for an index to be considered
dirty.private void dataChanged(int offset, java.lang.Object datum) throws PersistenceException
offset
- The datum's offset in the record's data array.datum
- The new value of the datum (may be null
).PersistenceException
private void bulkDataChanged(java.util.List<java.lang.Integer> offsets, java.util.List<java.lang.Object> datums) throws PersistenceException
offsets
- A list of offsets for the changed values in record's data array.datums
- A list of new values for the changed datums.PersistenceException
private void updateState(int flags, boolean set)
add
parameter, the
flags can be set (when true
) or removed (when false
. If one flag is already
set and add == true
that flag remains unchanged. The same stands for un-setting a
not active flag.flags
- A set of OR-ed RecordState
flags to be updated.set
- If true
the flags will be set, otherwise they will be unset.RecordState
private int prepareChangeSet(Session session) throws PersistenceException
For a change set to be prepared, the following conditions must be met:
If a change set for the current transaction already exists, another is not created.
A non-negative return value indicates a change set exists and is ready to track changes.
session
- Database session to be interrogated for transaction information.PersistenceException
private boolean lockForUpdate()
true
if the lock is successfully obtained, or if it is unneeded due to system state,
or if there is no active buffer. false
if there was an error obtaining the lock (and
we are in silent error mode).private java.lang.String getTable()