Goals:
Page bookmarks:
definition
Eve must read and update a database structure using an internal data model. A model is a map between Eve a relational data model and an object oriented model. This is called ORM = Object Relational Model. An ORM model is connecting to one or multiple databases using vendor specific database modules.
The model library must be imported using these statements:
# using Oracle library
import
db $evelib/db/core;
orcl: $evelib/db/oracle;
One application can connect to multiple databases simultaneously. A specific kind of application called pipeline can pull data from multiple sources to update one target database or, spread data from one database and push data into multiple other databases.
pattern:
** create a database session
class OracleSession = {user,password,name: String} <: orcl.Database:
new _credentials: String;
release
orcl.disconnect();
return
constructor OracleSession(location,user,password:String) => (@self db.Session):
** prepare credentials
let self.location = location;
let self.user = user;
let self.password = password;
** prepare credentials
let self.connect(self._credentials);
orcl.connect();
return;
process:
** create database connection
new demoDB = set db := OracleSession(user:$user,password:$password,location:$location);
...
return;
Note: Usually a database has a security protocol based on user-name and password. These credentials can not be encoded in the Eve scripts, they need to be received as input parameters by the driver or set using configuration files that are secret, safly stored on the server. Once a connection is established it can be used to manipulate data, using Eve statements.
For each database kind we will create a library module. These modules will provide similar functionality for different databases. This basic functionality is created to make available a "Database Connector" in Eve scripts. We need this functions:
Method | Description |
---|---|
connect() | Connect the database |
disconnect() | Disconnect from database |
query() | Direct SQL execution |
execute() | Execute a stored procedure |
commit() | Save all pending modifications |
rollback() | Rollback all pending modifications |
Eve should provide drivers for main-stream databases:
One database provide a complex framework to organize data in tables and documents. An application can read database structure and map it to an internal memory model. Then we can perform operations on this model: {search, append, update, delete}. The opperations are mirrored automaticly in the database.
A database table has records. Eve strategy is to define one memory table as a collection of objects for each database table. The memory table is mapped to database table. Mapping is one to one. Field names are case sensitive.
Data cashing. The memory table do not store all the database data. Instead, the memory table store a small buffer. On rare ocasions, user can store all data in memory tables for small crytical tables to improve performance when a table is used very often. Usually these tables are metadata tables.
Internal memory tables are mixed collections of objects having same name as database tables. We can read a database table record by record on demand. A memory table can load one or multiple records in memory. Developer/user has full control over what data is stored in memory buffer, can refresh the buffer or can clear the buffer and load other data set with a different filter.
A table is an smart class that knows it's bound to a database. Therefore we "inherit" a database table like it is a Eve class. EVE engine will bind the table to database. If the table in the database do not exist, EVE compiler will complain and will generate an error.
class TableName = {name: String} <: DB_Name.Table;
Records are object instances representing in memory table structure. A record has a status property that can be used in conditionals. One record is the current record. Several records are cached in memory for a table. A record id identified by a numeric id. Records are "bound" to database data. A record has information about it's origin.
class RecordName = {id: Integer} <: Record;
Table structure is represented by Records structure. This can be flat or hierarchic. A Table is a collection of records. Tables can be related to each other. A record can contain a table of records. This is called nested table. Some databases have support for nested tables, some do not. In this case the nested table is just a relational table that has a foreign key to main table.
Note:
Data model can work with sessions. A session have a name. A session can involve one or more databases. After a set of modifications is done you can commit changes in a session. If transaction fail all the modifications in session are reset. You can customize the actions in case of error. After session ends, you can create a report.
Operations
Any of the following operations can be included in a session:
Eve can add new data records into one table using append() method of a table. This create new records.
routine update_tables(value1,value2 ...):
** create empty records
new record1 := table_name1.append();
new record2 := table_name2.append();
** populate records with values
update record1:
let field_name := value1;
...
update record2:
let field_name := value2;
...
commit;
return;
Eve can do single or multiple row updates in one transaction. The Update need to find the record to update before is able to modify these records.
** use search fields and values
process
new record1 := table_name.find(field:value1, ...);
new record2 := table_name.find(field:value2, ...);
update record1:
let field_name := value;
...
update record2:
let field_name := value;
...
commit;
return;
Note A process or routine will start a new isolated transaction. In case of error, you can issue a rollback. The issue can be in the database, if you issue a rollback the database changes are not commited but the record remain modified in memory. You must refresh the record if you want the see the old values.
This statement will remove one or more records from a table.
Syntax
** Find one or more records and delete them
process
** local variables
new deleted1 := table_name1.delete(field:value,...);
new deleted2 := table_name2.delete(field:value,...);
commit;
finalize
** check status
expect deleted1 > 0;
expect deleted2 >= 0;
** print status
print (deleded1, "records deleted")
print (deleded2, "records deleted")
return;
For bulk operations there are 2 methods. Using Eve cycle or using direct SQL.
** using a loop to create a single session
routine delete_records(value):
** delete current record using: loop
for record in table_name loop
record.delete() if record.field_name == value;
repeat;
commit;
return;
Sometimes we need to bypass the ORM and execute native SQL:
# execute query string and return a list of records
new query_result := db.query(query_template ? source)
Working with Direct SQL is not our purpose. Eve has enaugh power to enable developers handle processes. Using direct SQL is a backup, offered for special opperations Eve is not capable of. Examples will be provided for testing.
Some databases have support for stored procedures. It will be a challenge to engage these procedures from EVE. Since not all databases have stored procedures, these are extra features that we can implement in Eve. Most of the time, Eve scripts are self contained and depend only on database row data and do not use extra layers like views, snapshots or stored procedures to read data. However just in case data is not available in any other way we can call stored procedures to retrive data.
driver main:
** prepare an object (not updatable)
class Result = {
field_name1:Type ,
field_name2:Type ,
...
} <: Record;
global
new buffer: ()Result; -- collection/table
process
** execute stored procedure and collect records in the buffer
let buffer <+ db.procedure_name(arguments);
...
return;
For debugging the SQL, Eve will enable introspection.
Read next: Eve Tutorial