| java.lang.Object simpleorm.core.SRecordInstance
All known Subclasses: simpleorm.examples.Project, simpleorm.simplewebapp.dbute.WRecordInstance, simpleorm.examples.Employee, simpleorm.examples.Department,
SRecordInstance | abstract public class SRecordInstance implements SSerializable(Code) | | Each SRecordInstance represents an individual record in memory
that either correspond to a row in the database or are a new
record that has yet to be inserted.
RecordInstances are created by either
SRecordMeta.findOrCreate findOrCreate or
SResultSet.getRecord . The
former selects a specific record by primary key, while the latter
retrieves a record from a general query result set.
The main methods are get* and set* which
get and set field values of the record.
SRecordInstance.deleteRecord deletes a row.
No two RecordInstances can have the same primary key field values
within the same connection. Attempts to retrieve the same row
twice will simple return a pointer to an existing SRecordInstance.
There is no relationship between RecordInstances in different
connections -- all locking is done by the underlying database.
SRecordsInstances may also be
SRecordInstance.detach ed from a connection
and then subsequently
SRecordInstance.attach ed to another connection.
Optimistic locking is used.
### Serializable, but only naively. Should throw exception if one
attempts to serialize an attached record, or references to purged
records. When deserializing, should match meta data with current
SRecordMeta. The following of links to referenced records should
be manual and should be part of the serialization process and not
the detachment process.
|
Inner Class :public static class BrokenOptimisticLockException extends SException | |
Field Summary | |
byte | bitSets INstance BitSet, one per field instance in fieldValues, in
particular INS_DIRTY which indicates that the field has been
modified. | boolean | deleted Need to delete at commit. | boolean | dirty Is dirty and needs to be flushed. | protected Object | fieldValues The actual field values for this record instance. | boolean | newRow Set after findOrInsert() that does not find the row
already in the database. | Object[] | optimisticFieldValues The copy of the fieldValues used for optimistic locking. | boolean | readOnly True if queried read only. | transient SConnection | sConnection The one connection to which this Instance is glued. | int | updateListIndex -1: not in sConnection.updateList either because 1. | boolean | wasInCache |
Constructor Summary | |
public | SRecordInstance() Should only be called by SimpleORM, but difficult to protect. |
Method Summary | |
public String | allFields() For debugging like toString(), but shows all the fields. | public void | assertNewRow() Throws an excpetion if !
SRecordInstance.isNewRow . | public void | assertNotNewRow() | public SRecordInstance | attach() Attach a detached record to the current transaction. | public Object | clone() Override Object.clone() to make copies of the internal fieldValues array. | public void | deleteRecord() Sets a flag to delete this record when the transaction is
commited. | public void | detach(boolean nullRefs) Detach this instance from the current transactions. | public void | detach() | public void | dirtyPurge() Removes this record from the cache but without flushing it. | public boolean | equals(Object key2) Determines whehter two SRecordInstances are for the same
SRecordMeta and have the same primary key fields.
This is purely for the purpose of putting
SRecordInstances in the
SConnection.transactionCache . | public void | flush() Flush this instance to the database. | public void | flushAndPurge() Flushes this record instance to the database, removes it from
the transaction cache, and then destroys the record so that it
can no longer be used. | public BigDecimal | getBigDecimal(SFieldMeta field) etc. | public boolean | getBoolean(SFieldMeta field) Casts getObject() to double iff a Number, see getString().
Returns false if null. | public byte[] | getBytes(SFieldMeta field) Casts getObject() to byte[]. | public java.sql.Date | getDate(SFieldMeta field) etc. | public double | getDouble(SFieldMeta field) Casts getObject() to double iff a Number, see getString().
Returns 0 if null, following JDBC. | public int | getInt(SFieldMeta field) Casts getObject() to int iff a Number, see getString().
Returns 0 if null, following JDBC. | public long | getLong(SFieldMeta field) Casts getObject() to long iff a Number, see getString().
Returns 0 if null, following JDBC. | abstract public SRecordMeta | getMeta() This must be defined in every user record's definition to access
the SRecord which provides the meta data for this instance. | public Object | getObject(SFieldMeta field) Returns the field's value as a Java Object. | public Object | getObject(SFieldReference field, long sqy_flags) For references only. | public SRecordInstance | getReference(SFieldReference field, long sqy_flags) Gets a record referenced by this.field . | public SRecordInstance | getReference(SFieldReference field) | public Object | getReferenceNoQuery(SFieldReference field) Same as getReference, except that if the referenced record is not
already in the cache then it will simply return Boolean.FALSE.
Particularily useful for detached records where it is not possible
to do such a query.
If the underlying scalar key values are null, then this always
null because there is no record to retrieve. | protected SRecordInstance | getReferenceWhileDetached(SFieldReference reference, Object[] keys) This method is called if the record is detached, and a request is made for
a reference that has not also been detached. | public String | getString(SFieldMeta field) Gets the value of field cast to a String, trimed of trailing
spaces. | public java.sql.Time | getTime(SFieldMeta field) etc. | public java.sql.Timestamp | getTimestamp(SFieldMeta field) etc. | public int | hashCode() See equals() . | void | incompleteDestroy() Destroys this instance so that it can no longer be used. | public boolean | isAttached() True if this instance is attached to the current begun
transaction. | public boolean | isDeleted() | public boolean | isDirty() True iff this record is dirty but not yet flushed to the
database. | public boolean | isDirty(SFieldMeta field) Tests whether just field is dirty. | public boolean | isEmpty(SFieldMeta field) True if field is the empty value. | public boolean | isNewRow() Often called after {SRecordMeta#findOrCreate} to determine
whether a row was retrieved from the database (not a new row) or
whether this record represents a new row that will be inserted
when it is flushed. | public boolean | isNull(SFieldMeta field) | public boolean | isValid() True if the record has valid data, ie. | public boolean | isValid(SFieldMeta field) True if the field has been queried as part of the current
transaction and so a get is valid. | SArrayList | keyFieldMetas(boolean optimistic, Object[] keyMetaValues) Returns the list of fields that are used as part of the
optimistic locking. | public void | nullReferences() Null direct references from this record instance to other
instances. | public void | setBigDecimal(SFieldMeta field, BigDecimal value) | public void | setBoolean(SFieldMeta field, boolean value) | public void | setBytes(SFieldMeta field, byte[] value) | public void | setDate(SFieldMeta field, java.util.Date value) See
SRecordInstance.setTimestamp for discussion of Date parameter. | public void | setDirty() Sets this record to be dirty so that it will be updated in the
database. | public void | setDouble(SFieldMeta field, double value) | public void | setEmpty(SFieldMeta field) Sets field to be empty, ie. | public void | setInt(SFieldMeta field, int value) | public void | setLong(SFieldMeta field, long value) | public void | setNull(SFieldMeta field) | public void | setObject(SFieldMeta field, Object value) Generic routine to set a fields value. | void | setOptimistic(boolean redo) Set this record to be locked Optimistically. | public void | setReference(SFieldReference field, SRecordInstance value) | public void | setString(SFieldMeta field, String value) | public void | setTime(SFieldMeta field, java.util.Date value) See
SRecordInstance.setTimestamp for discussion of Date parameter. | public void | setTimestamp(SFieldMeta field, java.util.Date value) Note that value should normally be a java.sql.Timestamp (a
subclass of java.util.Date). | public String | toString() toString just shows the Key field(s). | String | toStringDefault() Default behavior of toString(). | public void | validateField(SFieldMeta field, Object newValue) The main field validation method, this is specialized for
records that need to perform field level validation. | public void | validateRecord() The main record validation method,
this is specialized for records that need to perform validation.
Throw an SValidationException if not OK.
This is called just before a record would be flushed. | public boolean | wasInCache() Was in the cache before the most recent findOrCreate. |
bitSets | byte bitSets(Code) | | INstance BitSet, one per field instance in fieldValues, in
particular INS_DIRTY which indicates that the field has been
modified. Values are INS_*
|
deleted | boolean deleted(Code) | | Need to delete at commit.
|
dirty | boolean dirty(Code) | | Is dirty and needs to be flushed. But may not be in
sConnection.updateList because the instance is detached.
|
fieldValues | protected Object fieldValues(Code) | | The actual field values for this record instance. Null if
destroyed. Elements null if retrieved data SQL Null.
|
newRow | boolean newRow(Code) | | Set after findOrInsert() that does not find the row
already in the database.
|
optimisticFieldValues | Object[] optimisticFieldValues(Code) | | The copy of the fieldValues used for optimistic locking. null
means not optimistically locked.
|
readOnly | boolean readOnly(Code) | | True if queried read only. Ie. either selected FOR UPDATE,
optimistically locked, or new and not read only.
|
sConnection | transient SConnection sConnection(Code) | | The one connection to which this Instance is glued. Instances
may never be shared across connections.
|
updateListIndex | int updateListIndex(Code) | | -1: not in sConnection.updateList either because 1. not dirty or
2. not attached.
|
SRecordInstance | public SRecordInstance()(Code) | | Should only be called by SimpleORM, but difficult to protect.
|
allFields | public String allFields()(Code) | | For debugging like toString(), but shows all the fields.
|
attach | public SRecordInstance attach()(Code) | | Attach a detached record to the current transaction. A record
with the same key must not already exist in this transaction,
although this restriction may be relaxed later. Recursively
attaches any referenced records.
Currently, the returned value is just this. However, a
future version may allow the unattached record to be merged with
an existing record, so one should always assume that the record
may change identity during attachment.
Attaching the same record twice is OK.
If the record has a generated primary key then the key is generated
as part of the attachement processing.
|
clone | public Object clone()(Code) | | Override Object.clone() to make copies of the internal fieldValues array.
This method may only be used while the object is detached.
|
deleteRecord | public void deleteRecord()(Code) | | Sets a flag to delete this record when the transaction is
commited. The record is not removed from the
transaction cache. Any future findOrCreate will
thus return the deleted record but its fields may not be
referenced. (isDeleted can be used to determine
that the record has been deleted.)
The record is only deleted from the database when the
transaction is committed or is flushed. Thus a transaction that
nulls out references to a parent record and then deletes the
parent record will not cause a referential integrity violation
because the update order is preserved so that the updates will
(normally) be performed before the deletes.
Note that for Optimistic locks only those fields that were
retrieved are checked for locks.
|
detach | public void detach(boolean nullRefs)(Code) | | Detach this instance from the current transactions. Sets
optimistic locking if the record was updatable. ,p>
If a future query in the same transaction retrieves a record
with the same key then it will be a different instance.
Ie. this really does detach the record from the transaction.
Nulls any references to records that have not already been
detached so that they will not be serialized. The keys are kept
so that the references can be reconstructed later. (This does
NOT recursively detach any referenced records -- you have to
explicitly detach all the records that you need.)
Set nullRefs to false to break instance indirect circular
references, eg. if an Employee e manages someone that manages e. Very
rarely needed. It is OK to detach a record twice.
|
detach | public void detach()(Code) | | detach(true)
|
dirtyPurge | public void dirtyPurge()(Code) | | Removes this record from the cache but without flushing it. Any
changes to it will be lost. This might be useful if the record is
being manually updated/deleted and you want to deliberately ignore
any direct changes.
Dangerous, use with care.
See Also: SRecordInstance.flushAndPurge |
equals | public boolean equals(Object key2)(Code) | | Determines whehter two SRecordInstances are for the same
SRecordMeta and have the same primary key fields.
This is purely for the purpose of putting
SRecordInstances in the
SConnection.transactionCache . This is done as
tc.put(SRecordInstance, SRecordInstance) , ie the
instance is used for both the key and the body. This avoids
having to create a separate primary key object for each record
instance.
It only foreign keys, not referenced SRecordInstances which may
be null if the parent record has not been retrieved.
|
flush | public void flush()(Code) | | Flush this instance to the database. Normally called by
SConnection.flush() in reponse to
commit but can also be called expicitly if the
update order needs to be modified. This method does nothing
unless the record is dirty.
## This should really utilize the new batching techniques if the
JDBC driver supports them. This would substantially minimize
the nr of round trips to the server.
See Also: SConnection.flush |
flushAndPurge | public void flushAndPurge()(Code) | | Flushes this record instance to the database, removes it from
the transaction cache, and then destroys the record so that it
can no longer be used. Any future findOrCreate
will requery the data base and produce a new record.
This is useful if one wants to do raw JDBC updates on the
record, and be guaranteed not to have an inconsistent cache.
(Where bulk updates can be used they are several times faster
than updates made via the JVM -- see the benchmarks section in
the white paper.)
Problems with the update order can generally be avoided by first
flushing the entire connection.
See Also: SRecordMeta.flushAndPurge See Also: SConnection.flushAndPurge See Also: SConnection.flush |
getBoolean | public boolean getBoolean(SFieldMeta field)(Code) | | Casts getObject() to double iff a Number, see getString().
Returns false if null.
|
getBytes | public byte[] getBytes(SFieldMeta field)(Code) | | Casts getObject() to byte[].
|
getDouble | public double getDouble(SFieldMeta field)(Code) | | Casts getObject() to double iff a Number, see getString().
Returns 0 if null, following JDBC.
|
getInt | public int getInt(SFieldMeta field)(Code) | | Casts getObject() to int iff a Number, see getString().
Returns 0 if null, following JDBC.
|
getLong | public long getLong(SFieldMeta field)(Code) | | Casts getObject() to long iff a Number, see getString().
Returns 0 if null, following JDBC. Note that longs may not be
accurately supported by the database -- see SFieldLong.
|
getMeta | abstract public SRecordMeta getMeta()(Code) | | This must be defined in every user record's definition to access
the SRecord which provides the meta data for this instance. It
is normally defined as:-
SRecord getMeta() { return meta; };
The actual meta variable is thus not Serialized,
but it would not be anyway as it is usually static.
|
getObject | public Object getObject(SFieldMeta field)(Code) | | Returns the field's value as a Java Object. Methods such as
getString() dispach to this method but are
generally more convenient because the cast the result to the
correct type. This method in turn just dispaches to
field.getFieldValue() , ie. it is the declaredtype
of the SField that determines how the value is retrieved.
|
getReferenceNoQuery | public Object getReferenceNoQuery(SFieldReference field)(Code) | | Same as getReference, except that if the referenced record is not
already in the cache then it will simply return Boolean.FALSE.
Particularily useful for detached records where it is not possible
to do such a query.
If the underlying scalar key values are null, then this always
null because there is no record to retrieve. It is only for a
non-null, non-retrieved references that this returns FALSE.
|
getReferenceWhileDetached | protected SRecordInstance getReferenceWhileDetached(SFieldReference reference, Object[] keys)(Code) | | This method is called if the record is detached, and a request is made for
a reference that has not also been detached. This method gives you one last
chance to return the referenced record before an exception is thrown.
By default, this method just returns null to indicate that no last-ditch effort
is made to fetch missing references, so you must override it to do
something more useful (i.e. fetch the reference from the database)
Warning: Note that because this record and the fetched reference are obtained
in separate transactions, any consistency constraints between the two objects
might not hold.
Parameters: reference - The reference that is being followed Parameters: keys - Object[] keys for the reference, just as you would pass themto THE REFERENCED record's findOrCreate() SRecordInstance Return the fetched record, or null, if no attempt wasmade. NOTE: currently, this does not cover the awkward case where an attempt ISmade, but the value returned is NULL, anyway. |
getString | public String getString(SFieldMeta field)(Code) | | Gets the value of field cast to a String, trimed of trailing
spaces. This is equivalent to
getObject().toString().trimTrailingSpaces() . So
the field type itself need not be SFieldString , but
just something that can be cast to a String. Trailing spaces
can be a problem with some databases and fileds declared
CHAR .
Note that if you do not want trailing spaces trimmed, then
just call getObject().toString() manually.
(For CHAR fields, most dbs/jdbc drivers seem to trim, but this is
highly inconsistent.)
## Trimming is an issue with CHAR style fields that pad with spaces.
Currently we always read from database into the fields without trimming.
The idea being to let the SimpleORM user get at the raw query result using
getObject, whatever that raw result is.
Most DBs seem to trim for us. But some may not, and some may require the
trailing spaces on queries. Certainly trailing spaces in the objects will
upset the record cache, and there is some dubious code in SRecordFinder.retrieveRecord()
to handle this.
I think that for CHAR fields we need to always trim at database read, and pad where
needed. This should also be dispatched to DB handler. I think that Oracle gives grief.
(Note that trim means trailing spaces, leading should be left alone.)
I have not done this because it would require testing on many datbases.
|
hashCode | public int hashCode()(Code) | | See equals() .
|
incompleteDestroy | void incompleteDestroy()(Code) | | Destroys this instance so that it can no longer be used. Also
nulls out variables so to reduce risk of memory leaks. Note
that it does not remove the record from the transaction cache
and update list -- it cannot be called on its
own.
|
isAttached | public boolean isAttached()(Code) | | True if this instance is attached to the current begun
transaction. Exception if is attached but not to the current
transaction or the current transaction has not begun.
|
isDeleted | public boolean isDeleted()(Code) | | |
isDirty | public boolean isDirty()(Code) | | True iff this record is dirty but not yet flushed to the
database. May be both dirty and unattached.
A record is not dirty if a record has been flushed to the
database but the transaction not committed.
## Should add wasEverDirty method for both record and
fields for validation tests.
|
isDirty | public boolean isDirty(SFieldMeta field)(Code) | | Tests whether just field is dirty.
|
isEmpty | public boolean isEmpty(SFieldMeta field)(Code) | | True if field is the empty value. Currently just tests
getObject() == null . But other options will be added
later to allow "" to be treated as null.
See Also: SRecordInstance.SMANDATORY |
isNewRow | public boolean isNewRow()(Code) | | Often called after {SRecordMeta#findOrCreate} to determine
whether a row was retrieved from the database (not a new row) or
whether this record represents a new row that will be inserted
when it is flushed.
|
isValid | public boolean isValid()(Code) | | True if the record has valid data, ie. it has not been
destroyed. (This has nothing to do with validateRecord.)
|
isValid | public boolean isValid(SFieldMeta field)(Code) | | True if the field has been queried as part of the current
transaction and so a get is valid. Use this to guard
validations if partial column queries are performed. See
isDirty.
|
keyFieldMetas | SArrayList keyFieldMetas(boolean optimistic, Object[] keyMetaValues)(Code) | | Returns the list of fields that are used as part of the
optimistic locking. Includes Primary key fields, any valid
fields that are dirty, but not references.
|
nullReferences | public void nullReferences()(Code) | | Null direct references from this record instance to other
instances. Leaves the actual scalar referencing key fields
alone. May only be called on a detached object. This method was
created for two reasons:
- Reduce communication burden when sending detached records from a client back
to the server for processing
- Avoid problems when recursively attaching referenced records that we really did not want.
attach() currently does not allow multiple attach'es of same record. Even though this
reason may evenutually go away, the first reason will remain.
|
setDirty | public void setDirty()(Code) | | Sets this record to be dirty so that it will be updated in the
database. Normally done implicitly by setting a specific column.
But may occasionally be useful after a findOrInsert() to add a
record that contains nothing appart from its primary key.
|
setObject | public void setObject(SFieldMeta field, Object value)(Code) | | Generic routine to set a fields value. Just dispaches to
field.setFieldValue()
|
setOptimistic | void setOptimistic(boolean redo)(Code) | | Set this record to be locked Optimistically. Assert that it is
not dirty and copy the current value of all relevant fields.
These can be compared with values stored in the database at
flush time.
The copy is cheap because it is only copying pointers. However,
if the optimisitic lock fails then an exception will be thrown.
Mainly used for long lived transactions.
|
setTimestamp | public void setTimestamp(SFieldMeta field, java.util.Date value)(Code) | | Note that value should normally be a java.sql.Timestamp (a
subclass of java.util.Date). However, if it is a java.util.Date
instead, then it will replaced by a new java.sql.Timestamp
object before being set. This is convenient for people using
java.util.Date as their main date type.
|
toString | public String toString()(Code) | | toString just shows the Key field(s). It is meant to be
consise, often used as part of longer messages.
|
toStringDefault | String toStringDefault()(Code) | | Default behavior of toString(). This was split out from the toString() method
to avoid infinite recursion if a subclass of SRecordInstance overrode toString()
to use any of the get...() methods.
|
validateField | public void validateField(SFieldMeta field, Object newValue)(Code) | | The main field validation method, this is specialized for
records that need to perform field level validation.
It is called each time a field is set a value, (now) including keys.
Throw an SValidationException if not OK. The value is not assigned, and
the transaction can continue.
This is called after the value has been converted to its proper
type, eg. from a String to a Double. (Which is why it is not a
good place to also do conversions.)
This is called for key values as well.
This is only for newly created records but is during the findOrCreate
-- ie it is called even if the record is never made dirty and thus saved.
See ADemo and ValidationTest for examples.
|
validateRecord | public void validateRecord()(Code) | | The main record validation method,
this is specialized for records that need to perform validation.
Throw an SValidationException if not OK.
This is called just before a record would be flushed. Only
dirty records are validated. If the validation fails then the
record is not flushed. (It may also be called directly by the
application to validate the record before it is flushed.)
Use this routine when a record may be in a temporarily invalid
state, but which must be corrected before flushing. This is
common when there is a more complex relationship between
different fields that cannot be validated until all the fields
have been assigned values.
If an exception is thrown then the condition will need to be
corrected or the transaction will need to be rolled back.
See ADemo for an example.
|
wasInCache | public boolean wasInCache()(Code) | | Was in the cache before the most recent findOrCreate.
(Will always been in the cache after a findOrCreate.)
Used to prevent two create()s for the same key.
Also for unit tests.
|
|
|