Daniele Teti 2023-08-08 14:31:23 +02:00
parent 4a27f8b64d
commit c201c916cb
4 changed files with 403 additions and 275 deletions

View File

@ -245,6 +245,8 @@ Congratulations to Daniele Teti and all the staff for the excellent work!" -- Ma
- ⚡ Wizard updated to be dotEnv aware - ⚡ Wizard updated to be dotEnv aware
- ⚡ Added "Load Style" methods to `TMVCActiveRecord` as suggested by https://github.com/danieleteti/delphimvcframework/issues/675
- ⚡ Better error message in case of serialization of `TArray<TObject>` - ⚡ Better error message in case of serialization of `TArray<TObject>`
- ⚡ Improved serialization of `TObjectList<TDataSet>` (however `ObjectDict` is still the preferred way to serialize multiple datasets). - ⚡ Improved serialization of `TObjectList<TDataSet>` (however `ObjectDict` is still the preferred way to serialize multiple datasets).

View File

@ -2,62 +2,49 @@ object MainForm: TMainForm
Left = 0 Left = 0
Top = 0 Top = 0
Caption = 'TMVCActiveRecord - ShowCase' Caption = 'TMVCActiveRecord - ShowCase'
ClientHeight = 1423 ClientHeight = 569
ClientWidth = 2760 ClientWidth = 1104
Color = clBtnFace Color = clBtnFace
Font.Charset = DEFAULT_CHARSET Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText Font.Color = clWindowText
Font.Height = -28 Font.Height = -11
Font.Name = 'Tahoma' Font.Name = 'Tahoma'
Font.Style = [] Font.Style = []
OnDestroy = FormDestroy OnDestroy = FormDestroy
OnShow = FormShow OnShow = FormShow
PixelsPerInch = 240
DesignSize = ( DesignSize = (
2760 1104
1423) 569)
TextHeight = 34 TextHeight = 13
object btnCRUD: TButton object btnCRUD: TButton
Left = 20 Left = 8
Top = 20 Top = 8
Width = 303 Width = 121
Height = 83 Height = 33
Margins.Left = 8
Margins.Top = 8
Margins.Right = 8
Margins.Bottom = 8
Caption = 'CRUD' Caption = 'CRUD'
TabOrder = 0 TabOrder = 0
OnClick = btnCRUDClick OnClick = btnCRUDClick
end end
object btnSelect: TButton object btnSelect: TButton
Left = 20 Left = 8
Top = 605 Top = 242
Width = 303 Width = 121
Height = 83 Height = 33
Margins.Left = 8
Margins.Top = 8
Margins.Right = 8
Margins.Bottom = 8
Caption = 'Queries' Caption = 'Queries'
TabOrder = 1 TabOrder = 1
OnClick = btnSelectClick OnClick = btnSelectClick
end end
object Memo1: TMemo object Memo1: TMemo
Left = 700 Left = 280
Top = 20 Top = 8
Width = 2040 Width = 816
Height = 1383 Height = 553
Margins.Left = 8
Margins.Top = 8
Margins.Right = 8
Margins.Bottom = 8
Anchors = [akLeft, akTop, akRight, akBottom] Anchors = [akLeft, akTop, akRight, akBottom]
Ctl3D = True Ctl3D = True
DoubleBuffered = True DoubleBuffered = True
Font.Charset = ANSI_CHARSET Font.Charset = ANSI_CHARSET
Font.Color = clWindowText Font.Color = clWindowText
Font.Height = -33 Font.Height = -13
Font.Name = 'Consolas' Font.Name = 'Consolas'
Font.Style = [] Font.Style = []
ParentCtl3D = False ParentCtl3D = False
@ -68,306 +55,214 @@ object MainForm: TMainForm
TabOrder = 2 TabOrder = 2
WantReturns = False WantReturns = False
WordWrap = False WordWrap = False
ExplicitWidth = 2020 ExplicitWidth = 812
ExplicitHeight = 1381 ExplicitHeight = 552
end end
object btnRelations: TButton object btnRelations: TButton
Left = 20 Left = 8
Top = 703 Top = 281
Width = 303 Width = 121
Height = 87 Height = 35
Margins.Left = 8
Margins.Top = 8
Margins.Right = 8
Margins.Bottom = 8
Caption = 'Relations' Caption = 'Relations'
TabOrder = 3 TabOrder = 3
OnClick = btnRelationsClick OnClick = btnRelationsClick
end end
object btnInheritance: TButton object btnInheritance: TButton
Left = 20 Left = 8
Top = 805 Top = 322
Width = 303 Width = 121
Height = 85 Height = 34
Margins.Left = 8
Margins.Top = 8
Margins.Right = 8
Margins.Bottom = 8
Caption = 'Inheritance' Caption = 'Inheritance'
TabOrder = 4 TabOrder = 4
OnClick = btnInheritanceClick OnClick = btnInheritanceClick
end end
object btnValidation: TButton object btnValidation: TButton
Left = 20 Left = 8
Top = 905 Top = 362
Width = 303 Width = 121
Height = 85 Height = 34
Margins.Left = 8
Margins.Top = 8
Margins.Right = 8
Margins.Bottom = 8
Caption = 'Validation' Caption = 'Validation'
TabOrder = 5 TabOrder = 5
OnClick = btnValidationClick OnClick = btnValidationClick
end end
object btnMultiThreading: TButton object btnMultiThreading: TButton
Left = 360 Left = 144
Top = 20 Top = 8
Width = 303 Width = 121
Height = 83 Height = 33
Margins.Left = 8
Margins.Top = 8
Margins.Right = 8
Margins.Bottom = 8
Caption = 'Multi Threading' Caption = 'Multi Threading'
TabOrder = 6 TabOrder = 6
OnClick = btnMultiThreadingClick OnClick = btnMultiThreadingClick
end end
object btnRQL: TButton object btnRQL: TButton
Left = 20 Left = 8
Top = 1005 Top = 402
Width = 303 Width = 121
Height = 85 Height = 34
Margins.Left = 8
Margins.Top = 8
Margins.Right = 8
Margins.Bottom = 8
Caption = 'RQL Query' Caption = 'RQL Query'
TabOrder = 7 TabOrder = 7
OnClick = btnRQLClick OnClick = btnRQLClick
end end
object btnReadOnlyFields: TButton object btnReadOnlyFields: TButton
Left = 20 Left = 8
Top = 508 Top = 203
Width = 303 Width = 121
Height = 82 Height = 33
Margins.Left = 8
Margins.Top = 8
Margins.Right = 8
Margins.Bottom = 8
Caption = 'CRUD With R/O Field' Caption = 'CRUD With R/O Field'
TabOrder = 8 TabOrder = 8
OnClick = btnReadOnlyFieldsClick OnClick = btnReadOnlyFieldsClick
end end
object btnNullTest: TButton object btnNullTest: TButton
Left = 360 Left = 144
Top = 118 Top = 47
Width = 303 Width = 121
Height = 82 Height = 33
Margins.Left = 8
Margins.Top = 8
Margins.Right = 8
Margins.Bottom = 8
Caption = 'Nullables' Caption = 'Nullables'
TabOrder = 9 TabOrder = 9
OnClick = btnNullTestClick OnClick = btnNullTestClick
end end
object btnCRUDNoAutoInc: TButton object btnCRUDNoAutoInc: TButton
Left = 20 Left = 8
Top = 215 Top = 86
Width = 303 Width = 121
Height = 83 Height = 33
Margins.Left = 8
Margins.Top = 8
Margins.Right = 8
Margins.Bottom = 8
Caption = 'CRUD (no autoinc)' Caption = 'CRUD (no autoinc)'
TabOrder = 10 TabOrder = 10
OnClick = btnCRUDNoAutoIncClick OnClick = btnCRUDNoAutoIncClick
end end
object btnCRUDWithStringPKs: TButton object btnCRUDWithStringPKs: TButton
Left = 20 Left = 8
Top = 313 Top = 125
Width = 303 Width = 121
Height = 82 Height = 33
Margins.Left = 8
Margins.Top = 8
Margins.Right = 8
Margins.Bottom = 8
Caption = 'CRUD (string pks)' Caption = 'CRUD (string pks)'
TabOrder = 11 TabOrder = 11
OnClick = btnCRUDWithStringPKsClick OnClick = btnCRUDWithStringPKsClick
end end
object btnWithSpaces: TButton object btnWithSpaces: TButton
Left = 20 Left = 8
Top = 410 Top = 164
Width = 303 Width = 121
Height = 83 Height = 33
Margins.Left = 8
Margins.Top = 8
Margins.Right = 8
Margins.Bottom = 8
Caption = 'CRUD (entity with spaces)' Caption = 'CRUD (entity with spaces)'
TabOrder = 12 TabOrder = 12
WordWrap = True WordWrap = True
OnClick = btnWithSpacesClick OnClick = btnWithSpacesClick
end end
object btnCountWithRQL: TButton object btnCountWithRQL: TButton
Left = 360 Left = 144
Top = 215 Top = 86
Width = 303 Width = 121
Height = 83 Height = 33
Margins.Left = 8
Margins.Top = 8
Margins.Right = 8
Margins.Bottom = 8
Caption = 'Count with RQL' Caption = 'Count with RQL'
TabOrder = 13 TabOrder = 13
OnClick = btnCountWithRQLClick OnClick = btnCountWithRQLClick
end end
object btnReadAndWriteOnly: TButton object btnReadAndWriteOnly: TButton
Left = 360 Left = 144
Top = 313 Top = 125
Width = 303 Width = 121
Height = 82 Height = 33
Margins.Left = 8
Margins.Top = 8
Margins.Right = 8
Margins.Bottom = 8
Caption = 'R/O, R/W' Caption = 'R/O, R/W'
TabOrder = 14 TabOrder = 14
OnClick = btnReadAndWriteOnlyClick OnClick = btnReadAndWriteOnlyClick
end end
object btnClientGeneratedPK: TButton object btnClientGeneratedPK: TButton
Left = 360 Left = 144
Top = 410 Top = 164
Width = 303 Width = 121
Height = 83 Height = 33
Margins.Left = 8
Margins.Top = 8
Margins.Right = 8
Margins.Bottom = 8
Caption = 'Client Generated PKs' Caption = 'Client Generated PKs'
TabOrder = 15 TabOrder = 15
OnClick = btnClientGeneratedPKClick OnClick = btnClientGeneratedPKClick
end end
object btnAttributes: TButton object btnAttributes: TButton
Left = 360 Left = 144
Top = 508 Top = 203
Width = 303 Width = 121
Height = 82 Height = 33
Margins.Left = 8
Margins.Top = 8
Margins.Right = 8
Margins.Bottom = 8
Caption = 'Attributes' Caption = 'Attributes'
TabOrder = 16 TabOrder = 16
OnClick = btnAttributesClick OnClick = btnAttributesClick
end end
object btnJSON_XML_Types: TButton object btnJSON_XML_Types: TButton
Left = 360 Left = 144
Top = 605 Top = 242
Width = 303 Width = 121
Height = 88 Height = 35
Margins.Left = 8
Margins.Top = 8
Margins.Right = 8
Margins.Bottom = 8
Caption = 'JSON && XML' Caption = 'JSON && XML'
TabOrder = 17 TabOrder = 17
OnClick = btnJSON_XML_TypesClick OnClick = btnJSON_XML_TypesClick
end end
object btnMerge: TButton object btnMerge: TButton
Left = 360 Left = 144
Top = 708 Top = 283
Width = 303 Width = 121
Height = 85 Height = 34
Margins.Left = 8
Margins.Top = 8
Margins.Right = 8
Margins.Bottom = 8
Caption = 'Merge' Caption = 'Merge'
TabOrder = 18 TabOrder = 18
OnClick = btnMergeClick OnClick = btnMergeClick
end end
object btnTableFilter: TButton object btnTableFilter: TButton
Left = 360 Left = 144
Top = 808 Top = 323
Width = 303 Width = 121
Height = 85 Height = 34
Margins.Left = 8
Margins.Top = 8
Margins.Right = 8
Margins.Bottom = 8
Caption = 'Table Filter' Caption = 'Table Filter'
TabOrder = 19 TabOrder = 19
OnClick = btnTableFilterClick OnClick = btnTableFilterClick
end end
object btnPartitioning: TButton object btnPartitioning: TButton
Left = 360 Left = 144
Top = 908 Top = 363
Width = 303 Width = 121
Height = 82 Height = 33
Margins.Left = 8
Margins.Top = 8
Margins.Right = 8
Margins.Bottom = 8
Caption = 'Table Partitioning' Caption = 'Table Partitioning'
TabOrder = 20 TabOrder = 20
OnClick = btnPartitioningClick OnClick = btnPartitioningClick
end end
object btnCRUDWithGUID: TButton object btnCRUDWithGUID: TButton
Left = 20 Left = 8
Top = 118 Top = 47
Width = 303 Width = 121
Height = 82 Height = 33
Margins.Left = 8
Margins.Top = 8
Margins.Right = 8
Margins.Bottom = 8
Caption = 'CRUD (with GUID PK)' Caption = 'CRUD (with GUID PK)'
TabOrder = 21 TabOrder = 21
OnClick = btnCRUDWithGUIDClick OnClick = btnCRUDWithGUIDClick
end end
object btnOOP: TButton object btnOOP: TButton
Left = 360 Left = 144
Top = 1005 Top = 402
Width = 303 Width = 121
Height = 85 Height = 34
Margins.Left = 8
Margins.Top = 8
Margins.Right = 8
Margins.Bottom = 8
Caption = 'OOP with Partitioning and Filtering' Caption = 'OOP with Partitioning and Filtering'
TabOrder = 22 TabOrder = 22
WordWrap = True WordWrap = True
OnClick = btnOOPClick OnClick = btnOOPClick
end end
object btnReadOnly: TButton object btnReadOnly: TButton
Left = 20 Left = 8
Top = 1105 Top = 442
Width = 303 Width = 121
Height = 85 Height = 34
Margins.Left = 8
Margins.Top = 8
Margins.Right = 8
Margins.Bottom = 8
Caption = 'Read/Only Entities' Caption = 'Read/Only Entities'
TabOrder = 23 TabOrder = 23
OnClick = btnReadOnlyClick OnClick = btnReadOnlyClick
end end
object btnSpeed: TButton object btnSpeed: TButton
Left = 20 Left = 8
Top = 1205 Top = 482
Width = 303 Width = 121
Height = 85 Height = 34
Margins.Left = 8
Margins.Top = 8
Margins.Right = 8
Margins.Bottom = 8
Caption = 'Metadata Speed Test' Caption = 'Metadata Speed Test'
TabOrder = 24 TabOrder = 24
OnClick = btnSpeedClick OnClick = btnSpeedClick
end end
object btnRefresh: TButton object btnRefresh: TButton
Left = 360 Left = 144
Top = 1105 Top = 442
Width = 303 Width = 121
Height = 85 Height = 34
Margins.Left = 8
Margins.Top = 8
Margins.Right = 8
Margins.Bottom = 8
Caption = 'Manual Refresh' Caption = 'Manual Refresh'
TabOrder = 25 TabOrder = 25
OnClick = btnRefreshClick OnClick = btnRefreshClick

View File

@ -1040,6 +1040,7 @@ var
lItem: TMVCActiveRecord; lItem: TMVCActiveRecord;
lCustomer: TCustomer; lCustomer: TCustomer;
lCustList: TObjectList<TCustomer>; lCustList: TObjectList<TCustomer>;
lRecCount: Integer;
const const
cRQL1 = 'in(City,["Rome","London"]);sort(+code);limit(0,50)'; cRQL1 = 'in(City,["Rome","London"]);sort(+code);limit(0,50)';
cRQL2 = 'and(eq(City,"Rome"),or(contains(CompanyName,"GAS"),contains(CompanyName,"Motors")))'; cRQL2 = 'and(eq(City,"Rome"),or(contains(CompanyName,"GAS"),contains(CompanyName,"Motors")))';
@ -1133,6 +1134,114 @@ begin
lList.Free; lList.Free;
end; end;
//******************************************************
// Using "Load" methods ********************************
//******************************************************
Log('*************************************************');
Log('** RQL Queries Test (using "Load" style methods)');
Log('*************************************************');
Log('>> RQL Query (1) - ' + cRQL1);
lList := TMVCActiveRecordList.Create;
try
TMVCActiveRecord.SelectRQL(TCustomer, cRQL1, 20, lList);
Log(lList.Count.ToString + ' record/s found');
for lItem in lList do
begin
lCustomer := TCustomer(lItem);
Log(Format('%5s - %s (%s)', [lCustomer.Code.ValueOrDefault,
lCustomer.CompanyName.ValueOrDefault, lCustomer.City]));
end;
finally
lList.Free;
end;
Log('>> RQL Query (2) - ' + cRQL2);
lCustList := TObjectList<TCustomer>.Create;
try
lRecCount := TMVCActiveRecord.SelectRQL<TCustomer>(cRQL2, 20, lCustList);
Log(lRecCount.ToString + ' record/s found');
for lCustomer in lCustList do
begin
Log(Format('%5s - %s (%s)', [lCustomer.Code.ValueOrDefault,
lCustomer.CompanyName.ValueOrDefault, lCustomer.City]));
end;
finally
lCustList.Free;
end;
Log('**RQL Query (3) - ' + cRQL2);
lList := TMVCActiveRecordList.Create;
try
lRecCount := TMVCActiveRecord.SelectRQL(TCustomer, cRQL2, 20, lList);
Log(lRecCount.ToString + ' record/s found');
for lItem in lList do
begin
lCustomer := TCustomer(lItem);
Log(Format('%5s - %s (%s)', [lCustomer.Code.ValueOrDefault,
lCustomer.CompanyName.ValueOrDefault, lCustomer.City]));
end;
finally
lList.Free;
end;
Log('**RQL Query (4) - <empty> with limit 20');
lList := TMVCActiveRecordList.Create;
try
lRecCount := TMVCActiveRecord.SelectRQL(TCustomer, '', 20, lList);
Log(lRecCount.ToString + ' record/s found');
Assert(lRecCount = 20);
Assert(lList.Count = lRecCount);
finally
lList.Free;
end;
Log('**RQL Query (5) - <empty> sort by code with limit 20');
lList := TMVCActiveRecordList.Create;
try
lRecCount := TMVCActiveRecord.SelectRQL(TCustomer, 'sort(+code)', 20, lList);
Log(lRecCount.ToString + ' record/s found');
Assert(lRecCount = lList.Count);
Assert(lList.Count = 20);
finally
lList.Free;
end;
Log('**RQL Query (6) - <empty> with limit 10');
lList := TMVCActiveRecordList.Create;
try
lRecCount := TMVCActiveRecord.SelectRQL(TCustomer, '', 10, lList);
Log(lList.Count.ToString + ' record/s found');
Assert(lRecCount = lList.Count);
Assert(lList.Count = 10);
finally
lList.Free;
end;
Log('**RQL Query (7) - <empty> with limit 1');
lList := TMVCActiveRecordList.Create;
try
lRecCount := TMVCActiveRecord.SelectRQL(TCustomer, '', 1, lList);
Log(lList.Count.ToString + ' record/s found');
Assert(lList.Count = 1);
Assert(lRecCount = lList.Count);
finally
lList.Free;
end;
Log('**RQL Query (8) - <empty> with limit 0');
lList := TMVCActiveRecordList.Create;
try
lRecCount := TMVCActiveRecord.SelectRQL(TCustomer, '', 0, lList);
Log(lList.Count.ToString + ' record/s found');
Assert(lList.Count = 0);
Assert(lRecCount = lList.Count);
finally
lList.Free;
end;
end; end;
procedure TMainForm.btnSelectClick(Sender: TObject); procedure TMainForm.btnSelectClick(Sender: TObject);

View File

@ -333,7 +333,9 @@ type
function InternalCount(const RQL: string): int64; function InternalCount(const RQL: string): int64;
function InternalSelectRQL(const RQL: string; const MaxRecordCount: Integer) function InternalSelectRQL(const RQL: string; const MaxRecordCount: Integer)
: TMVCActiveRecordList; : TMVCActiveRecordList; overload;
function InternalSelectRQL(const RQL: string; const MaxRecordCount: Integer;
const OutList: TMVCActiveRecordList): UInt32; overload;
public public
constructor Create(aLazyLoadConnection: Boolean); overload; constructor Create(aLazyLoadConnection: Boolean); overload;
{ cannot be virtual! } { cannot be virtual! }
@ -392,18 +394,33 @@ type
class function Select(const aClass: TMVCActiveRecordClass; const SQL: string; class function Select(const aClass: TMVCActiveRecordClass; const SQL: string;
const Params: array of Variant; const Params: array of Variant;
const Connection: TFDConnection): TMVCActiveRecordList; overload; const Connection: TFDConnection): TMVCActiveRecordList; overload;
class function Select(const aClass: TMVCActiveRecordClass; const SQL: string;
const Params: array of Variant;
const Connection: TFDConnection; const OutList: TMVCActiveRecordList): UInt32; overload;
class function SelectRQL(const aClass: TMVCActiveRecordClass; const RQL: string; class function SelectRQL(const aClass: TMVCActiveRecordClass; const RQL: string;
const MaxRecordCount: Integer) const MaxRecordCount: Integer)
: TMVCActiveRecordList; overload; : TMVCActiveRecordList; overload;
class function SelectRQL(const aClass: TMVCActiveRecordClass; const RQL: string;
const MaxRecordCount: Integer; const OutList: TMVCActiveRecordList): UInt32; overload;
class function DeleteRQL(const aClass: TMVCActiveRecordClass; const RQL: string): int64; class function DeleteRQL(const aClass: TMVCActiveRecordClass; const RQL: string): int64;
function SelectRQL(const RQL: string; const MaxRecordCount: Integer) function SelectRQL(const RQL: string; const MaxRecordCount: Integer)
: TMVCActiveRecordList; overload; : TMVCActiveRecordList; overload;
class function Where(const aClass: TMVCActiveRecordClass; const SQLWhere: string; class function Where(
const aClass: TMVCActiveRecordClass;
const SQLWhere: string;
const Params: array of Variant) const Params: array of Variant)
: TMVCActiveRecordList; overload; : TMVCActiveRecordList; overload;
class function Where(const aClass: TMVCActiveRecordClass; const SQLWhere: string; class function Where(
const aClass: TMVCActiveRecordClass;
const SQLWhere: string;
const Params: array of Variant; const Params: array of Variant;
const Connection: TFDConnection): TMVCActiveRecordList; overload; const Connection: TFDConnection): TMVCActiveRecordList; overload;
class function Where(
const aClass: TMVCActiveRecordClass;
const SQLWhere: string;
const Params: array of Variant;
const Connection: TFDConnection;
const OutList: TMVCActiveRecordList): UInt32; overload;
class function All(const aClass: TMVCActiveRecordClass): TObjectList<TMVCActiveRecord>; class function All(const aClass: TMVCActiveRecordClass): TObjectList<TMVCActiveRecord>;
overload; overload;
class function DeleteAll(const aClass: TMVCActiveRecordClass): int64; overload; class function DeleteAll(const aClass: TMVCActiveRecordClass): int64; overload;
@ -451,13 +468,27 @@ type
const RaiseExceptionIfNotFound: Boolean = True): T; overload; const RaiseExceptionIfNotFound: Boolean = True): T; overload;
class function GetByPK<T: TMVCActiveRecord, constructor>(const aValue: TGuid; class function GetByPK<T: TMVCActiveRecord, constructor>(const aValue: TGuid;
const RaiseExceptionIfNotFound: Boolean = True): T; overload; const RaiseExceptionIfNotFound: Boolean = True): T; overload;
/// <summary>
/// Returns a TObjectList<TMVCActiveRecord> from a SQL using variant params
/// </summary>
class function Select<T: TMVCActiveRecord, constructor>(const SQL: string; class function Select<T: TMVCActiveRecord, constructor>(const SQL: string;
const Params: array of Variant; const Params: array of Variant;
const Options: TMVCActiveRecordLoadOptions = []): TObjectList<T>; overload; const Options: TMVCActiveRecordLoadOptions = []): TObjectList<T>; overload;
/// <summary>
/// Returns a TObjectList<TMVCActiveRecord> from a SQL using typed params
/// </summary>
class function Select<T: TMVCActiveRecord, constructor>(const SQL: string; class function Select<T: TMVCActiveRecord, constructor>(const SQL: string;
const Params: array of Variant; const Params: array of Variant;
const ParamTypes: array of TFieldType; const Options: TMVCActiveRecordLoadOptions = []) const ParamTypes: array of TFieldType; const Options: TMVCActiveRecordLoadOptions = [])
: TObjectList<T>; overload; : TObjectList<T>; overload;
/// <summary>
/// Fills a TObjectList<TMVCActiveRecord> from a SQL using typed params.
/// Returns number of the records in the list (not only the selected records, but the current .Count of the list)
/// </summary>
class function Select<T: TMVCActiveRecord, constructor>(const SQL: string; const Params: array of Variant;
const ParamTypes: array of TFieldType; const Options: TMVCActiveRecordLoadOptions;
const OutList: TObjectList<T>): UInt32; overload;
class function SelectOne<T: TMVCActiveRecord, constructor>(const SQL: string; class function SelectOne<T: TMVCActiveRecord, constructor>(const SQL: string;
const Params: array of Variant; const Params: array of Variant;
const ParamTypes: array of TFieldType; const Options: TMVCActiveRecordLoadOptions = []; const ParamTypes: array of TFieldType; const Options: TMVCActiveRecordLoadOptions = [];
@ -468,6 +499,8 @@ type
class function SelectRQL<T: constructor, TMVCActiveRecord>(const RQL: string; class function SelectRQL<T: constructor, TMVCActiveRecord>(const RQL: string;
const MaxRecordCount: Integer) const MaxRecordCount: Integer)
: TObjectList<T>; overload; : TObjectList<T>; overload;
class function SelectRQL<T: constructor, TMVCActiveRecord>(const RQL: string;
const MaxRecordCount: Integer; const OutList: TObjectList<T>): UInt32; overload;
class function SelectOneByRQL<T: constructor, TMVCActiveRecord>(const RQL: string; class function SelectOneByRQL<T: constructor, TMVCActiveRecord>(const RQL: string;
const RaiseExceptionIfNotFound: Boolean = True): T; overload; const RaiseExceptionIfNotFound: Boolean = True): T; overload;
class function All<T: TMVCActiveRecord, constructor>: TObjectList<T>; overload; class function All<T: TMVCActiveRecord, constructor>: TObjectList<T>; overload;
@ -483,6 +516,10 @@ type
class function Where<T: TMVCActiveRecord, constructor>(const SQLWhere: string; class function Where<T: TMVCActiveRecord, constructor>(const SQLWhere: string;
const Params: array of Variant; const Params: array of Variant;
const ParamTypes: array of TFieldType): TObjectList<T>; overload; const ParamTypes: array of TFieldType): TObjectList<T>; overload;
class function Where<T: TMVCActiveRecord, constructor>(const SQLWhere: string;
const Params: array of Variant;
const ParamTypes: array of TFieldType;
const OutList: TObjectList<T>): UInt32; overload;
class function GetOneByWhere<T: TMVCActiveRecord, constructor>(const SQLWhere: string; class function GetOneByWhere<T: TMVCActiveRecord, constructor>(const SQLWhere: string;
const Params: array of Variant; const RaiseExceptionIfNotFound: Boolean = True): T; overload; const Params: array of Variant; const RaiseExceptionIfNotFound: Boolean = True): T; overload;
class function GetOneByWhere<T: TMVCActiveRecord, constructor>(const SQLWhere: string; class function GetOneByWhere<T: TMVCActiveRecord, constructor>(const SQLWhere: string;
@ -1419,6 +1456,16 @@ begin
Result := GetScalar(lSQL, []); Result := GetScalar(lSQL, []);
end; end;
function TMVCActiveRecord.InternalSelectRQL(const RQL: string;
const MaxRecordCount: Integer; const OutList: TMVCActiveRecordList): UInt32;
var
lSQL: string;
begin
lSQL := SQLGenerator.CreateSQLWhereByRQL(RQL, GetMapping, True, false, MaxRecordCount);
LogD(Format('RQL [%s] => SQL [%s]', [RQL, lSQL]));
Result := Where(TMVCActiveRecordClass(Self.ClassType), lSQL, [], nil, OutList);
end;
function TMVCActiveRecord.InternalSelectRQL(const RQL: string; const MaxRecordCount: Integer): TMVCActiveRecordList; function TMVCActiveRecord.InternalSelectRQL(const RQL: string; const MaxRecordCount: Integer): TMVCActiveRecordList;
var var
lSQL: string; lSQL: string;
@ -1657,6 +1704,52 @@ begin
end; end;
end; end;
class function TMVCActiveRecordHelper.SelectRQL<T>(const RQL: string;
const MaxRecordCount: Integer; const OutList: TObjectList<T>): UInt32;
var
lAR: TMVCActiveRecord;
lSQL: string;
begin
lAR := T.Create;
try
lSQL := lAR.SQLGenerator.CreateSQLWhereByRQL(RQL, lAR.GetMapping, MaxRecordCount > -1, false, MaxRecordCount).Trim;
lSQL := TMVCSQLGenerator.RemoveInitialWhereKeyword(lSQL);
Result := Where<T>(lSQL, [], [], OutList);
finally
lAR.Free;
end;
end;
class function TMVCActiveRecordHelper.Where<T>(const SQLWhere: string;
const Params: array of Variant; const ParamTypes: array of TFieldType;
const OutList: TObjectList<T>): UInt32;
var
lAR: TMVCActiveRecord;
lFilter: string;
begin
lAR := T.Create;
try
lFilter := lAR.SQLGenerator.GetDefaultSQLFilter(True);
if SQLWhere.Trim.IsEmpty() or SQLWhere.Trim.StartsWith('/*limit*/') or SQLWhere.Trim.StartsWith('/*sort*/') then
begin
Result := Select<T>(lAR.GenerateSelectSQL + lFilter + SQLWhere, Params, ParamTypes, [], OutList);
end
else
begin
if lFilter.IsEmpty then
begin
Result := Select<T>(lAR.GenerateSelectSQL + ' WHERE ' + SQLWhere, Params, ParamTypes, [], OutList);
end
else
begin
Result := Select<T>(lAR.GenerateSelectSQL + lFilter + ' AND ' + SQLWhere, Params, ParamTypes, [], OutList);
end;
end;
finally
lAR.Free;
end;
end;
function TMVCActiveRecord.GetPartitionInfo: TPartitionInfo; function TMVCActiveRecord.GetPartitionInfo: TPartitionInfo;
var var
lRQLCompilerClass: TRQLCompilerClass; lRQLCompilerClass: TRQLCompilerClass;
@ -2454,29 +2547,14 @@ end;
class function TMVCActiveRecord.Select(const aClass: TMVCActiveRecordClass; const SQL: string; class function TMVCActiveRecord.Select(const aClass: TMVCActiveRecordClass; const SQL: string;
const Params: array of Variant; const Connection: TFDConnection): TMVCActiveRecordList; const Params: array of Variant; const Connection: TFDConnection): TMVCActiveRecordList;
var
lDataSet: TDataSet;
lAR: TMVCActiveRecord;
begin begin
Result := TMVCActiveRecordList.Create; Result := TMVCActiveRecordList.Create;
try try
lDataSet := ExecQuery(SQL, Params, Connection, True, False); Select(aClass, SQL, Params, Connection, Result);
try
while not lDataSet.Eof do
begin
lAR := aClass.Create;
Result.Add(lAR);
lAR.LoadByDataset(lDataSet);
lDataSet.Next;
end;
finally
lDataSet.Free;
end;
except except
Result.Free; Result.Free;
raise; raise;
end; end;
end; end;
class function TMVCActiveRecord.SelectDataSet(const SQL: string; const Params: array of Variant; class function TMVCActiveRecord.SelectDataSet(const SQL: string; const Params: array of Variant;
@ -2502,6 +2580,41 @@ begin
Result := InternalSelectRQL(RQL, MaxRecordCount); Result := InternalSelectRQL(RQL, MaxRecordCount);
end; end;
class function TMVCActiveRecord.SelectRQL(const aClass: TMVCActiveRecordClass;
const RQL: string; const MaxRecordCount: Integer;
const OutList: TMVCActiveRecordList): UInt32;
var
lAR: TMVCActiveRecord;
begin
lAR := aClass.Create(True);
try
Result := lAR.InternalSelectRQL(RQL, MaxRecordCount, OutList);
finally
lAR.Free;
end;
end;
class function TMVCActiveRecordHelper.Select<T>(const SQL: string; const Params: array of Variant;
const ParamTypes: array of TFieldType; const Options: TMVCActiveRecordLoadOptions; const OutList: TObjectList<T>): UInt32;
var
lDataSet: TDataSet;
lAR: TMVCActiveRecord;
begin
lDataSet := ExecQuery(SQL, Params, ParamTypes, True, False);
try
while not lDataSet.Eof do
begin
lAR := T.Create;
OutList.Add(lAR);
lAR.LoadByDataset(lDataSet, Options);
lDataSet.Next;
end;
Result := OutList.Count;
finally
lDataSet.Free;
end;
end;
class function TMVCActiveRecordHelper.Select<T>(const SQL: string; const Params: array of Variant; class function TMVCActiveRecordHelper.Select<T>(const SQL: string; const Params: array of Variant;
const ParamTypes: array of TFieldType; const Options: TMVCActiveRecordLoadOptions): TObjectList<T>; const ParamTypes: array of TFieldType; const Options: TMVCActiveRecordLoadOptions): TObjectList<T>;
var var
@ -2511,18 +2624,7 @@ var
begin begin
Result := TObjectList<T>.Create(True); Result := TObjectList<T>.Create(True);
try try
lDataSet := ExecQuery(SQL, Params, ParamTypes, True, False); Select<T>(SQL, Params, ParamTypes, Options, Result);
try
while not lDataSet.Eof do
begin
lAR := T.Create;
Result.Add(lAR);
lAR.LoadByDataset(lDataSet, Options);
lDataSet.Next;
end;
finally
lDataSet.Free;
end;
except except
Result.Free; Result.Free;
raise; raise;
@ -2581,28 +2683,13 @@ end;
class function TMVCActiveRecordHelper.Where<T>(const SQLWhere: string; const Params: array of Variant; class function TMVCActiveRecordHelper.Where<T>(const SQLWhere: string; const Params: array of Variant;
const ParamTypes: array of TFieldType): TObjectList<T>; const ParamTypes: array of TFieldType): TObjectList<T>;
var
lAR: TMVCActiveRecord;
lFilter: string;
begin begin
lAR := T.Create; Result := TObjectList<T>.Create(True);
try try
lFilter := lAR.SQLGenerator.GetDefaultSQLFilter(True); Where<T>(SQLWhere, Params, ParamTypes, Result);
if SQLWhere.Trim.IsEmpty() or SQLWhere.Trim.StartsWith('/*limit*/') or SQLWhere.Trim.StartsWith('/*sort*/') then except
begin Result.Free;
Result := Select<T>(lAR.GenerateSelectSQL + lFilter + SQLWhere, Params, ParamTypes) raise;
end
else
begin
if lFilter.IsEmpty then
Result := Select<T>(lAR.GenerateSelectSQL + ' WHERE ' + SQLWhere, Params, ParamTypes)
else
begin
Result := Select<T>(lAR.GenerateSelectSQL + lFilter + ' AND ' + SQLWhere, Params, ParamTypes);
end;
end;
finally
lAR.Free;
end; end;
end; end;
@ -3000,6 +3087,20 @@ begin
OnAfterInsertOrUpdate; OnAfterInsertOrUpdate;
end; end;
class function TMVCActiveRecord.Where(const aClass: TMVCActiveRecordClass;
const SQLWhere: string; const Params: array of Variant;
const Connection: TFDConnection; const OutList: TMVCActiveRecordList): UInt32;
var
lAR: TMVCActiveRecord;
begin
lAR := aClass.Create;
try
Result := Select(aClass, lAR.GenerateSelectSQL + SQLWhere, Params, Connection, OutList);
finally
lAR.Free;
end;
end;
procedure TMVCActiveRecord.AddChildren(const ChildObject: TObject); procedure TMVCActiveRecord.AddChildren(const ChildObject: TObject);
begin begin
if fChildren = nil then if fChildren = nil then
@ -3051,14 +3152,13 @@ end;
class function TMVCActiveRecord.Where(const aClass: TMVCActiveRecordClass; const SQLWhere: string; class function TMVCActiveRecord.Where(const aClass: TMVCActiveRecordClass; const SQLWhere: string;
const Params: array of Variant; const Connection: TFDConnection): TMVCActiveRecordList; const Params: array of Variant; const Connection: TFDConnection): TMVCActiveRecordList;
var
lAR: TMVCActiveRecord;
begin begin
lAR := aClass.Create; Result := TMVCActiveRecordList.Create;
try try
Result := Select(aClass, lAR.GenerateSelectSQL + SQLWhere, Params, Connection); Where(aClass, SQLWhere, Params, Connection, Result);
finally except
lAR.Free; Result.Free;
raise;
end; end;
end; end;
@ -4063,6 +4163,28 @@ begin
inherited; inherited;
end; end;
class function TMVCActiveRecord.Select(const aClass: TMVCActiveRecordClass;
const SQL: string; const Params: array of Variant;
const Connection: TFDConnection; const OutList: TMVCActiveRecordList): UInt32;
var
lDataSet: TDataSet;
lAR: TMVCActiveRecord;
begin
lDataSet := ExecQuery(SQL, Params, Connection, True, False);
try
while not lDataSet.Eof do
begin
lAR := aClass.Create;
OutList.Add(lAR);
lAR.LoadByDataset(lDataSet);
lDataSet.Next;
end;
Result := OutList.Count;
finally
lDataSet.Free;
end;
end;
initialization initialization
gConnectionsLock := TObject.Create; gConnectionsLock := TObject.Create;