FireDAC 映射规则不适用于参数?
FireDAC mapping rules do not apply to parameters?
我有一个 TFDConnection
到 FireBird 数据库,我申请 Data type mapping 以向后兼容以前的数据访问技术 (SQLDirect):
with FormatOptions.MapRules.Add do // TIMESTAMP will be ftDateTime instead of ftTimeStamp
begin
SourceDataType := dtDateTimeStamp;
TargetDataType := dtDateTime;
end;
with FormatOptions.MapRules.Add do // FLOAT will be ftFloat instead of ftSingle
begin
SourceDataType := dtSingle;
TargetDataType := dtDouble;
end;
FormatOptions.OwnMapRules := true;
在运行时我创建了一个 TFDQuery,我 link 到那个 TFDConnection。
我可以看到它继承了映射规则:FormatOptions.MapRules.count=2
我将一个 INSERT 查询分配给它的 SQL.Text:
insert into TT_ACT (TT_ACT_ID,TT_PARENT_ID,TT_FROMDATE,TT_TODATE,TT_NAME,TT_NR,TT_CODE,TT_GROUP...)
values (:TT_ACT_ID,:TT_PARENT_ID,:TT_FROMDATE,:TT_TODATE,:TT_NAME,:TT_NR,:TT_CODE,:TT_GROUP,...)
这给了我 params.count=42
数据类型为 ftUnknown 的参数(当然)。
然后我调用准备查询。
如果我现在检查一个已知的日期时间参数,我会看到 params[x].datatype = ftTimeStamp
,而不是 ftDateTime
。
所以查询回数据库看字段的时候,设置参数的时候好像没有听数据映射规则
这是一个错误吗?
在我的代码的后期,这让我遇到了麻烦,导致了著名的 338 错误:
[FireDac][Phys][IB]-338 Param [TT_FROMDATE] type changed from [ftSQLTimeStamp] to [ftDateTime]. Query must be reprepared.
我设法解决了这个错误,所以这不是问题的一部分。但我希望 Params 也遵循数据类型映射规则,这会让这一切变得更容易。
您刚刚错误定义了映射规则定义。对于参数来说,就是target到source的转换。 Data Type Mapping 主题也这么说:
In case of a command parameter, the rule defines a transformation of a
target data type, specified by an application, into a source data
type, supported by a driver.
因此,要从 TIMESTAMP to dtDateTime and FLOAT to dtDouble 映射命令参数,只需在您的定义中将源与目标交换即可:
{ FLOAT → dtDouble in parameters }
with FormatOptions.MapRules.Add do
begin
SourceDataType := dtDouble; { TFDParam.DataType }
TargetDataType := dtSingle; { Firebird FLOAT }
end;
{ TIMESTAMP → dtDateTime in parameters }
with FormatOptions.MapRules.Add do
begin
SourceDataType := dtDateTime; { TFDParam.DataType }
TargetDataType := dtDateTimeStamp; { Firebird TIMESTAMP }
end;
{ enable custom map rules }
FormatOptions.OwnMapRules := True;
值得补充的是,参数的映射规则是唯一的。它们仅在准备命令时为参数映射数据类型(数据类型必须是可确定的)。它们不会在传递给驱动程序时转换参数值。考虑这段代码:
{ Firebird FLOAT equals to dtSingle data type, map it to dtDouble }
with FDQuery1.FormatOptions.MapRules.Add do
begin
SourceDataType := dtDouble;
TargetDataType := dtSingle;
end;
FDQuery1.FormatOptions.OwnMapRules := True;
{ setup the command; MyFloat field is Firebird FLOAT }
FDQuery1.SQL.Text := 'INSERT INTO MyTable (MyFloat) VALUES (:MyFloat)';
{ rules are applied when preparing command, so let's prepare it }
FDQuery1.Prepare;
{ now the parameter data type should be dtDouble instead of dtSingle }
if FDQuery1.ParamByName('MyFloat').DataType = dtDouble then
ShowMessage('Parameter is of dtDouble data type');
{ but you can easily change the parameter data type to another, e.g. by mistake;
this will change data type to dtSingle, so the whole mapping effort is lost }
FDQuery1.ParamByName('MyFloat').AsSingle := 1.2345;
{ if this would execute (which does not because the parameter data type has been
changed since the command preparation), parameter map rules would still not be
involved in converting parameter value for the driver }
FDQuery1.ExecSQL;
所以正如您所看到的,这几乎是白费力气(仅将确定的参数数据类型更改为另一种)。无论映射规则如何,参数值都会自动转换。因此,即使您的参数数据类型与 DBMS 数据类型不匹配但可以转换,无论如何 FireDAC 都会简单地为您转换它(这个魔法在 ConvertRawData 方法中):
{ assume MyFloat FLOAT, MyTimeStamp TIMESTAMP in Firebird }
FDQuery1.SQL.Text := 'INSERT INTO MyTable (MyFloat, MyTimeStamp) VALUES (:MyFloat, :MyTimeStamp)';
{ setup parameter data types badly to be dtDouble and dtDateTime }
FDQuery1.ParamByName('MyFloat').AsFloat := 1.2345;
FDQuery1.ParamByName('MyTimeStamp').AsDateTime := Now;
{ and execute; parameter values will be converted automatically to DBMS data types
dtDouble → dtSingle and dtDateTime → dtDateTimeStamp }
FDQuery1.ExecSQL;
所以即使在这里我也要重复,参数集合应该手动定义,而不是通过准备好的命令由 DBMS 定义(开发人员必须知道哪些值填充哪些字段)。
我有一个 TFDConnection
到 FireBird 数据库,我申请 Data type mapping 以向后兼容以前的数据访问技术 (SQLDirect):
with FormatOptions.MapRules.Add do // TIMESTAMP will be ftDateTime instead of ftTimeStamp
begin
SourceDataType := dtDateTimeStamp;
TargetDataType := dtDateTime;
end;
with FormatOptions.MapRules.Add do // FLOAT will be ftFloat instead of ftSingle
begin
SourceDataType := dtSingle;
TargetDataType := dtDouble;
end;
FormatOptions.OwnMapRules := true;
在运行时我创建了一个 TFDQuery,我 link 到那个 TFDConnection。
我可以看到它继承了映射规则:FormatOptions.MapRules.count=2
我将一个 INSERT 查询分配给它的 SQL.Text:
insert into TT_ACT (TT_ACT_ID,TT_PARENT_ID,TT_FROMDATE,TT_TODATE,TT_NAME,TT_NR,TT_CODE,TT_GROUP...)
values (:TT_ACT_ID,:TT_PARENT_ID,:TT_FROMDATE,:TT_TODATE,:TT_NAME,:TT_NR,:TT_CODE,:TT_GROUP,...)
这给了我 params.count=42
数据类型为 ftUnknown 的参数(当然)。
然后我调用准备查询。
如果我现在检查一个已知的日期时间参数,我会看到 params[x].datatype = ftTimeStamp
,而不是 ftDateTime
。
所以查询回数据库看字段的时候,设置参数的时候好像没有听数据映射规则
这是一个错误吗?
在我的代码的后期,这让我遇到了麻烦,导致了著名的 338 错误:
[FireDac][Phys][IB]-338 Param [TT_FROMDATE] type changed from [ftSQLTimeStamp] to [ftDateTime]. Query must be reprepared.
我设法解决了这个错误,所以这不是问题的一部分。但我希望 Params 也遵循数据类型映射规则,这会让这一切变得更容易。
您刚刚错误定义了映射规则定义。对于参数来说,就是target到source的转换。 Data Type Mapping 主题也这么说:
In case of a command parameter, the rule defines a transformation of a target data type, specified by an application, into a source data type, supported by a driver.
因此,要从 TIMESTAMP to dtDateTime and FLOAT to dtDouble 映射命令参数,只需在您的定义中将源与目标交换即可:
{ FLOAT → dtDouble in parameters }
with FormatOptions.MapRules.Add do
begin
SourceDataType := dtDouble; { TFDParam.DataType }
TargetDataType := dtSingle; { Firebird FLOAT }
end;
{ TIMESTAMP → dtDateTime in parameters }
with FormatOptions.MapRules.Add do
begin
SourceDataType := dtDateTime; { TFDParam.DataType }
TargetDataType := dtDateTimeStamp; { Firebird TIMESTAMP }
end;
{ enable custom map rules }
FormatOptions.OwnMapRules := True;
值得补充的是,参数的映射规则是唯一的。它们仅在准备命令时为参数映射数据类型(数据类型必须是可确定的)。它们不会在传递给驱动程序时转换参数值。考虑这段代码:
{ Firebird FLOAT equals to dtSingle data type, map it to dtDouble }
with FDQuery1.FormatOptions.MapRules.Add do
begin
SourceDataType := dtDouble;
TargetDataType := dtSingle;
end;
FDQuery1.FormatOptions.OwnMapRules := True;
{ setup the command; MyFloat field is Firebird FLOAT }
FDQuery1.SQL.Text := 'INSERT INTO MyTable (MyFloat) VALUES (:MyFloat)';
{ rules are applied when preparing command, so let's prepare it }
FDQuery1.Prepare;
{ now the parameter data type should be dtDouble instead of dtSingle }
if FDQuery1.ParamByName('MyFloat').DataType = dtDouble then
ShowMessage('Parameter is of dtDouble data type');
{ but you can easily change the parameter data type to another, e.g. by mistake;
this will change data type to dtSingle, so the whole mapping effort is lost }
FDQuery1.ParamByName('MyFloat').AsSingle := 1.2345;
{ if this would execute (which does not because the parameter data type has been
changed since the command preparation), parameter map rules would still not be
involved in converting parameter value for the driver }
FDQuery1.ExecSQL;
所以正如您所看到的,这几乎是白费力气(仅将确定的参数数据类型更改为另一种)。无论映射规则如何,参数值都会自动转换。因此,即使您的参数数据类型与 DBMS 数据类型不匹配但可以转换,无论如何 FireDAC 都会简单地为您转换它(这个魔法在 ConvertRawData 方法中):
{ assume MyFloat FLOAT, MyTimeStamp TIMESTAMP in Firebird }
FDQuery1.SQL.Text := 'INSERT INTO MyTable (MyFloat, MyTimeStamp) VALUES (:MyFloat, :MyTimeStamp)';
{ setup parameter data types badly to be dtDouble and dtDateTime }
FDQuery1.ParamByName('MyFloat').AsFloat := 1.2345;
FDQuery1.ParamByName('MyTimeStamp').AsDateTime := Now;
{ and execute; parameter values will be converted automatically to DBMS data types
dtDouble → dtSingle and dtDateTime → dtDateTimeStamp }
FDQuery1.ExecSQL;
所以即使在这里我也要重复,参数集合应该手动定义,而不是通过准备好的命令由 DBMS 定义(开发人员必须知道哪些值填充哪些字段)。