Added MVCActiveRecord feature which allows to map a boolean object attribute to integer field on the actual table.

This commit is contained in:
Daniele Teti 2023-10-01 15:22:43 +02:00
parent 88127a7b44
commit ec9140f2a7
9 changed files with 135 additions and 8 deletions

View File

@ -601,6 +601,25 @@ type
end;
[MVCTable('integers_as_booleans')]
TIntegersAsBooleans = class(TMVCActiveRecord)
private
[MVCTableField('id', [foPrimaryKey, foAutoGenerated])]
FID: NullableInt64;
[MVCTableField('done_int', 'int2')]
FDoneAsInteger: Integer;
[MVCTableField('done_bool')]
FDoneAsBoolean: Boolean;
procedure SetDoneAsBoolean(const Value: Boolean);
procedure SetDoneAsInteger(const Value: Integer);
procedure SetID(const Value: NullableInt64);
public
property ID: NullableInt64 read FID write SetID;
property DoneAsBoolean: Boolean read FDoneAsBoolean write SetDoneAsBoolean;
property DoneAsInteger: Integer read FDoneAsInteger write SetDoneAsInteger;
end;
implementation
@ -785,4 +804,21 @@ begin
fFullName := GetFullName;
end;
{ TIntegersAsBooleans }
procedure TIntegersAsBooleans.SetDoneAsBoolean(const Value: Boolean);
begin
FDoneAsBoolean := Value;
end;
procedure TIntegersAsBooleans.SetDoneAsInteger(const Value: Integer);
begin
FDoneAsInteger := Value;
end;
procedure TIntegersAsBooleans.SetID(const Value: NullableInt64);
begin
FID := Value;
end;
end.

View File

@ -2,7 +2,7 @@ object MainForm: TMainForm
Left = 0
Top = 0
Caption = 'TMVCActiveRecord - ShowCase'
ClientHeight = 569
ClientHeight = 593
ClientWidth = 1104
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
@ -14,7 +14,7 @@ object MainForm: TMainForm
OnShow = FormShow
DesignSize = (
1104
569)
593)
TextHeight = 13
object btnCRUD: TButton
Left = 8
@ -38,7 +38,7 @@ object MainForm: TMainForm
Left = 280
Top = 8
Width = 816
Height = 553
Height = 577
Anchors = [akLeft, akTop, akRight, akBottom]
Ctl3D = True
DoubleBuffered = True
@ -56,7 +56,7 @@ object MainForm: TMainForm
WantReturns = False
WordWrap = False
ExplicitWidth = 812
ExplicitHeight = 552
ExplicitHeight = 576
end
object btnRelations: TButton
Left = 8
@ -285,6 +285,15 @@ object MainForm: TMainForm
TabOrder = 27
OnClick = btnVirtualEntitiesClick
end
object btnIntegersAsBool: TButton
Left = 8
Top = 522
Width = 121
Height = 34
Caption = 'Integers As Booleans'
TabOrder = 28
OnClick = btnIntegersAsBoolClick
end
object FDConnection1: TFDConnection
Left = 312
Top = 40

View File

@ -60,6 +60,7 @@ type
btnRefresh: TButton;
btnNamedQuery: TButton;
btnVirtualEntities: TButton;
btnIntegersAsBool: TButton;
procedure btnCRUDClick(Sender: TObject);
procedure btnInheritanceClick(Sender: TObject);
procedure btnMultiThreadingClick(Sender: TObject);
@ -90,6 +91,7 @@ type
procedure btnRefreshClick(Sender: TObject);
procedure btnNamedQueryClick(Sender: TObject);
procedure btnVirtualEntitiesClick(Sender: TObject);
procedure btnIntegersAsBoolClick(Sender: TObject);
private
procedure Log(const Value: string);
procedure LoadCustomers;
@ -388,10 +390,15 @@ begin
lCustWithGUID.LoadByPK(lIDGUID);
lCustWithGUID.Code.Value := '😉9012🙂';
lCustWithGUID.Update;
lCustWithGUID.GUID := TGUID.NewGuid;
lCustWithGUID.Insert;
finally
lCustWithGUID.Free;
end;
lCustWithGUID := TMVCActiveRecord.GetByPK<TCustomerWithGUID>(lIDGUID);
try
lCustWithGUID.Delete;
@ -481,6 +488,55 @@ begin
end;
end;
procedure TMainForm.btnIntegersAsBoolClick(Sender: TObject);
begin
Log('** Bool as Integer');
Log(' Only in the mapping layer it is possibile to map an integer field used ');
Log(' as boolean with values (0,1) as a boolean property');
Log(' --> (False is stored as 0, True is stored as 1) <--');
TMVCActiveRecord.DeleteAll(TIntegersAsBooleans);
for var I := 0 to 1 do
begin
for var b := False to True do
begin
var lTest1 := TIntegersAsBooleans.Create;
try
lTest1.DoneAsBoolean := b;
lTest1.DoneAsInteger := I;
lTest1.Store;
finally
lTest1.Free;
end;
end;
end;
{ ** WARNING **
While mapping layer recognize a boolean stored as integer, queries must still
use the actual type (integer) instead of the mapped types}
Assert(2 = TMVCActiveRecord.Count<TIntegersAsBooleans>('eq(doneasboolean,true)'));
Assert(2 = TMVCActiveRecord.Count<TIntegersAsBooleans>('eq(doneasinteger,1)')); {the boolean attribute as integer}
Assert(1 = TMVCActiveRecord.Count<TIntegersAsBooleans>('and(eq(doneasboolean,true),eq(doneasinteger,1))'));
Assert(1 = TMVCActiveRecord.Count<TIntegersAsBooleans>('and(eq(doneasboolean,false),eq(doneasinteger,0))'));
var lList := TMVCActiveRecord.SelectRQL<TIntegersAsBooleans>('sort(+id)', 10);
try
Assert(lList.Count = 4);
var lIdx := 0;
for var I := 0 to 1 do
begin
for var b := False to True do
begin
Assert(b = lList[lIdx].DoneAsBoolean);
Assert(I = lList[lIdx].DoneAsInteger);
Inc(lIdx);
end;
end;
finally
lList.Free;
end;
end;
procedure TMainForm.btnJSON_XML_TypesClick(Sender: TObject);
var
lCTypes: TComplexTypes;

Binary file not shown.

View File

@ -95,5 +95,11 @@ create table phones (
id_person integer not null references people(id)
);
create table integers_as_booleans (
id bigint generated by default as identity primary key,
done_bool boolean not null,
done_int smallint not null
);
ALTER TABLE orders ADD CONSTRAINT orders_customers_fk FOREIGN KEY (id_customer) REFERENCES customers(id) ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE order_details ADD CONSTRAINT order_details_orders_fk FOREIGN KEY (id_order) REFERENCES orders(id) ON DELETE CASCADE ON UPDATE CASCADE;

View File

@ -97,3 +97,8 @@ CREATE TABLE nullables_test (
f_blob varchar(max)
);
create table integers_as_booleans (
id bigint not null identity primary key,
done_bool bit not null,
done_int smallint not null
);

View File

@ -55,7 +55,7 @@ CREATE TABLE customers_with_code (
CREATE TABLE customers_with_guid (
idguid binary(16) NOT NULL,
idguid char(36) NOT NULL,
code varchar(20) NULL,
description varchar(200) NULL,
city varchar(200) NULL,
@ -109,5 +109,11 @@ create table phones (
id_person integer not null references people(id)
);
create table integers_as_booleans (
id bigint not null auto_increment primary key,
done_bool boolean not null,
done_int smallint not null
);
ALTER TABLE orders ADD CONSTRAINT orders_customers_fk FOREIGN KEY (id_customer) REFERENCES customers(id) ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE order_details ADD CONSTRAINT order_details_orders_fk FOREIGN KEY (id_order) REFERENCES orders(id) ON DELETE CASCADE ON UPDATE CASCADE;

View File

@ -392,6 +392,14 @@ CREATE TABLE public.phones (
ALTER TABLE public.phones OWNER TO postgres;
create table integers_as_booleans (
id bigint not null generated by default as identity primary key,
done_bool boolean not null default false,
done_int smallint not null default 0
);
--
-- TOC entry 217 (class 1259 OID 58912)
-- Name: phones_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres

View File

@ -1139,16 +1139,17 @@ begin
end;
ftInteger, ftSmallint, ftShortint, ftByte:
begin
// sqlite doesn't support boolean, so are identified as integers
// so we need to do some more checks...
// recognize "smallintegers" mapped to boolean attribute
if (aRTTIField.FieldType.TypeKind = tkEnumeration) and (aRTTIField.FieldType.Handle = TypeInfo(Boolean)) then
begin
aRTTIField.SetValue(AObject, AField.AsInteger = 1);
end
else if (aRTTIField.FieldType.TypeKind = tkEnumeration) then
// general enumerations
else if (aRTTIField.FieldType.TypeKind = tkEnumeration) then
begin
TValue(AField.AsInteger).ExtractRawData(PByte(Pointer(AObject)) + aRTTIField.Offset);
end
// plain integers
else
begin
aRTTIField.SetValue(AObject, AField.AsInteger);