public abstract class Db<T extends Db>
extends java.lang.Object
The goal of the Db abstraction is to allow Actions like RestGet/Put/Post/Patch/DeleteAction to apply the same REST CRUD operations agnostically across multiple backend data storage engines.
The primary job of a Db subclass is to:
#doStartup(Api).
select(Collection, Map), upsert(Collection, List), delete(Collection, List).
Actions such as DbGetAction then:
| Modifier and Type | Field and Description |
|---|---|
protected boolean |
bootstrap
Indicates that this Db should reflectively create and configure Collections to represent its underlying tables.
|
protected java.util.ArrayList<Collection> |
collections
The Collections that are the REST interface to the backend tables (or buckets, folders, containers etc.) this Db exposes through an Api.
|
protected boolean |
dryRun
When set to true the Db will do everything it can to "work offline" logging commands it would have run but not actually running them.
|
protected Path |
endpointPath
Used to differentiate which Collection is being referred by a Request when an Api supports Collections with the same name from different Dbs.
|
protected java.util.Set<java.lang.String> |
excludeColumns
OPTIONAL column names that should be excluded from RQL queries, upserts and patches.
|
protected java.util.Set<java.lang.String> |
includeColumns
OPTIONAL column names that should be included in RQL queries, upserts and patches.
|
protected java.util.Map<java.lang.String,java.lang.String> |
includeTables
A tableName to collectionName map that can be used by whitelist backend tables that should be included in reflicitive Collection creation.
|
protected org.slf4j.Logger |
log |
protected java.lang.String |
name
The name of his Db used for "name.property" style autowiring.
|
protected static java.util.Set<java.lang.String> |
reservedParams
These params are specifically NOT passed to the Query for parsing.
|
protected java.lang.String |
type
A property that can be used to disambiguate different backends supported by a single subclass.
|
| Modifier and Type | Method and Description |
|---|---|
protected java.lang.String |
beautifyCollectionName(java.lang.String tableName)
Attempts to camelCase and pluralize the table name to make it an attractive REST collection name.
|
protected java.lang.String |
beautifyName(java.lang.String name)
Try to make an attractive camelCase valid javascript variable name.
|
protected void |
buildCollections()
Creates a collection for every table name in
includeTables giving the Collections and Properties beautified JSON names. |
protected void |
buildRelationships()
Creates ONE_TO_MANY, MANY_TO_ONE, and MANY_TO_MANY Relationships with attractive RESTish names
based on the primary Index and foreign key Index structures of the Collections.
|
java.lang.Object |
castDbOutput(Property property,
java.lang.Object value)
Casts value as Property.type
|
java.lang.Object |
castJsonInput(Property property,
java.lang.Object value)
Casts value as Property.type.
|
java.lang.Object |
castJsonInput(java.lang.String type,
java.lang.Object value)
Casts value to as type.
|
protected void |
configApi(Api api)
Adds all non excluded Collections to the Api via Api.withCollection
|
protected void |
configDb()
Subclasses should reflectively create Collections and Properties, Indexes, and Relationships here.
|
abstract void |
delete(Collection collection,
java.util.List<java.util.Map<java.lang.String,java.lang.Object>> indexValues)
Deletes rows identified by the unique index values from the underlying data source.
|
void |
doPatch(Collection collection,
java.util.List<java.util.Map<java.lang.String,java.lang.Object>> rows) |
abstract Results |
doSelect(Collection collection,
java.util.List<Term> queryTerms)
Finds all records that match the supplied RQL query terms.
|
protected void |
doShutdown() |
protected void |
doShutdown(Api api)
Made to be overridden by subclasses or anonymous inner classes to do specific cleanup
|
protected void |
doStartup(Api api)
Made to be overridden by subclasses or anonymous inner classes to do specific init of an Api.
|
abstract java.util.List<java.lang.String> |
doUpsert(Collection collection,
java.util.List<java.util.Map<java.lang.String,java.lang.Object>> records)
Upserts the key/values pairs for each record into the underlying data source.
|
boolean |
filterOutJsonProperty(Collection collection,
java.lang.String name)
Checks if "collectionName.columnName" or just "columnName" is specifically included or excluded via
includeColumns
excludeColumns or is a valid Property columnName. |
Collection |
getCollectionByTableName(java.lang.String tableName) |
java.util.List<Collection> |
getCollections() |
Path |
getEndpointPath() |
java.lang.String |
getHref(java.lang.Object hrefOrNode) |
java.util.Map<java.lang.String,java.lang.Object> |
getKey(Collection collection,
java.lang.Object node) |
java.lang.String |
getName() |
protected Property |
getProperty(java.lang.String tableName,
java.lang.String columnName) |
java.lang.String |
getType() |
boolean |
isBootstrap() |
boolean |
isDryRun() |
boolean |
isRunning(Api api) |
boolean |
isType(java.lang.String... types) |
protected java.lang.String |
makeRelationshipName(Collection collection,
Relationship relationship)
Attempts to construct a sensible json property name for a Relationship.
|
java.util.Map<java.lang.String,java.lang.Object> |
mapTo(java.util.Map<java.lang.String,java.lang.Object> srcRow,
Index srcCols,
Index destCols) |
protected java.util.Set<Term> |
mapToColumnNames(Collection collection,
Term term) |
protected void |
mapToJsonNames(Collection collection,
Term term) |
java.util.List<java.lang.String> |
patch(Collection collection,
java.util.List<java.util.Map<java.lang.String,java.lang.Object>> records)
Should be called by Actions instead of upsert() only when all records are strictly known to exist.
|
void |
removeCollection(Collection table) |
Results |
select(Collection collection,
java.util.Map<java.lang.String,java.lang.String> params)
Finds all records that match the supplied RQL query terms.
|
T |
shutdown()
Shutsdown all running Apis.
|
T |
shutdown(Api api) |
T |
startup(Api api)
Called by an Api to as part of Api.startup().
|
java.util.List<java.lang.String> |
upsert(Collection collection,
java.util.List<java.util.Map<java.lang.String,java.lang.Object>> rows) |
T |
withBootstrap(boolean bootstrap) |
T |
withCollection(Collection collection) |
T |
withCollections(Collection... collections) |
T |
withDryRun(boolean dryRun) |
T |
withEndpointPath(Path endpointPath) |
T |
withExcludeColumns(java.lang.String... columnNames) |
T |
withIncludeColumns(java.lang.String... columnNames) |
T |
withIncludeTable(java.lang.String tableName,
java.lang.String collectionName)
Whitelists tableName as an underlying table that should be reflectively bootstrapped and exposed as collectionName.
|
T |
withIncludeTables(java.lang.String includeTables)
Utility that parses a comma and pipe separated list of table name to collection name mappings.
|
T |
withName(java.lang.String name)
The name of the Db is used primarily for autowiring "name.property" bean props from
|
T |
withType(java.lang.String type) |
protected static final java.util.Set<java.lang.String> reservedParams
protected final org.slf4j.Logger log
protected final java.util.ArrayList<Collection> collections
protected final java.util.Map<java.lang.String,java.lang.String> includeTables
protected final java.util.Set<java.lang.String> includeColumns
protected final java.util.Set<java.lang.String> excludeColumns
protected boolean bootstrap
This would be false when an Api designer wants to very specifically configure an Api probably when the underlying db does not support the type of reflection required. For example, you may want to put specific Property and Relationship structure on top of an unstructured JSON document store.
protected java.lang.String name
protected java.lang.String type
For example type might be "mysql" for a JdbcDb.
protected Path endpointPath
protected boolean dryRun
public final T startup(Api api)
This implementation really only manages starting/started state, with the heaving lifting of bootstrapping delegated to doStartup(Api).
api - the api to startdoStartup(Api)protected void doStartup(Api api)
This method will not be called a second time after for an Api unless the Api is shutdown and then restarted.
The default implementation, when isBootstrap() is true, calls configDb() once globally and configApi(Api) once for each Api passed in.
api - the api to startconfigDb(),
configApi(Api)public T shutdown()
This is primarily a method used for testing.
protected void doShutdown()
protected void doShutdown(Api api)
api - the api shutting downpublic boolean isRunning(Api api)
public final Results select(Collection collection, java.util.Map<java.lang.String,java.lang.String> params) throws ApiException
The implementation of this method primarily translates jsonNames to columnNames for RQL inputs and JSON outputs
delegating the work to doSelect(Collection, List) where all ins and outs are based on columnName.
collection - the collection being queriedparams - RQL terms that have been translated to use Property jsonNamesApiException - TODO: update/correct this javadocpublic abstract Results doSelect(Collection collection, java.util.List<Term> queryTerms) throws ApiException
collection - the collection to queryqueryTerms - RQL terms that have been translated to use Property columnNames not jsonNamesApiExceptionpublic final java.util.List<java.lang.String> upsert(Collection collection, java.util.List<java.util.Map<java.lang.String,java.lang.Object>> rows) throws ApiException
ApiExceptionpublic java.lang.String getHref(java.lang.Object hrefOrNode)
public java.util.Map<java.lang.String,java.lang.Object> getKey(Collection collection, java.lang.Object node)
public java.util.Map<java.lang.String,java.lang.Object> mapTo(java.util.Map<java.lang.String,java.lang.Object> srcRow,
Index srcCols,
Index destCols)
public abstract java.util.List<java.lang.String> doUpsert(Collection collection, java.util.List<java.util.Map<java.lang.String,java.lang.Object>> records) throws ApiException
Keys that are not supplied in the call but that exist in the row in the target DB should not be modified.
Each row should minimally contain key value pairs that satisfy one of the tables unique index constraints allowing an update to take place instead of an insert if the row already exists in the underlying data source.
IMPORTANT #1 - implementors should note that the keys on each record may be different.
IMPORTANT #2 - strict POST/PUT vs POST/PATCH semantics are implementation specific. For example, a RDBMS backed implementation may choose to upsert only the supplied client supplied keys effectively making this a POST/PATCH operation. A document store that is simply storing the supplied JSON may not be able to do partial updates elegantly and replace existing documents entirely rendering this a POST/PUT.
collection - the collection being modifiedrecords - the records being modifiedApiExceptionpublic java.util.List<java.lang.String> patch(Collection collection, java.util.List<java.util.Map<java.lang.String,java.lang.Object>> records) throws ApiException
The default implementation simply calls upsert().
collection - the collection to patchrecords - the key/value pairs to update on existing recordsApiExceptionpublic void doPatch(Collection collection, java.util.List<java.util.Map<java.lang.String,java.lang.Object>> rows) throws ApiException
ApiExceptionpublic abstract void delete(Collection collection, java.util.List<java.util.Map<java.lang.String,java.lang.Object>> indexValues) throws ApiException
IMPORTANT implementors should note that the keys on each indexValues row may be different.
The keys should have come from a unique index, meaning that the key/value pairs for each row should uniquely identify the row, however there is no guarantee that each row will reference the same index.
collection - the collection being modifiedindexValues - the identifiers for the records to deleteApiExceptionprotected void configApi(Api api)
api - the Api to configure.protected void configDb()
throws ApiException
The default implementation simply delegates to buildCollections() and buildRelationships().
Generally, you will need to override buildCollections when implementing a new Db subclass but can probably leave buildRelationships alone as all it does is reflectively build Relationships off of Indexes that are on the Collections.
ApiException - when configuration failsprotected void buildCollections()
includeTables giving the Collections and Properties beautified JSON names.
Subclasses should override this method to reflectively create Collections, Properties and Indexes and then call super.buildCollections() if they want names them beautified.
protected void buildRelationships()
For all foreign key indexes, two relationships objects are created representing both sides of the relationship. A MANY_TO_ONE also creates a ONE_TO_MANY and vice versa and there are always two for a MANY_TO_MANY modeling the relationship from both sides.
protected java.lang.String beautifyCollectionName(java.lang.String tableName)
tableName - the name of an underlying datasource table to be turned into a pretty REST collection namebeautifyName(String)protected java.lang.String beautifyName(java.lang.String name)
Lots of sql db designers use things like SNAKE_CASE_COLUMN_NAMES that look terrible as json property names.
name - the to beautifynameprotected java.lang.String makeRelationshipName(Collection collection, Relationship relationship)
For example, a "ONE Author TO_MANY Books" relationship might show up as "books" on the Author collection and "Author" on the Books collection.
The algorithm attempts to pluralize the "MANY" side of the relationship.
The "ONE" side of a relationship uses the first foreign key column name minus an trailing case insensitive "ID", so in the above example, if there is a foreign key Book.authorId pointing to the Author table, the the name would be "author". If the foreign key column name was Book.primaryAuthorKey, the relationship would be named "primaryAuthorKey".
collection - the collection the relationship name being created will belong torelationship - the relationshippublic java.lang.Object castDbOutput(Property property, java.lang.Object value)
property - the property the value is assigned tovalue - the value pulled from the DBvalue cast to Property.typeUtils.castDbOutput(String, Object)public java.lang.Object castJsonInput(Property property, java.lang.Object value)
property - the property the value is assigned tovalue - the value to cast to the datatype of propertyvalue cast to Property.typeUtils.castJsonInput(String, Object)public java.lang.Object castJsonInput(java.lang.String type,
java.lang.Object value)
type - the type to cast tovalue - the value to castvalue cast to typeUtils.castJsonInput(String, Object)protected void mapToJsonNames(Collection collection, Term term)
protected java.util.Set<Term> mapToColumnNames(Collection collection, Term term)
protected Property getProperty(java.lang.String tableName, java.lang.String columnName)
public Collection getCollectionByTableName(java.lang.String tableName)
public void removeCollection(Collection table)
public java.util.List<Collection> getCollections()
collectionspublic T withIncludeTables(java.lang.String includeTables)
Example: db.tables=customers,books-prod-catalog|books,INVENTORY_ADJUSTED_BY_PERIOD|inventory
This method is primarily useful when an Api designer wants to manually configure Collections instead of having the Db reflectively build the Collections.
This method can also be called prior to the db reflection phase of startup to whitelist tables that the reflection should include.
The string is first split on "," to get a list of table names.
If the table name contains a "|" character, the part on the left is considered the tableName and the part on the right is considered the collectionName.
If there is no "|" then the beautified tableName is used for the collectionName.
includeTables - underlying data source tables that should be included as REST collectionsbeautifyCollectionName(String)public T withIncludeTable(java.lang.String tableName, java.lang.String collectionName)
tableName - the table to build a Collection forcollectionName - the name of the target collection that can be different from tableName.public T withCollections(Collection... collections)
collections - to include (add not replace)public T withCollection(Collection collection)
public boolean filterOutJsonProperty(Collection collection, java.lang.String name)
includeColumns
excludeColumns or is a valid Property columnName.
This can be used to filter out things like URL path mapped parameters that don't actually match to "columns" in a document store.
This does not prevent the underlying Property from being part of a Collection object model and the names here don't actually have to be Properties.
collection - the collection in questionname - the name of the property to optionally filterpublic T withIncludeColumns(java.lang.String... columnNames)
public T withExcludeColumns(java.lang.String... columnNames)
public java.lang.String getName()
public T withName(java.lang.String name)
name - the name to setpublic boolean isType(java.lang.String... types)
public java.lang.String getType()
public T withType(java.lang.String type)
public boolean isBootstrap()
public T withBootstrap(boolean bootstrap)
public Path getEndpointPath()
public boolean isDryRun()
public T withDryRun(boolean dryRun)
Copyright © 2021 Rocket Partners, LLC. All rights reserved.