Symbolic Logic:Programming:ORM Persistence
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.
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,
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,
- Simple query
- List(Person) personList = Person.Select(p : p.GetName() == "Bob");
- Conditions on related files,
- List(Person) personList = Person.Select(p : p.GetPetList().Count() > 0);
- Setting up a constraint.
- 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,
- creating tables
- adding columns
- modifying columns
- 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.
- Inserting a new row at the time of the new timestamp.
- Archiving off or forgetting the old row.
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
- Symbolic Logic:Programming:Object Relational Mapping
- Symbolic Logic:Programming:Framework
- Symbolic Logic:Programming
- Intelligence and Reasoning