public final class Repository extends java.lang.Object implements AppContextCleanup
Repository
encapsulates a Database table.
This class is used to hold the
Connection (via the AppContext
) and to cache information about the table
including the name and meta-data. Actual rows are stored in Record
objects
which are inner classes of Repository
to allow them to access the cached
information.
Each record is assumed to have a unique integer identifier that can be used to reference the
appropriate table record.
The Repository.FieldInfo
class records information about each field in the table. This class attempts to capture
where a field is used to reference the integer identifier of a different table. This can be determined automatically
where there is a foreign key constraint or can be specified using properties. A property
reference.repository-tag.DBfield defines the name of the table the field references.
If a handler class is registered for the remote table this is used to create a TypeProducer
for the field.
Alternatively a reference can be registered explicitly with the Repository by registering a TypeProducer
for the field.
This will reduce overhead where an instance of the remote handler class is already available.
A boolean property of the form truncate.repository-tag.DBfield can be used to request that field values be truncated to the database field length when being stored.
At the moment the integer identifier is implemented by requiring each
tables to have a primary key consisting of a mysql
auto_increment field if the is more than one auto_increment field the first
is taken. This dependency on mysql specific features is encapsulated within
the MysqlRepository
class and could be replaced by a different class.
Alternative DB back-ends can be less flexible than mysql when it comes to table and field names
so SQL statements should be constructed using the addUniqueName addTable and FieldInfo.addName
method calls as this allows the Repository class to perform name mangling via the following properties:
getParamTag()
method returns a String to be used in looking up configuration parameters for the
enclosing object. This defaults to tag but can be overridden by setting the config.tag property.
If the table_alias.tag<\b> property is set this is used as an alias string when constructing SQL statements. This is needed because the join filter classes assume a single reference between tables. If you have multiple fields that reference the same table you can register the same table under different tags with different aliases allowing multiple joins to the same table.
New Repository
objects are obtained using a static method.
Repositories created in this way are cached in the
AppContext c;
Repository rep = Repository.getInstance(c,tag_name);
AppContext
so only a single Repository
for each tag is ever created per AppContext
. The tag is normally the same as the database table
and should match the tag used to create the corresponding DataObjectFactory
from the AppContext. However the tag should not
be used as a table name in SQL as it is possible
for table renaming and name-mangling to occur.
Repository can implement a cache of Record data based on id. This is enabled on a table by table basis via the
cache.tag-name property.
This is only intended to cache values within the lifetime of an AppContext
(ie during a single request).
The cache is populated
whenever SetContents
is called with the record ejected whenever put
is called.
This is to ensure the cache never contains a dirty record.
The cache should also be flushed if the table is modified directly via an SQL update, though this is only an issue if a single request re-fetches records after a SQL update.
The setID
checks for cached data and populates itself by copying the cached record instead of fetching a database record.
The cache only holds non-dirty data so could hold normal Map objects rather than actual Records.
Currently this is left as a future optimisation.
Note that lookups via a TypeProducer
may also utilise this caching mechanism.
Repositories are intended to be local to a parent AppContext which should represent a single thread of execution. However synchronisation is implemented within the code. The cost of this should be quite low and it also seems to prevent optimisation bugs in some JVMs
The Repository class implements some additional automatic type conversions beyond those implemented in JDBC.
BACKUP_SUFFIX_ATTR
is stored in the AppContext
then this name will be used as a table name suffix to create backup tables where deleted records will be
copied before being deleted in the main table. This is intended to allow
old data to be backed up as part of a purge of data from a live table.Modifier and Type | Class and Description |
---|---|
class |
Repository.FieldInfo
Information about fields
|
static class |
Repository.IdMode
modes supported by
Repository.Record.setID(int) |
class |
Repository.IndexInfo
information about indexes
|
class |
Repository.Order
Class representing an Order clause
|
class |
Repository.Record
Record encapsulates a Database record.
|
Modifier and Type | Field and Description |
---|---|
static java.lang.String |
BACKUP_SUFFIX_ATTR |
static Feature |
BACKUP_WITH_SELECT |
static java.lang.String |
CACHE_FEATURE_PREFIX |
static Feature |
CHECK_INDEX |
protected static java.lang.String |
CONFIG_TAG_PREFIX |
static Feature |
READ_ONLY_FEATURE |
static java.lang.String |
REFERENCE_PREFIX |
static Feature |
REQUIRE_ID_KEY |
protected boolean |
use_id
can we use the unique id code for this table
|
Modifier | Constructor and Description |
---|---|
protected |
Repository(AppContext c,
java.lang.String tag)
Create a Repository object private to force the use of the Factory
method.
|
Modifier and Type | Method and Description |
---|---|
java.lang.StringBuilder |
addAlias(java.lang.StringBuilder sb,
boolean quote)
Add the table alias
|
java.lang.StringBuilder |
addSource(java.lang.StringBuilder sb,
boolean quote)
Add the table as a query source.
|
java.lang.StringBuilder |
addTable(java.lang.StringBuilder sb,
boolean quote)
Add the table name to a query.
|
void |
addTypeProducer(TypeProducer producer)
Cache a
TypeProducer in the repository. |
java.lang.StringBuilder |
addUniqueName(java.lang.StringBuilder sb,
boolean qualify,
boolean quote)
Add the primary key name of this table to a query
|
void |
cleanup()
AppContext is being closed. |
protected void |
clearCache()
clear the Record cache.
|
protected void |
clearFields() |
<T> T |
convert(java.lang.Class<? extends T> target,
java.lang.Object value) |
java.lang.Object |
convert(java.lang.String key,
java.lang.Object value)
Performs automatic type conversions to the canonical type as specified by
the database table.
|
java.lang.Boolean |
convertBoolean(java.lang.Object o,
java.lang.Boolean def) |
java.util.Date |
convertDate(java.lang.Object o)
perform any conversions to Date supported by this Repository.
|
java.lang.Number |
convertNumber(java.lang.Object o)
Perform any object conversions to Number appropriate to thisRepository.
|
void |
createBackupTable(java.lang.String name) |
java.lang.String |
dbFieldtoTag(java.lang.String name)
Map the actual name of the DB field to the tag used in the code.
|
boolean |
equals(java.lang.Object obj) |
void |
flushCache() |
static void |
flushCaches(AppContext c) |
Repository |
getBackup()
create a backup table structured like this one if backups are enabled.
|
<X extends DataObject> |
getBooleanExpression(java.lang.Class<X> filter_type,
java.lang.String key)
get a
BooleanFieldExpression for a field |
AppContext |
getContext()
get the AppContext associated with this Repository.
|
<X extends DataObject> |
getDateExpression(java.lang.Class<X> target,
java.lang.String key)
get a
Date valued FieldValue for a field. |
java.lang.String |
getDBTag()
Get the tag used to retrieve the correct database connection for this table from a
DatabaseService |
java.util.Set<java.lang.String> |
getFields()
get the list of Record fields for this table in canonical order.
|
static java.lang.String |
getForeignKeyDescriptor(AppContext c,
java.lang.String tag,
boolean quote)
Get a foreign key descriptor by tag.
|
Repository.IndexInfo |
getIndexInfo(java.lang.String name) |
java.util.Set<java.lang.String> |
getIndexNames() |
java.util.Collection<Repository.FieldInfo> |
getInfo()
Get all the FieldInfo objects for this Repository
|
Repository.FieldInfo |
getInfo(java.lang.String key)
get the FieldInfo object for a field null key always returns a null
result
|
<T extends java.lang.Number,X extends DataObject> |
getNumberExpression(java.lang.Class<X> filter_type,
java.lang.Class<T> target,
java.lang.String key)
get a
NumberFieldExpression for a field. |
Repository.Order |
getOrder(java.lang.String key,
boolean desc)
get a
Repository.Order element corresponding to a key. |
java.lang.String |
getParamTag()
get the tag used to qualify configuration parameters.
|
<T extends DataObject> |
getReferenceExpression(java.lang.Class<T> self,
java.lang.String key)
Get a
IndexedFieldValue for a reference field |
<T extends DataObject,I extends DataObject> |
getReferenceExpression(java.lang.Class<T> self,
java.lang.String key,
IndexedProducer<I> prod)
Get a
IndexedFieldValue for a field
The field does not have to be tagged as a reference field |
long |
getResolution()
Return the number of milliseconds per tick to use when using an integer type to
specify a date or vice versa.
|
IndexedTypeProducer |
getSelfProducer() |
SQLContext |
getSQLContext() |
<X extends DataObject> |
getStringExpression(java.lang.Class<X> filter_type,
java.lang.String key)
get a
StringFieldExpression for a field |
java.lang.String |
getTable() |
java.lang.String |
getTag()
Get the Tag string used to reference this Repository
|
<T extends DataObject,O,D> |
getTypeProducerExpression(TypeProducer<O,D> prod) |
protected java.lang.String |
getUniqueIdName()
get the field name of the primary key
|
protected java.lang.String |
getUniqueIdName(boolean qualify) |
boolean |
hasField(Repository.FieldInfo info) |
boolean |
hasField(java.lang.String key)
Is the specified object a valid Field key for this table.
|
int |
hashCode() |
boolean |
hasIndex(java.lang.String index)
Is there a named index of the specified name for this table.
|
boolean |
hasTypeProducer(java.lang.String field) |
protected int |
insert(Repository.Record r)
Default insert operation that uses Generated Keys to
obtain the unique id.
|
boolean |
isUniqueIdName(java.lang.String name)
Is this the primary key field.
|
static <T> T |
makeTargetObject(java.lang.Class<T> target,
java.sql.ResultSet rs,
int pos)
Static method to result a field from a result set
with a specified target class
|
static void |
reset(AppContext c,
java.lang.String tag)
Clear the cached repository
|
boolean |
setAllowBogusPut(boolean f)
control if it is an error to set a property with no corresponding
database field.
|
boolean |
setAllowNull(boolean f)
control if it is an error to set a property with a null value
|
void |
setFromResultSet(Repository.Record r,
java.sql.ResultSet rs,
boolean qualify)
populate an object from a ResultSet
It seems to work if we always qualify the field names but its slower
|
void |
setResolution(long resolution) |
static java.lang.String |
TableToTag(AppContext c,
java.lang.String tag) |
static java.lang.String |
tagToTable(AppContext ctx,
java.lang.String tag)
Map a tag name to the actual database table.
|
java.lang.String |
toString() |
static java.lang.String |
typeName(int type) |
boolean |
useID()
Does this repository support id fields.
|
boolean |
usesAlias() |
boolean |
usesCache() |
protected static final java.lang.String CONFIG_TAG_PREFIX
public static final java.lang.String BACKUP_SUFFIX_ATTR
public static final java.lang.String REFERENCE_PREFIX
public static final java.lang.String CACHE_FEATURE_PREFIX
public static final Feature REQUIRE_ID_KEY
public static final Feature CHECK_INDEX
public static final Feature READ_ONLY_FEATURE
public static final Feature BACKUP_WITH_SELECT
protected final boolean use_id
protected Repository(AppContext c, java.lang.String tag) throws java.sql.SQLException
c
- AppContexttag
- String tag used to find repository.java.sql.SQLException
public void createBackupTable(java.lang.String name) throws java.sql.SQLException
java.sql.SQLException
public Repository getBackup() throws DataFault
DataFault
public static java.lang.String TableToTag(AppContext c, java.lang.String tag)
public java.lang.StringBuilder addUniqueName(java.lang.StringBuilder sb, boolean qualify, boolean quote)
sb
- StringBuilderqualify
- boolean should we qualify with table namequote
- boolean request quoting if supportedpublic java.lang.StringBuilder addTable(java.lang.StringBuilder sb, boolean quote)
addSource(StringBuilder, boolean)
in preference
for select/update statements as qualified field names will use the aliassb
- StringBuilderquote
- request quoting if supportedaddSource(StringBuilder, boolean)
public java.lang.StringBuilder addAlias(java.lang.StringBuilder sb, boolean quote)
sb
- quote
- StringBuilder
public java.lang.StringBuilder addSource(java.lang.StringBuilder sb, boolean quote)
addTable(StringBuilder, boolean)
but is allowed to add an "AS alias" clause as wellsb
- quote
- StringBuilder
public java.lang.String getTable()
public IndexedTypeProducer getSelfProducer()
public java.lang.Object convert(java.lang.String key, java.lang.Object value)
key
- Database field to base conversion onvalue
- input objectpublic <T> T convert(java.lang.Class<? extends T> target, java.lang.Object value)
protected void clearCache()
protected void clearFields()
public final java.util.Date convertDate(java.lang.Object o)
o
- Input Objectpublic final java.lang.Number convertNumber(java.lang.Object o)
o
- input Objectpublic final java.lang.Boolean convertBoolean(java.lang.Object o, java.lang.Boolean def)
public void addTypeProducer(TypeProducer producer)
TypeProducer
in the repository.
This allows the basic database meta-data to be augmented by
run-time information.producer
- public boolean hasTypeProducer(java.lang.String field)
public AppContext getContext()
public final void flushCache()
public java.util.Set<java.lang.String> getFields()
public boolean hasField(Repository.FieldInfo info)
public java.util.Set<java.lang.String> getIndexNames()
public Repository.IndexInfo getIndexInfo(java.lang.String name)
public java.util.Collection<Repository.FieldInfo> getInfo()
public Repository.FieldInfo getInfo(java.lang.String key)
key
- public Repository.Order getOrder(java.lang.String key, boolean desc)
Repository.Order
element corresponding to a key.key
- field to order by (null for primary key).desc
- Repository.Order
public final SQLContext getSQLContext()
public long getResolution()
public java.lang.String getTag()
public java.lang.String getParamTag()
public final java.lang.String getDBTag()
DatabaseService
protected java.lang.String getUniqueIdName()
public boolean isUniqueIdName(java.lang.String name)
name
- protected java.lang.String getUniqueIdName(boolean qualify)
public boolean hasField(java.lang.String key)
key
- public boolean hasIndex(java.lang.String index)
index
- public <T extends java.lang.Number,X extends DataObject> NumberFieldExpression<T,X> getNumberExpression(java.lang.Class<X> filter_type, java.lang.Class<T> target, java.lang.String key)
NumberFieldExpression
for a field.filter_type
- type of the hosting objecttarget
- desired numeric type for the field.key
- field nameNumberFieldExpression
public <X extends DataObject> BooleanFieldExpression<X> getBooleanExpression(java.lang.Class<X> filter_type, java.lang.String key)
BooleanFieldExpression
for a fieldfilter_type
- type of hosting objectkey
- field nameBooleanFieldExpression
public <X extends DataObject> StringFieldExpression<X> getStringExpression(java.lang.Class<X> filter_type, java.lang.String key)
StringFieldExpression
for a fieldfilter_type
- type of hosting objectkey
- field nameStringFieldExpression
public <X extends DataObject> FieldValue<java.util.Date,X> getDateExpression(java.lang.Class<X> target, java.lang.String key)
Date
valued FieldValue
for a field.
The underlying database field may be a time-stamp or a numeric field.target
- type of hosting objectkey
- field nameFieldValue
public <T extends DataObject> IndexedFieldValue getReferenceExpression(java.lang.Class<T> self, java.lang.String key)
IndexedFieldValue
for a reference fieldself
- type of owning objectkey
- public <T extends DataObject,O,D> TypeProducerFieldValue<T,O,D> getTypeProducerExpression(TypeProducer<O,D> prod)
public <T extends DataObject,I extends DataObject> IndexedFieldValue<T,I> getReferenceExpression(java.lang.Class<T> self, java.lang.String key, IndexedProducer<I> prod)
IndexedFieldValue
for a field
The field does not have to be tagged as a reference fieldself
- key
- prod
- protected int insert(Repository.Record r) throws DataFault
r
- DataFault
public boolean setAllowBogusPut(boolean f)
f
- booleanpublic boolean setAllowNull(boolean f)
f
- booleanpublic void setFromResultSet(Repository.Record r, java.sql.ResultSet rs, boolean qualify) throws DataFault, DataNotFoundException
r
- rs
- ResultSetqualify
- boolean qualify the field names with the table name as
ResultSet is from a joinDataFault
DataNotFoundException
ConsistencyError
public java.lang.String dbFieldtoTag(java.lang.String name)
public static java.lang.String tagToTable(AppContext ctx, java.lang.String tag)
ctx
- tag
- public void setResolution(long resolution)
resolution
- the resolution to setpublic boolean useID()
public boolean usesCache()
public boolean usesAlias()
public static <T> T makeTargetObject(java.lang.Class<T> target, java.sql.ResultSet rs, int pos) throws java.sql.SQLException
T
- target
- rs
- pos
- java.sql.SQLException
public static java.lang.String getForeignKeyDescriptor(AppContext c, java.lang.String tag, boolean quote)
c
- AppContexttag
- Stringquote
- public static void flushCaches(AppContext c)
public static void reset(AppContext c, java.lang.String tag)
c
- tag
- public int hashCode()
hashCode
in class java.lang.Object
public boolean equals(java.lang.Object obj)
equals
in class java.lang.Object
public java.lang.String toString()
toString
in class java.lang.Object
public void cleanup()
AppContextCleanup
AppContext
is being closed.
Only use this for cleanup that can't be handled by
normal garbage collection or for state which is never returned by reference.cleanup
in interface AppContextCleanup
public static java.lang.String typeName(int type)