使用 Delphi 和 Firedac 设置 sqlite 数据库的相对路径

Setting a relative path to sqlite database with Delphi and Firedac

我的 previous question 令人困惑且表述不当,这里是 "real" 问题。

我想知道如何在运行时使用 Firedac 设置到位于我的应用程序文件夹的子文件夹中的 sqlite 数据库的相对路径。

Jerry Dodge所述:

Any application should never rely on writable data in the same directory anyway. Also, even if you did, you should make sure all your paths are relative to the application at least.

目前,我想到的应用程序是可移植的,我希望将数据库文件存储在主 exe 文件夹的子文件夹中。

在我的主窗体的Form.Create事件中,首先使用

path := ExtractFilePath(Application.ExeName);

然后对于 FDConnection :

with FDConnection1 do begin
  Close;
  with Params do begin
    Clear;
    Add('DriverID=SQLite');
    Add('Database='+path+'Data\sqlite.db');
  end;
  Open;
end;

我一直收到错误提示 "unable to open database file"。

我不想在 FiredDac 连接编辑器中设置数据库文件的路径,因为那样的话它将是绝对的并绑定到我的机器,对吗?

我如何设置数据库文件的路径,以便它可以在任何配置下工作,无论用户将应用程序文件夹放在何处?

提前谢谢大家

数学

在 android 中,我无法在设计时打开 fdConnection 进行编译,然后确认它已关闭。你的第一行对我不起作用,但目标是:

使用以下表达式,对我有用:

FdConnection1.Params.Values['Database'] := GetHomePath
        + PathDelim + 'sqlite.db';

看来我没有使用子文件夹 'Data'。你可以先试试简单的。

如果您的资源对于最终​​用户来说是轻量级的并且只读,您可以将它们直接打包到 exe 文件中。这将为您提供真正便携的应用程序,您的用户可以从任意位置 运行,甚至从 USB 闪存。

转到项目->资源和图像菜单,添加文件。

您可以在 运行 时间内使用 TStream 访问它们:

var
  s:   TStream;
  {.....}
  s:=TResourceStream.Create(hinstance, 'myfile_1', RT_RCDATA);

一些你可以直接在内存中处理的资源。至于sqlite数据库,你可以将它从应用程序资源复制到用户的文档路径:

  MyAppPath := Tpath.GetDocumentsPath+'\MyAppName'; // you need System.IOUtils in uses  
  If not DirectoryExists(MyAppPath) then CreateDir(MyAppPath);
  MyDBPath := MyAppPath+'\Data\sqlite.db';
  If not FileExists(MyDBPath) then begin
    FL:=TFileStream.Create(MyDBPath ,fmCreate);
    FL.CopyFrom(s,0); // this will copy your db into 'sqlite.db' file 
  end;

因为我找到了自己的解决方案,所以我决定在这里 post 以供将来可能遇到相同问题的用户使用(也就是说 Delphi 初学者水平并且需要 link 相对于他们的项目 exe 文件的数据库文件)。

第一步 是向项目添加数据模块。这是通过转到文件 -> 新建 -> 其他 -> Delphi 文件 -> 数据模块来完成的

第二步将数据模块添加到项目后,由于我的主申请表在创建时调用数据库,我必须确保创建了数据模块第一的。为了实现这一点,我去了 Project -> Options -> Forms 并将数据模块拖到自动创建的表单列表的第一个位置

第三步 是在数据模块上放置 FDConnection 并设置所有参数 EXCEPT 数据库文件。

第四步 是将 OnCreate 事件添加到数据模块,以指定相对于应用程序 exe 和连接的数据库路径。它是这样完成的:

procedure TDataModule1.DataModuleCreate(Sender: TObject);
begin
  path := ExtractFilePath(ParamStr(0));

  FDConnection.Params.Add('Database='+path+'Database\sqlite.db');
  FDConnection.Connected := True;
end;

第五步也是最后一步 是将数据模块添加到所有其他需要连接到数据库的单元的 uses 子句中。

我意识到这个解决方案远非完美,正如非常有经验的用户已经指出的那样,将数据库存储在与主应用程序 Exe 相同的文件夹(或子文件夹)中并不是一个好的解决方案。

此外,我决定在创建 DataModule 时连接到数据库,但另一种解决方案可能是在触发查询之前按需连接,然后断开连接。这取决于您和您的需求

感谢大家的帮助、提示和建议

数学

PS :请​​注意我没有检查我的答案是否被接受为最佳答案,这不公平吧:-)

简介

恕我直言

数据库路径和服务器名称不应在应用程序中硬编码。

为什么?

  1. 当你处理一个项目时,你需要在数据库连接、设置数据集、查询等方面做很多事情。通常这是在工作数据库上完成的。则服务器名和数据库路径与真实数据库不同。
  2. 您应该能够轻松设置服务器名称和数据库路径,而无需编译项目。这允许在随机计算机上正确设置数据库连接参数。

解决方案:

  1. 在设计时设置数据库连接组件,不要在运行时创建它。将所有参数(包括服务器名称、数据库路径、字符集等)设置到数据库的工作副本中。这将允许您在设计时设置与该数据库关联的其他组件。 (在您的回答中,我看到您所做的几乎相同。)

  2. 将服务器名称、数据库路径和您想要的任何其他参数保存到外部资源、ini 文件、windows 注册表或其他内容。然后在应用程序启动时或连接到数据库之前获取这些参数。

    在您的情况下,您使用本地服务器和与应用程序相同的路径,因此您不需要存储任何内容。

关于问题

代码:

with FDConnection1 do begin
  Close;
  with Params do begin
    Clear;  <-- this removes all parameters
    Add('DriverID=SQLite');
    Add('Database='+path+'Data\sqlite.db');
  end;
  Open;
end;

删除除 DriverIDDatabase 之外的所有其他参数。错误可能由此产生。

如果您已经在 FDConnection 中设置了所有参数:

不要使用:

FDConnection.Params.Add('Database='+path+'Data\sqlite.db');

这将添加具有相同名称的新参数,但连接将使用第一个参数。

这解释了为什么在您的答案中一切正常,因为您没有在设计时设置参数 'Database':

THIRD STEP was to drop a FDConnection on the datamodule and set all parameters EXCEPT database file.

改为使用:

FDConnection.Params.Database := 'Database='+path+'Data\sqlite.db'; 

您可以在以下示例中使用它 OnDataModuleCreateFDConnectionBeforeConnect 事件

希望对您有所帮助。