Symbolic Logic:Programming:ORM Persistence

From Knowino
Jump to: navigation, search

An Object to Relational Mapping (ORM) for a logic programming language allow access to a relational database using logic programming. No SQL need be written while providing a wide range of SQL functionality.

This page describes how to create persistent classes. A persistent class has the data for each object stored as a row in a table. There is one table per class.

Contents

[edit] Usage of Persistence

Persistence is added to a class by inheriting from the Persist class.

class Person
{
inherit Persist("person", Person);
}

Inheriting from Persist gives access to the following methods.

ORM Persistence Functionality
Name Description
CreateTemplate Create an object in template mode for use in constructing queries.
CreateObject Create a new object to be added to the database.
Select Run queries to retrieve a list of persistent objects into memory.
Save Save changes to the database
UpgradeDB Upgrade the database to create the table and any missing columns.

A persistent class may have Attributes. The following attributes are added by default,

ORM Default Attributes
Name Description
Id A number that identifies the object.
Timestamp The time at which the object was last updated. This field is used to implement optimistic locking.
UpdateUser The user who last updated the object.

Attributes may be added,

class Person
{
Attribute("name", String) as Name;
}

One to Many Relations may be added by defining a relation class,

class FKPet
{
FKOneToMany(class Person, symbol Pet, class Dog, symbol Owner)
}

[edit] Selection of Records

A list of objects may be retrieved using code like,

List(Person) personList = Person.Select(p : p.GetName() == "Bob");
List(Person) personList = Person.Select(p : p.GetPetList().Count() > 0);
Person person = Person.CreateTemplate();
person.GetAge() > 50;
List(Person) personList = person.Select(); // All people older than 50.
List(Dog) personList = person.GetPetList().Select(); //All dogs for people older than 50.

[edit] Implementation of Persist

Persist is a generic class with very little functionality. Its main purpose is to associate a table name with the class. Most of the implementation is in PersistBase.

class Persist(String tableName, class T)
{
inherit PersistBase;
T CreateTemplate()
{
T t = new T;
t.SetMode(EnumMode::TemplateMode);
return t;
}
T CreateObject()
{
T t = new T;
t.SetMode(EnumMode::InsertMode);
return t;
}
String GetTableName()
{
return tableName;
}
List(T) Select(SqlCondition condition, SqlOrder order)
{
return PersistBase.SelectBase(SqlCondition condition, SqlOrder order);
}
}

Persists base implements attributes that all tables must support.

class PersistBase
{
public:
inherit Attribute("id", DbIdType) as Id; // Identifies the record
inherit Attribute("timestamp", Time) as TimeStamp; // Identifes the time of last modification.
inherit Attribute("update_user", String) as UpdateUser; // Identifies the used
}

[edit] Selection of Records

Selection of records is implemented in PersistBase by,

class PersistBase
{
public:
// Return a list of objects from this class and all classes that inherit from it.
List(PersistBase) SelectBase(SqlCondition condition, SqlOrder order)
{
return GetAllClasses().ForEachCombineList(t : t.SelectTable(condition, order), order);
}
// Return a list of objects that match the condition from only this object.
List(PersistBase) SelectTable(SqlCondition condition, SqlOrder order)
{
db.Query(GenerateSelect(condition, order), GenerateBind(condition))
.ForEachCombine(row : CreateObject().Read(row));
}
protected:
// Get a list of classes that inherit from the implementing class.
abstract List(PersistBase) GetInheritingClasses();
// Get a comma separate string of field names.
abstract String GetAttributeNames();
abstract void Read();
String CommaCombine(String first, String second)
{
return first + ", " + second;
}
private:
// Generate the SQL statement that returns the rows from the table.
String GenerateSelect(SqlCondition condition, SqlOrder order)
{
return "SELECT " + GetAttributeNames()
+ " FROM " + GetTableName()
+ " WHERE " + condition(this).GetText()
+ " ORDER BY " + order(this).GetText()
}
DBBind GenerateBind(SqlCondition condition)
{
return condition.GetBind();
}
}

[edit] Saving

class PersistBase
{
enum EnumMode { TemplateMode, InsertMode, UpdateMode, DeleteMode, DeletedMode };
inherit Property(EnumMode) as Mode;
void Save(role Database db)
{
if IsDirty() then
{
DBBind bind = new DBBind();
if (GetMode() == InsertMode)
{
Bind(bind);
db.Call(GetSPITName(), bind);
SetMode(UpdateMode);
}
else if (GetMode() == UpdateMode)
{
Bind(bind);
db.Call(GetSPUTName(), bind);
}
else if (GetMode() == DeleteMode)
{
db.Call(GetSPDTName(), bind);
SetMode(DeletedMode);
}
}
}
protected:
abstract bool IsDirty();
abstract Bind(DBBind bind);
private:
String GetSPITName()
{
return "SPIT" + GetTableName();
}
String GetSPUTName()
{
return "SPUT" + GetTableName();
}
String GetSPDTName()
{
return "SPDT" + GetTableName();
}
}

[edit] Upgrading the Database

[edit] Database Tables

Upgrading the database tables is restricted to,

class PersistBase
{
public:
void UpgradeDB(role Database db, role Time currentTime)
{
if (not db.TableExists(GetTableName())
{
UpgradeColumns(db.CreateTable());
}
else
{
UpgradeColumns(db.GetTable(GetTableName()));
}
}
protected:
abstract void UpgradeColumns(DBTable table);
}

[edit] Stored Procedures

Upgrading the stored procedures is relatively straight forward. There are standard templates for the stored procedures. The templates implement optimistic locking.

Inserting a record makes sense in a logic programming environment because of the timestamp. The record is being brought into existence at time. From the mathematical point of view the whole history of the database is a single unchanging entity. But we dont know the whole history yet. Inserting a row is discovering more history.

Technically updating a row makes sense in terms of logic programming as long as we think of it as steps.

As long as we only ask questions about the state of the database now Updating and Deleting are OK.

[edit] SPIT - Insert a row
[edit] SPUT - Update a row
[edit] SPDT - Delete a row

[edit] Links

Personal tools
Variants
Actions
Navigation
Community
Toolbox