Slides published for BeDelphi 2014 Event.
Create high performance Client Server ORM SOA REST MVC applications using Open Source Synopse mORMot framework and Delphi. Publish any SQL or NoSQL database content over JSON or XML: SQLite3, PostgreSQL, Oracle, MSSQL, FireBird, MongoDB. Define RESTful services using interfaces. Create MVC web applications, using Mustache templates. Running under Windows or Linux, with VCL/FMX clients on Mac OSX, Android or iOS/iPhone/iPad, or AJAX/PhoneGap.
15. Open Source
Started in 2008
3520 checkins since 2010
56 COCOMO years
http://synopse.info/fossil
http://github.com/synopse/mORMot
http://openhub.net/p/mormot
21. Hall of fame
Alexander (sha)
Alfred Glaenzer (alf)
Arnaud Bouchez
Aweste
Cheemeng
CoMPi
Damien (ddemars)
David Mead (MDW)
Delphinium
DigDiver
EMartin
Eric Grange
Esmond
Joe (jokusoft)
Jordi Tudela
Mario Moretti
Martin Suer
MChaos
Ondrej (reddwarf)
Pavel (mpv)
Pierre le Riche
RalfS
Sabbiolina
Sanyin
Sinisa (sinisav)
Sllimr7139
Vadim Orel
Win2014
Wolfgang Ehrhardt (via MPL)
22. Hall of fame
Alexander (sha)
Alfred Glaenzer (alf)
Arnaud Bouchez
Aweste
Cheemeng
CoMPi
Damien (ddemars)
David Mead (MDW)
Delphinium
DigDiver
EMartin
Eric Grange
Esmond
Joe (jokusoft)
Jordi Tudela
Mario Moretti
Martin Suer
MChaos
Ondrej (reddwarf)
Pavel (mpv)
Pierre le Riche
RalfS
Sabbiolina
Sanyin
Sinisa (sinisav)
Sllimr7139
Vadim Orel
Win2014
Wolfgang Ehrhardt (via MPL)
… YOU ?
23. mORMot & Friends
Open Source
Architecture & Design
Cross-Cutting features
DB Layer
ORM/ODM/SOA/MVC
Cross platform
Q&A
55. REST Interfaces
Verbs + URI vs
GET customer/123
POST customer
PUT order/456
DELETE order/456
Methods
GetCustomer
NewCustomer
ModifyOrder
DeleteOrder
91. Interfaces
Abstraction is your health
Publish classes as services
Test, mock
Write SOLID code
and manage memory for you
unless zeroing weak pointers
93. mORMot & Friends
Open Source
Architecture & Design
Cross-Cutting features
DB Layer
ORM/ODM/SOA/MVC
Cross platform
Q&A
94. Cross Cutting Features
UTF-8 JSON
TDynArray
TDocVariant
Logging
Testing
Compression
Crypto
PDF
Mustache
SpiderMonkey
Uncoupled features: could be used
without the ORM/SOA/MVC framework
95. SynCommons
UTF-8 JSON
From the ground up
With objects, records,
dynamic arrays, variants, any value
Performance and integration
96. SynCommons
TDynArray
Wrapper to an existing dynamic array
TList<> on steroids
e.g. sorting, search,
binary or JSON serialization
using enhanced RTTI if available
99. SynCommons
TDocVariant
var V: variant; // stored as any variant
...
TDocVariant.New(V);
// or slightly slower V := TDocVariant.New;
V.name := 'John';
// property accessed via late-binding
V.year := 1972;
// now V contains {"name":"john","year":1972}
100. SynCommons
TDocVariant
V.name := 'Mark'; // overwrite a property value
writeln(V.name); // will write 'Mark'
V.age := 12; // add a property to the object
writeln(V.age); // will write '12'
writeln(V); // implicit conversion to JSON string
// i.e. '{"name":"Mark","age":12}'
writeln(VariantSaveJSON(V1)); // as RawUTF8
102. TDocVariant + mORMot.pas
property Data: variant read fData write fData;
We store a TDocVariant in Data
to mutate a SQL database
into a NoSQL engine
103. TDocVariant + mORMot.pas
property Data: variant read fData write fData;
We store a TDocVariant in Data
to mutate a SQL database
into a NoSQL engine
104. TDocVariant + mORMot.pas
var aRec: TSQLRecordData;
aID: integer;
begin
// initialization of one record
aRec := TSQLRecordData.Create;
aRec.Name := 'Joe’;
// create a TDocVariant
aRec.data := _JSONFast('{name:"Joe",age:30}');
// or we can use this overloaded constructor
aRec := TSQLRecordData.Create(
['Joe',_ObjFast(['name','Joe','age',30])]);
105. TDocVariant + mORMot.pas
// now we can play with the data, e.g. via late-binding:
writeln(aRec.Name); // will write 'Joe'
writeln(aRec.Data); // write '{"name":"Joe","age":30}
// one year older
aRec.Data.age := aRec.Data.age+1;
// add a property to the schema
aRec.Data.interests := 'football';
aID := aClient.Add(aRec);
// we stored {"name":"Joe","age":31,"interests":"footbal"}
aRec.Free;
// now we can retrieve the data e.g. via aID
end;
107. TDocVariant + mORMot.pas
property Data: variant read fData write fData;
Data will be stored as TEXT
in the underlying RDBMS
Of course, if the database is a MongoDB engine,
the data will be stored as a true BSON document
135. SynDB
Direct RDBMS access layer
Not linked to DB.pas
Multi providers
UTF-8 JSON
Interface based
Knows SQL dialects
SynDBExplorer
136. SynDB
Not linked to DB.pas
Enter the 21th century
Less data types
By-pass slow TDataSet
Unicode even before Delphi 2009
Array binding
Native JSON support
138. SynDB
Connect to a DB
var Props: TSQLDBConnectionProperties;
...
Props := TOleDBMSSQLConnectionProperties.Create(
'.SQLEXPRESS','AdventureWorks2008R2','','');
try
UseProps(Props);
finally
Props.Free;
end;
139. SynDB
Execute statements
procedure UseProps(Props: TSQLDBConnectionProperties);
var I: ISQLDBRows;
begin
I := Props.Execute(
'select * from Sales.Customer '+
'where AccountNumber like ?',['AW000001%']);
while I.Step do
assert(Copy(I['AccountNumber'],1,8)='AW000001');
end;
144. SynDB
Remote access via HTTP
http.sys based server
SynLZ compression
Digital signature
Authentication
145. SynDB
Remote access via HTTP
Mutate SQLite3 into a
high performance Client-Server RDBMS
No library to deploy on Client side
Easy remote hosting on Server side
146. SynDB
Remote access via HTTP
Mutate SQLite3 into a
high performance Client-Server RDBMS
No library to deploy on Client side
Easy remote hosting on Server side
147. SynDBExplorer
Manage and request your DBs
Any supported database
High performance grid
Export to CSV or SQLite3
SQLite3 integrated
Remote server or client
165. TSQLModel
Define your data model
function CreateSampleModel: TSQLModel;
begin
result := TSQLModel.Create([TSQLSampleRecord]);
end;
Shared on both Client and Server side
166. Database: TSQLRest
CRUD Operations
procedure TForm1.FindButtonClick(Sender: TObject);
var Rec: TSQLSampleRecord;
begin
Rec := TSQLSampleRecord.Create(
Database,'Name=?',[StringToUTF8(NameEdit.Text)]);
try
if Rec.ID=0 then
QuestionMemo.Text := 'Not found' else
QuestionMemo.Text := UTF8ToString(Rec.Question);
finally
Rec.Free;
end;
end;
167. Database: TSQLRest
CRUD Operations
procedure TForm1.AddButtonClick(Sender: TObject);
var Rec: TSQLSampleRecord;
begin
Rec := TSQLSampleRecord.Create;
try
Rec.Name := StringToUTF8(NameEdit.Text);
Rec.Question := StringToUTF8(QuestionMemo.Text);
if Database.Add(Rec,true)=0 then
ShowMessage('Error adding the data') else begin
NameEdit.Text := '';
QuestionMemo.Text := '';
NameEdit.SetFocus;
end;
finally
Rec.Free;
end;
end;
174. TSQLRecord
Mapping by Convention
TSQLRecordPeopleExt = class(TSQLRecord)
..
published
property FirstName: RawUTF8 index 40
property LastName: RawUTF8 index 40
property Data: TSQLRawBlob
property YearOfBirth: integer
property YearOfDeath: word
property LastChange: TModTime
property CreatedAt: TCreateTime
end;
175. TSQLRecord
Mapping by configuration
Model := TSQLModel.Create([TSQLRecordPeopleExt],'root');
VirtualTableExternalRegister(
Model,TSQLRecordPeopleExt,Props,'Test.People');
176. TSQLRecord
Mapping by configuration
Model := TSQLModel.Create([TSQLRecordPeopleExt],'root');
VirtualTableExternalRegister(
Model,TSQLRecordPeopleExt,Props,'Test.People');
Model.Props[TSQLRecordPeopleExt].ExternalDB.
MapField('ID','Key').
MapField('YearOfDeath','YOD');
182. SOA
Define the contract
type
ICalculator = interface(IInvokable)
['{9A60C8ED-CEB2-4E09-87D4-4A16F496E5FE}']
/// add two signed 32 bit integers
function Add(n1,n2: integer): integer;
end;
183. SOA
Implement the contract
type
TServiceCalculator = class(TInterfacedObject, ICalculator)
public
function Add(n1,n2: integer): integer;
end;
function TServiceCalculator.Add(n1, n2: integer): integer;
begin
result := n1+n2;
end;
184. SOA
Publish the contract on the Server Side
RestServer.ServiceRegister(
TServiceCalculator,[TypeInfo(ICalculator)],sicShared);
Define the contract on the Client Side
RestServer.ServiceRegister(
[TypeInfo(ICalculator)],sicShared);
185. SOA
Use the service
var I: ICalculator;
begin
I := Rest.Services<ICalculator>;
if I<>nil then
result := I.Add(10,20);
end;
var I: ICalculator; // for older versions of Delphi
begin
if Rest.Services['Calculator'].Get(I) then
result := I.Add(10,20);
end;
On both client and server sides
192. Web Apps
Implement a Controller
method name → page name
var const params → URI params
var out params → Mustache context
193. Web Apps
Implement a Controller
procedure TBlogApplication.AuthorView(var ID: integer;
out Author: TSQLAuthor; out Articles: variant);
begin
RestModel.Retrieve(ID,Author);
Author.HashedPassword := ''; // no need to publish it
if Author.ID<>0 then
Articles := RestModel.RetrieveDocVariantArray(
TSQLArticle,'','Author=? order by RowId desc limit
50',[ID],ARTICLE_FIELDS) else
raise EMVCApplication.CreateGotoError(HTML_NOTFOUND);
End;
→ /blog/AuthorView?....
194. Web Apps
Implement a Controller
procedure TBlogApplication.AuthorView(var ID: integer;
out Author: TSQLAuthor; out Articles: variant);
http://localhost:8092/blog/mvc-info
→ /blog/AuthorView?ID=..[integer]..
{{Main}}: variant
{{ID}}: integer
{{Author}}: TSQLAuthor
{{Articles}}: variant
195. Web Apps
/blog/AuthorView?ID=123
procedure TBlogApplication.AuthorView(var ID: integer;
out Author: TSQLAuthor; out Articles: variant);
begin
→ ID = 123
RestModel.Retrieve(ID,Author);
Author.HashedPassword := ''; // no need to publish it
if Author.ID<>0 then
Articles := RestModel.RetrieveDocVariantArray(
TSQLArticle,'','Author=? order by RowId desc limit
50',[ID],ARTICLE_FIELDS) else
raise EMVCApplication.CreateGotoError(HTML_NOTFOUND);
end;
196. Web Apps
Mustache Data Context
procedure TBlogApplication.AuthorView(var ID: integer;
out Author: TSQLAuthor; out Articles: variant);
begin
RestModel.Retrieve(ID,Author);
Author.HashedPassword := ''; // no need to publish it
if Author.ID<>0 then
Articles := RestModel.RetrieveDocVariantArray(
TSQLArticle,'','Author=? order by RowId desc limit
50',[ID],ARTICLE_FIELDS) else
raise EMVCApplication.CreateGotoError(HTML_NOTFOUND);
end;
{{ID}} {{Author}} {{Articles}}