RDBMS
This article aims to give an overview about the RDBMS module, describe its propose and give some fine details of usage.
RDBMS Overview
Lightblue can have its data stored in different kinds of databases due its flexible plugin-enabled architeture. It just requires the implementation of the business logic to handle the specific data storage.
To use this module the user must:
- setup the RDBMS enviroment
- make a datasource available to lightblue via JNDI
- create metadata using the RDBMS datastore, referencing the JNDI above
How it works
To configure your entity's data be persisted on a relational database, you need to create metadata that references a valid JNDI name for a datasource connection pool in datasources.json. By default the datasource used is defined in the Metadata's schema).
In the Schema of your metadata create an rdbms`field following rdbms.json. This field maps all the lightblue CRUD operations (insert, find, update, save, delete) so the entity can be properly handled by the lightblue RDBMS controller (mapped by lightblue-crud.json).
A simple example of a new Entity Metadata that will use RDBMS for data persistence. The JNDI name for the datasource is rdbmsdata
:
{
"entityInfo" : {
"name": "user",
"datastore": {
"backend":"rdbms",
"datasource": "rdbmsdata"
}
},
"schema" : {
"name" : "user",
"version": {
"value": "1.0.0",
"changelog": "Test version"
},
"status": {
"value": "active"
},
"access" : {
"insert": ["anyone"],
"find":["anyone"],
"update":["anyone"],
"delete":["anyone"]
},
"fields": {
"id": {"type": "string"},
"object_type": {"type": "string"},
"login": {
"type": "string",
"constraints": {
"maxLength": 64,
"minLength": 1,
"required": true
}
},
"createdDate": {"type": "date"}
},
"rdbms" : {
"delete":{"bindings":{...}, "expressions":[...]},
"fetch":{"bindings":{...}, "expressions":[...]},
"insert":{"bindings":{...}, "expressions":[...]},
"save":{"bindings":{...}, "expressions":[...]},
"update":{"bindings":{...}, "expressions": [...]},
"dialect":"oracle",
"SQLMapping":{...}
}
},
}
RDBMS Schema at a glance
There are the following core fields that enable RDBMS schema mapping to be dynamic and flexible:
bindings
- configures the variables to put or collect from the SQL statement.expressions
- flexible structure of crieteria and conditions for evaluation of an expression.
bindings
The binding allow you to specify intermediate placeholders for inbound and outbound data.
in
- the values that need to be inserted into the statementsout
- the values that need to be extracted from the result set
For each the following attributes are set:
column
- the column in the request or result to bind to the given fieldfield
- the name to bind the result in the column to for future use
"bindings": {
"in" : [
{
"column" : "NameCustomer",
"field" : "name"
},
{
"column" : "y",
"field" : "x"
}
],
"out" : [..]
}
expressions
This field holds an array of:
statement
- SQL statements (insert, delete, select, update, call).if
/then
- conditional statement describing the logic to be evaluated, what should happen, and optionalelseIf
orelse
.foreach
andfor
- iteration operators used to operate on each value in a given result.
"expressions" : [
{
"statement": {
"sql": "SELECT * FROM DUAL",
"type": "select"
}
},
{
"if": {
"or": [
{
"fieldCheckField": {
"field": "x",
"op": "gt",
"rfield": "y"
}
},
{
"fieldCheckValues": {
"field": "z",
"op": "in",
"values": ["a", "1", "3"]
}
}
]
},
"then": "fail",
"elseIf": [
{
"if": {
"fieldCheckField": {
"fieldCheckField": "x",
"op": "greaterThan",
"rfield": "z"
},
"then": [
{
"sql": "SELECT 5 FROM DUAL",
"type": "select"
}
]
},
"else": [
{
"foreach": {
"iterateOverField": "z",
"expressions": [
{
"sql": "SELECT z FROM DUAL",
"type": "select"
}
]
}
},
{
"for": {
"loopTimes": "10",
"loopCounterVariableName": "i",
"expressions": [
{
"sql": "SELECT i FROM DUAL",
"type": "select"
}
]
}
}
]
}
]
}
]
RDBMS: a deep dive
As already discussed some details around the RDBMS module, now we will focus about its schema. An example of the format of rdbms is:
{
"delete": operation,
"fetch": operation,
"insert": operation,
"save": operation,
"update": operation,
"dialect": RDBMS_VENDOR,
"SQLMapping": SQLMapping
}
delete
/fetch
/insert
/save
/update
: A Operation object for each Lightblue operation
The schema have a contraint that at least one of those fields must be declared (empty object will result on a error message in the response). Lightblue will evaluate during the runtime if the operation requested is informed in the Entity's RDBMS object, if it wasn't informed before hand in the specific version and error message will be returned. If you have an entity persisted that will need all the Lightblue operations you should declare all the fields.
operation
Each RDBMS's operation must follow the below structure:
{
"bindings": bindings,
"expressions": [expressions]
}
bindings
: A Bindings object. Not required. This field will be used to input and output dynamic data from/into the statementsexpressions
: An array of Expression objects. Required to be declared. It will be used to evaluate some logic conditionals, iterate over a field (from binding or from the entity) and also run SQL statements
Bindings
The bindings must follow the below structure:
{
"in" : [InOut],
"out": [InOut]
}
in
: An array of InOut objects. This field stands for the information that the user want to be extracted from the Entity so it can be used in the SQL statementout
: An array of InOut objects. This field will extract the information from the one of the SQL statements and put that in the Entity
This part of the schema requires that you at least declare in
or out
field, if you declared a bindings object.
InOut
The InOut must follow the below structure:
{
"column": string,
"field": string
}
column
: is a String. It represent a variable in the SQL statement that will be replaced by the field value if used with 'in' field or the other way round, extracting from the result of an SQL statement to a field if it is in 'out' field.field
: is a String. It will get the information from a Entity's field to be used with 'in' field or the other way round, the field can be filled with the value from the result of an SQL statement.
expressions
Each RDBMS's expression element of the array can be one of the following objects:
{
"statement": statement
}
{
"if": if,
"then": then,
"elseIf": [elseIf],
"else": "else"
}
{
"foreach": foreach
}
{
"for": for
}
statement
: This object represent an SQL statement that needs to run in that informed orderif
et al: An conditional object that can make the dynamic SQL statement to run (or even to stop everything) depending on the inputted dataforeach
: This operator object will iterate over a field or variablefor
: This operator object will loop and create a variable that represent the state of the loop
An expression must follow one of those structures above, otherwise it won't be valid.
statement
The statement can defined as the following structure:
{
"sql": string,
"type": "select"|"insert"|"update"|"delete"|"call"
}
sql
: This SQL statement is a required field of String type that you want to be executed. It have have variables to be inputed or extracted if it is necessary.type
: It represents the type of the SQL statement is. It must be one of the enum above and it is a required field.
if et al
The if et al follows thi structure:
{
"if": conditional,
"then": elseOrThen,
"elseIf": [elseIf],
"else": elseOrThen
}
if
: This field represent an object that determinate the coditional that will trigger the execution of thethen
field or other field (if informed) of the same object (elseif
s andelse
). It is a required fieldthen
: Only executed when the evaluation of 'if' returns true. More information onelseOrThen
elseIf
: an array of elseIf objects which try a different conditional in can the first if field of this object returned falseelse
: Only executed when the evaluation. More information onelseOrThen
Where the elseOrThen
is defined as following:
"oneOf": [
{
[expressions]
},
{
"enum": "fail"|"continue"|"break"
}
]
elseOrThen
can be defined with one of two distinct objects. It can be an array ofexpressions
(also document in this article), or it can be one of the above enums, which:fail
: will rollback the transactioncontinue
: will skip the current loop iterationbreak
: will break the loop iteration
conditional
The conditional can be defined as one of following objects:
{
"or": [conditional]
}
{
"any": [conditional]
}
{
"and": [conditional]
}
{
"all": [conditional]
}
{
"not": conditional
}
{
"fieldEmpty": fieldEmpty
}
{
"fieldCheckField": fieldCheckField
}
{
"fieldCheckValue": fieldCheckValue
}
{
"fieldCheckValues": fieldCheckValues
}
{
"fieldRegex": fieldRegex
}
or
/any
: it is an array of at least two conditional objects. If any of the informed conditionals are asseted as true, it will trigger the then field associated with this fieldand
/all
: it is an array of at least two conditional objects. If all of the informed conditionals are asseted as true, it will trigger the then field associated with this fieldnot
: it is a conditional object. If the informed conditional is asseted as false, it will trigger the then field associated with this field- The other fields are objects which will be described in more details in next sections
fieldEmpty
The fieldEmpty is structed as following:
{
"field": String
}
field
: A required String that represents a field from the Entity or an inputed variable. If the evaluation of the field is empty, this conditional will return true, otherwise it will return false
fieldCheckField
The fieldCheckField is structed as following:
{
"field": String,
"op": "eq"|"neq"|"lt"|"gt"|"lte"|"gte"|"in"|"nin",
"rfield": String
}
field
: A required String that represents a field from the Entity or an inputed variableop
: A required field which must be one of the above enums. A better description of this enum, look on this reference.It will return the evaluation of the field against rfield using the operation oprfield
: A required String that represents a field from the Entity or an inputed variable
fieldCheckValue
The fieldCheckValue is structed as following:
{
"field": String,
"op": "eq"|"neq"|"lt"|"gt"|"lte"|"gte"|"in"|"nin",
"value": String
}
field
: A required String that represents a field from the Entity or an inputed variableop
: A required field which must be one of the above enums. A better description of this enum, look on this reference. It will return the evaluation of the field against rfield using the operation opvalue
: A required String that represents a simple value to be used to comparison
fieldCheckValues
The fieldCheckValues is structed as following:
{
"field": String,
"op": "eq"|"neq"|"lt"|"gt"|"lte"|"gte"|"in"|"nin",
"values": [String]
}
field
: A required String that represents a field from the Entity or an inputed variableop
: A required field which must be one of the above enums. A better description of this enum, look on this reference. It will return the evaluation of the field against rfield using the operation opvalues
: An array of required String that represents a simple value to be used to comparison
fieldRegex
The fieldRegex is structed as following:
{
"field": String,
"regex": String,
"caseInsensitive": boolean,
"multiline": boolean,
"extended": boolean,
"dotall": boolean
}
field
: A required String that represents a field from the Entity or an inputed variable.regex
: A required String that must follow the regex standard format. It will be used to be evaluated against the target fieldcaseInsensitive
: A non-required bolean field that can enable or disable the case sensitive of the regex executionmultiline
: A non-required bolean field that can enable or disable the multilne usage of the regex executionextended
: A non-required bolean field that can enable or disable the case extend function of the regex executiondotall
: A non-required bolean field that can enable or disable the regex to match any character including a newline
op
The op is structed as following:
"op": "eq"|"neq"|"lt"|"gt"|"lte"|"gte"|"in"|"nin"
eq
: The equals operator. Example of A equals B, so A eq Bneq
: The not equals operator. Example of A not equals B, so A neq Blt
: The less than operator. Example of 1 less than 2, so 1 lt 2gt
: The greater than operator. Example of 5 greater than 3, so 5 gt 3lte
: The less than or equals operator. Example of 1 less than or equals 1, so 1 lte 2gte
: The greater than or equals operator. Example of 5 greater than or equals 5, so 5 gte 5in
: The in operator. Example of [1,2,3] is in [1,2,3,4], so A in Bnin
: The not in operator. Example of [1,3] is in [0,2,4], so A nin B
elseIf
The elseIf can be defined as following:
{
"if": if,
"then": then
}
if
: Required If object. Find more information about If Object in this articlethen
: Required Then object. Find more information about Then Object in this article
foreach
The foreach is structed as following:
{
"iterateOverField": String,
"expressions": expressions
}
iterateOverField
: Required String. It will be evaluated in runtime to get a variable or a Entity's field array of values to iterate over it calling the informed expressionsexpressions
: Required expressions object. These expressions will be evaluated for each value from iterateOverField field
for
The for can be defined as following:
{
"loopTimes": number,
"loopCounterVariableName": String,
"expressions": expressions
}
loopTimes
: Required number. It will be evaluated in runtime to get a variable or a Entity's field to iterate over expressions as many times as it is resultsloopCounterVariableName
: Required String. It will be the temporary variable that will hold the current state of the loopexpressions
: Required expressions object. These expressions will be evaluated for each value from iterateOverField field
dialect
This field will enable you to nform to lightblue which RDBMS Vendor is going to be used, so lightblue can generate some specific statements for that vendor. The only value supported right now is "oracle". Example:
{
"dialect": "oracle",
}
SQLMapping
You can use this field to specify the relationship between the entity persisted as a document in lightblue and the tables persisted in RDBMS. It must follow the below structure:
{
"columnToFieldMap":[{
"table": "t",
"column": "c",
"field": "f"
}],
"joins": [{
"tables": [{
"name": "n",
"alias": "a"
}],
"joinTablesStatement": "x",
"projectionMappings": [{
"column": "c",
"field": "f",
"sort": "s"
}]
}]
}
columnToFieldMap
: Describe the relationship between eachfield to a column;joins
: Describe the tables, informing the necessary joins, sort alias, etc. Each object of this array will provide the tables that you want to map between the lightblue and the RDBMS using the tables field and with joinTablesStatement maps the lightblue entity's fields with the RDBMS columns