Recent

Author Topic: dOPF - Object persistence framework / ORM  (Read 2293 times)

Renat.Su

  • Full Member
  • ***
  • Posts: 243
    • Renat.Su
dOPF - Object persistence framework / ORM
« on: November 17, 2024, 07:44:02 pm »
I just want to inform about the dOPF library (author Silvio Clécio  https://github.com/silvioprog ). A very convenient, intuitive and lightweight ORM. Previously, it was in the BrookFreePascal plugins folder, although it was a self-contained library and has no dependency on Brook. Now it's got a separate location here https://github.com/pascal-libs/dopf


P.S.
When I need to get a fast and convenient database operations (especially not in GUI applications) this library is my most frequent choice.

Zoran

  • Hero Member
  • *****
  • Posts: 1894
    • http://wiki.lazarus.freepascal.org/User:Zoran
Re: dOPF - Object persistence framework / ORM
« Reply #1 on: November 17, 2024, 10:16:15 pm »
Is there any help or tutorial for it?

Renat.Su

  • Full Member
  • ***
  • Posts: 243
    • Renat.Su
Re: dOPF - Object persistence framework / ORM
« Reply #2 on: November 17, 2024, 10:38:55 pm »
Is there any help or tutorial for it?
The best tutorial is demo folder. There are some main ways to use the lib.

It has different generic classes for working with databases. The most basic way to CRUD operations with maximum approximation is to work with DB engines through objects of your classes. Published properties are fields. Standard for ORM encapsulation of work with SQL engines (MySQL, SQLite, Firebird etc - connecting via ***conn.pas from native sqldb packages and driver property). And many other features.

Renat.Su

  • Full Member
  • ***
  • Posts: 243
    • Renat.Su
Re: dOPF - Object persistence framework / ORM
« Reply #3 on: November 17, 2024, 10:45:05 pm »
For example, https://github.com/pascal-libs/dopf/tree/main/demos/opf/entity
Work with storage SQLite3 via entity class:
Code: Pascal  [Select][+][-]
  1. program demo1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.   dOpf, dSQLdbBroker, dbutils, person, sysutils;
  7.  
  8. type
  9.   Topf = specialize TdGSQLdbEntityOpf<TPerson>;
  10.  
  11. var
  12.   i: TPerson;
  13.   pers: Topf.TEntities;
  14.   opf: Topf;
  15. begin
  16.   opf := Topf.Create(dbutils.con, 'person');
  17.   pers := Topf.TEntities.Create;
  18.   try
  19.     WriteLn('Empty table');
  20.     opf.Empty;
  21.     opf.Apply;
  22.     WriteLn('Done.');
  23.  
  24.     WriteLn('Add Silvio Clécio');
  25.     opf.Entity.Name := 'Silvio Clécio';
  26.     opf.Add;
  27.     WriteLn('Done.');
  28.  
  29.     WriteLn('Add Anonymous');
  30.     opf.Entity.Id := 1000;
  31.     opf.Entity.Name := 'Anonymous';
  32.     opf.Add(False);
  33.     WriteLn('Done.');
  34.  
  35.     WriteLn('Add Waldir');
  36.     opf.Entity.Id := 1001;
  37.     opf.Entity.Name := 'Waldir';
  38.     opf.Add(False);
  39.     WriteLn('Done.');
  40.  
  41.     WriteLn('Add João Morais');
  42.     opf.Entity.Name := 'João Morais';
  43.     opf.Add;
  44.     WriteLn('Done.');
  45.  
  46.     WriteLn('Add Sven Barth');
  47.     opf.Entity.Name := 'Sven Barth';
  48.     opf.Add;
  49.     WriteLn('Done.');
  50.  
  51.     WriteLn('Modify name of Waldir to Waldir Paim');
  52.     opf.Entity.Id := 1001;
  53.     opf.Entity.Name := 'Waldir Paim';
  54.     opf.Modify;
  55.     WriteLn('Done.');
  56.  
  57.     WriteLn('Remove Anonymous');
  58.     opf.Entity.Id := 1000;
  59.     opf.Remove;
  60.     WriteLn('Done.');
  61.  
  62.     WriteLn('Get Waldir Paim');
  63.     opf.Entity.Id := 1001;
  64.     opf.Get;
  65.     WriteLn(opf.Entity.Id, ', ', opf.Entity.Name);
  66.     WriteLn('Done.');
  67.  
  68.     WriteLn('Find Silvio Clécio by name');
  69.     opf.Entity.Name := 'Silvio Clécio';
  70.     opf.Find('name = :name');
  71.     WriteLn(opf.Entity.Id, ', ', opf.Entity.Name);
  72.     WriteLn('Done.');
  73.  
  74.     WriteLn('Search for names containing "a"');
  75.     opf.Entity.Name := '%a%';
  76.     opf.Find(pers, 'name like (:name)');
  77.     for i in pers do
  78.       WriteLn(i.Id, ', ', i.Name);
  79.     pers.Clear;
  80.     WriteLn('Done.');
  81.  
  82.     WriteLn('List all');
  83.     opf.List(pers);
  84.     for i in pers do
  85.       WriteLn(i.Id, ', ', i.Name);
  86.     pers.Clear;
  87.     WriteLn('Done.');
  88.  
  89.     WriteLn('Search for names containing "a" (order by id DESC)');
  90.     opf.Entity.Name := '%a%';
  91.     opf.Search(pers, nil,
  92.       'select * from person where name like (:name) order by id desc');
  93.     for i in pers do
  94.       WriteLn(i.Id, ', ', i.Name);
  95.     pers.Clear;
  96.     WriteLn('Done.');
  97.  
  98.     opf.Apply;
  99.   finally
  100.     pers.Free;
  101.     opf.Free;
  102.   end;
  103.  
  104.   ReadLn;
  105. end.
Connection to DB in dbutils.pas
Code: Pascal  [Select][+][-]
  1. unit dbutils;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   dSQLdbBroker, sqlite3conn, SysUtils;
  9.  
  10. function con: TdSQLdbConnector;
  11.  
  12. implementation
  13.  
  14. var
  15.   _con: TdSQLdbConnector = nil;
  16.  
  17. function con: TdSQLdbConnector;
  18. begin
  19.   if not Assigned(_con) then
  20.   begin
  21.     _con := TdSQLdbConnector.Create(nil);
  22.     _con.Logger.Active := True;
  23.     _con.Logger.FileName := 'OUTPUT.LOG';
  24.     _con.Driver := 'sqlite3';
  25.     _con.Database := '../../data.sqlite3';
  26.   end;
  27.   Result := _con;
  28. end;
  29.  
  30. finalization
  31.   FreeAndNil(_con);
  32.  
  33. end.
  34.  

Zoran

  • Hero Member
  • *****
  • Posts: 1894
    • http://wiki.lazarus.freepascal.org/User:Zoran
Re: dOPF - Object persistence framework / ORM
« Reply #4 on: November 17, 2024, 11:05:19 pm »
Thanks. Apparently, it's taken from the original author to a new maintainer. Can we expect it to be actively maintained?

Renat.Su

  • Full Member
  • ***
  • Posts: 243
    • Renat.Su
Re: dOPF - Object persistence framework / ORM
« Reply #5 on: November 17, 2024, 11:10:20 pm »
The authorship of the library has not changed. The author and I discussed the expediency of the transfer. This is a joint decision.
I would also like to draw your attention to the fact that he also created the pascal-lib organization https://github.com/pascal-libs / and I also made the last commits to this library when it was part of BrookFreePascal, so I am aware of the affairs of this library.

Zoran

  • Hero Member
  • *****
  • Posts: 1894
    • http://wiki.lazarus.freepascal.org/User:Zoran
Re: dOPF - Object persistence framework / ORM
« Reply #6 on: November 17, 2024, 11:35:20 pm »
Thank you!

Renat.Su

  • Full Member
  • ***
  • Posts: 243
    • Renat.Su
Re: dOPF - Object persistence framework / ORM
« Reply #7 on: November 18, 2024, 12:00:44 pm »
Thank you!
Thanks for the questions.
I actively use it for non-visual work with databases and creating layers between business logic and storages

wp

  • Hero Member
  • *****
  • Posts: 12530
Re: dOPF - Object persistence framework / ORM
« Reply #8 on: November 18, 2024, 02:46:05 pm »
dOPF is in OPM now.

delphius

  • Jr. Member
  • **
  • Posts: 77
Re: dOPF - Object persistence framework / ORM
« Reply #9 on: November 18, 2024, 09:58:34 pm »
Another simple example...(empty db is in attach)

TdGSQLdbOpf:
This is a generic ORM (Object-Relational Mapping) operator that simplifies database operations.
By specializing it with TPerson, it manages CRUD operations for the Person table directly, reducing boilerplate SQL code.

Empty and Apply:
Empty clears all data in the associated table, useful for resetting test environments.
Apply commits any pending changes (e.g., inserts, updates, or deletes) to the database.

Add, Modify, Remove, and List:
Add: Adds a new entity to the database.
Modify: Updates an existing record based on the entity's ID.
Remove: Deletes a record identified by the entity's ID.
List: Retrieves all records from the table into a collection.

Entities:
Individual records are represented as instances of TPerson.
Multiple records are stored in a collection (Topf.TEntities), which is iterated using for..in.

Memory Management:
Entities (TPerson) and collections (Topf.TEntities) must be explicitly created and freed to avoid memory leaks.

Code: Pascal  [Select][+][-]
  1. program example;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.   dbutils, dSQLdbBroker, SysUtils, person;
  7.  
  8. type
  9.   // Specialization of TdGSQLdbOpf for handling TPerson entities
  10.   Topf = specialize TdGSQLdbOpf<TPerson>;
  11.  
  12. var
  13.   opf: Topf; // Object for managing operations on the `person` table
  14.   choice: Integer;
  15.   name: string;
  16.   age, id, newAge: Integer;
  17.   newName: string;
  18.   per: TPerson; // A single person entity
  19.   pers: Topf.TEntities; // A collection of person entities
  20.  
  21. begin
  22.   // Create an instance of the ORM operator for the `person` table
  23.   // `dbutils.con` provides the database connection, and 'person' is the table name
  24.   opf := Topf.Create(dbutils.con, 'person');
  25.  
  26.   try
  27.     // Ensures the database table is ready for use
  28.     // `Empty` clears all data in the table (useful for testing scenarios)
  29.     opf.Empty;
  30.     opf.Apply; // Applies the changes to the database
  31.  
  32.     // Main menu for interacting with the database
  33.     repeat
  34.       Writeln('1. Add Person');
  35.       Writeln('2. List Persons');
  36.       Writeln('3. Update Person');
  37.       Writeln('4. Delete Person');
  38.       Writeln('0. Exit');
  39.       Write('Enter your choice: ');
  40.       Readln(choice);
  41.  
  42.       case choice of
  43.         1: // Add a new person to the database
  44.           begin
  45.             Write('Enter Name: ');
  46.             Readln(name);
  47.             Write('Enter Age: ');
  48.             Readln(age);
  49.  
  50.             per := TPerson.Create; // Create a new entity instance
  51.             try
  52.               per.Name := name;
  53.               per.Age := age;
  54.               opf.Add(per); // Adds the entity to the database through the ORM
  55.               opf.Apply; // Persists the changes in the database
  56.               Writeln('Person added.');
  57.             finally
  58.               per.Free; // Free the entity after use
  59.             end;
  60.           end;
  61.  
  62.         2: // Retrieve and list all persons in the database
  63.           begin
  64.             pers := Topf.TEntities.Create; // Initialize a collection for entities
  65.             Writeln('Listing all persons:');
  66.             opf.List(pers); // Fetch all records into the collection
  67.             for per in pers do
  68.               Writeln(Format('ID: %d | Name: %s | Age: %d', [per.ID, per.Name, per.Age]));
  69.             Writeln('Done.');
  70.             pers.Free; // Free the collection after use
  71.           end;
  72.  
  73.         3: // Update an existing person's details
  74.           begin
  75.             Write('Enter ID to Update: ');
  76.             Readln(id);
  77.             Write('Enter New Name: ');
  78.             Readln(newName);
  79.             Write('Enter New Age: ');
  80.             Readln(newAge);
  81.  
  82.             per := TPerson.Create; // Create a temporary entity to hold changes
  83.             try
  84.               per.ID := id;
  85.               per.Name := newName;
  86.               per.Age := newAge;
  87.               opf.Modify(per); // Update the record in the database
  88.               opf.Apply; // Commit the updates
  89.               Writeln('Person updated.');
  90.             finally
  91.               per.Free; // Free the entity after use
  92.             end;
  93.           end;
  94.  
  95.         4: // Delete a person by their ID
  96.           begin
  97.             Write('Enter ID to Delete: ');
  98.             Readln(id);
  99.  
  100.             per := TPerson.Create; // Create a temporary entity for deletion
  101.             try
  102.               per.ID := id;
  103.               opf.Remove(per); // Remove the record from the database
  104.               opf.Apply; // Commit the deletion
  105.               Writeln('Person deleted.');
  106.             finally
  107.               per.Free; // Free the entity after use
  108.             end;
  109.           end;
  110.       end;
  111.  
  112.     until choice = 0; // Exit the program when the user chooses option 0
  113.  
  114.   finally
  115.     opf.Free; // Release the ORM operator and associated resources
  116.   end;
  117.  
  118.   Writeln('Exiting...');
  119. end.
  120.  

Code: Pascal  [Select][+][-]
  1. unit person;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. type
  8.  
  9.   { TPerson }
  10.  
  11.   TPerson = class(TObject)
  12.   private
  13.     FID: Integer;
  14.     FName: string;
  15.     FAge: Integer;
  16.   published
  17.     property ID: Integer read FID write FID;
  18.     property Name: string read FName write FName;
  19.     property Age: Integer read FAge write FAge;
  20.   end;
  21.  
  22. implementation
  23.  
  24. end.
  25.  
fpmtls - ssl/tls 1.3 implementation in pure pascal
fpmailsend - sending a simple email message
pascal-webui - use web browser as gui and fpc as backend

Thaddy

  • Hero Member
  • *****
  • Posts: 16420
  • Censorship about opinions does not belong here.
Re: dOPF - Object persistence framework / ORM
« Reply #10 on: November 18, 2024, 10:15:11 pm »
Memory Management:
Entities (TPerson) and collections (Topf.TEntities) must be explicitly created and freed to avoid memory leaks.
Not if they would be derived from TInterfacedObject... In that case it can be instantiated to IInterface and AS and IS provides access as the object and the memory management would be automatic. I have written an idea FR on the bug tracker and Warfley already wrote a MR for his implementation for my idea for a generic class that can add interface support to classes that are not derived from TInterfacedObject. But for now, deriving from TInterfacedObject would provide automatic memory management too.

There is nothing wrong with being blunt. At a minimum it is also honest.

Renat.Su

  • Full Member
  • ***
  • Posts: 243
    • Renat.Su
Re: dOPF - Object persistence framework / ORM
« Reply #11 on: November 18, 2024, 10:22:52 pm »
Memory Management:
Entities (TPerson) and collections (Topf.TEntities) must be explicitly created and freed to avoid memory leaks.
Not if they would be derived from TInterfacedObject... In that case it can be instantiated to IInterface and AS and IS provides access as the object and the memory management would be automatic. I have written an idea FR on the bug tracker and Warfley already wrote a MR for his implementation for my idea for a generic class that can add interface support to classes that are not derived from TInterfacedObject. But for now, deriving from TInterfacedObject would provide automatic memory management too.
Right but an entity class for the dOPF library can be any, not necessarily a descendant of IInterfacedObject or TComponent, but for example, just TObject class
« Last Edit: November 18, 2024, 10:28:11 pm by Renat.Su »

Renat.Su

  • Full Member
  • ***
  • Posts: 243
    • Renat.Su
Re: dOPF - Object persistence framework / ORM
« Reply #12 on: November 18, 2024, 10:24:56 pm »
Another simple example...(empty db is in attach)

TdGSQLdbOpf:
This is a generic ORM (Object-Relational Mapping) operator that simplifies database operations.
By specializing it with TPerson, it manages CRUD operations for the Person table directly, reducing boilerplate SQL code.

Empty and Apply:
Empty clears all data in the associated table, useful for resetting test environments.
Apply commits any pending changes (e.g., inserts, updates, or deletes) to the database.

Add, Modify, Remove, and List:
Add: Adds a new entity to the database.
Modify: Updates an existing record based on the entity's ID.
Remove: Deletes a record identified by the entity's ID.
List: Retrieves all records from the table into a collection.

Entities:
Individual records are represented as instances of TPerson.
Multiple records are stored in a collection (Topf.TEntities), which is iterated using for..in.

Memory Management:
Entities (TPerson) and collections (Topf.TEntities) must be explicitly created and freed to avoid memory leaks.

Code: Pascal  [Select][+][-]
  1. program example;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.   dbutils, dSQLdbBroker, SysUtils, person;
  7.  
  8. type
  9.   // Specialization of TdGSQLdbOpf for handling TPerson entities
  10.   Topf = specialize TdGSQLdbOpf<TPerson>;
  11.  
  12. var
  13.   opf: Topf; // Object for managing operations on the `person` table
  14.   choice: Integer;
  15.   name: string;
  16.   age, id, newAge: Integer;
  17.   newName: string;
  18.   per: TPerson; // A single person entity
  19.   pers: Topf.TEntities; // A collection of person entities
  20.  
  21. begin
  22.   // Create an instance of the ORM operator for the `person` table
  23.   // `dbutils.con` provides the database connection, and 'person' is the table name
  24.   opf := Topf.Create(dbutils.con, 'person');
  25.  
  26.   try
  27.     // Ensures the database table is ready for use
  28.     // `Empty` clears all data in the table (useful for testing scenarios)
  29.     opf.Empty;
  30.     opf.Apply; // Applies the changes to the database
  31.  
  32.     // Main menu for interacting with the database
  33.     repeat
  34.       Writeln('1. Add Person');
  35.       Writeln('2. List Persons');
  36.       Writeln('3. Update Person');
  37.       Writeln('4. Delete Person');
  38.       Writeln('0. Exit');
  39.       Write('Enter your choice: ');
  40.       Readln(choice);
  41.  
  42.       case choice of
  43.         1: // Add a new person to the database
  44.           begin
  45.             Write('Enter Name: ');
  46.             Readln(name);
  47.             Write('Enter Age: ');
  48.             Readln(age);
  49.  
  50.             per := TPerson.Create; // Create a new entity instance
  51.             try
  52.               per.Name := name;
  53.               per.Age := age;
  54.               opf.Add(per); // Adds the entity to the database through the ORM
  55.               opf.Apply; // Persists the changes in the database
  56.               Writeln('Person added.');
  57.             finally
  58.               per.Free; // Free the entity after use
  59.             end;
  60.           end;
  61.  
  62.         2: // Retrieve and list all persons in the database
  63.           begin
  64.             pers := Topf.TEntities.Create; // Initialize a collection for entities
  65.             Writeln('Listing all persons:');
  66.             opf.List(pers); // Fetch all records into the collection
  67.             for per in pers do
  68.               Writeln(Format('ID: %d | Name: %s | Age: %d', [per.ID, per.Name, per.Age]));
  69.             Writeln('Done.');
  70.             pers.Free; // Free the collection after use
  71.           end;
  72.  
  73.         3: // Update an existing person's details
  74.           begin
  75.             Write('Enter ID to Update: ');
  76.             Readln(id);
  77.             Write('Enter New Name: ');
  78.             Readln(newName);
  79.             Write('Enter New Age: ');
  80.             Readln(newAge);
  81.  
  82.             per := TPerson.Create; // Create a temporary entity to hold changes
  83.             try
  84.               per.ID := id;
  85.               per.Name := newName;
  86.               per.Age := newAge;
  87.               opf.Modify(per); // Update the record in the database
  88.               opf.Apply; // Commit the updates
  89.               Writeln('Person updated.');
  90.             finally
  91.               per.Free; // Free the entity after use
  92.             end;
  93.           end;
  94.  
  95.         4: // Delete a person by their ID
  96.           begin
  97.             Write('Enter ID to Delete: ');
  98.             Readln(id);
  99.  
  100.             per := TPerson.Create; // Create a temporary entity for deletion
  101.             try
  102.               per.ID := id;
  103.               opf.Remove(per); // Remove the record from the database
  104.               opf.Apply; // Commit the deletion
  105.               Writeln('Person deleted.');
  106.             finally
  107.               per.Free; // Free the entity after use
  108.             end;
  109.           end;
  110.       end;
  111.  
  112.     until choice = 0; // Exit the program when the user chooses option 0
  113.  
  114.   finally
  115.     opf.Free; // Release the ORM operator and associated resources
  116.   end;
  117.  
  118.   Writeln('Exiting...');
  119. end.
  120.  

Code: Pascal  [Select][+][-]
  1. unit person;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. type
  8.  
  9.   { TPerson }
  10.  
  11.   TPerson = class(TObject)
  12.   private
  13.     FID: Integer;
  14.     FName: string;
  15.     FAge: Integer;
  16.   published
  17.     property ID: Integer read FID write FID;
  18.     property Name: string read FName write FName;
  19.     property Age: Integer read FAge write FAge;
  20.   end;
  21.  
  22. implementation
  23.  
  24. end.
  25.  
An excellent description with an example. I will add this to README https://github.com/pascal-libs/dopf/blob/main/README.md ?
« Last Edit: November 18, 2024, 11:09:32 pm by Renat.Su »

Thaddy

  • Hero Member
  • *****
  • Posts: 16420
  • Censorship about opinions does not belong here.
Re: dOPF - Object persistence framework / ORM
« Reply #13 on: November 18, 2024, 10:34:24 pm »
Right but an entity class for the dOPF library can be any, not necessarily a descendant of IInterfacedObject or TComponent, but for example, just TObject class
And that is exactly what I wanted and Warfley implemented. It is still a merge request, no warranty, but you can try it, if you know how to patch.
https://gitlab.com/freepascal.org/fpc/source/-/merge_requests/851 Warfley's implementation
https://gitlab.com/freepascal.org/fpc/source/-/issues/40983 my report

BTW Delphi does not support that either and my imagined code contains 1 small bug.
« Last Edit: November 18, 2024, 10:44:55 pm by Thaddy »
There is nothing wrong with being blunt. At a minimum it is also honest.

egsuh

  • Hero Member
  • *****
  • Posts: 1525
Re: dOPF - Object persistence framework / ORM
« Reply #14 on: November 20, 2024, 06:35:51 am »
Does this operate over the Internet?
Any other way to use DB over the Internet, not via WebService. Direct accessing DB (e.g. Firebird) over HTTP server?
Currently I have implemented sending/receiving dataset as a file over the web (not web service, normal web server).

 

TinyPortal © 2005-2018