【问题标题】:Setting a relative path to sqlite database with Delphi and Firedac使用 Delphi 和 Firedac 设置 sqlite 数据库的相对路径
【发布时间】:2017-05-22 23:57:37
【问题描述】:

我的 previous question 令人困惑且表述不当,这里是“真正的”问题。

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

正如Jerry Dodge 所说:

任何应用程序都不应依赖同一目录中的可写数据。此外,即使您这样做了,您也应该确保所有路径至少都与应用程序相关。

目前,我想到的应用程序是可移植的,我希望将数据库文件存储在主 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;

我不断收到“无法打开数据库文件”的错误消息。

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

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

提前谢谢大家

数学

【问题讨论】:

  • 确保你设置了`FDConnection1.DriverName:='SQLite'`。即使您的数据库文件不在 Win32\debug\Data 文件夹中,它也应该在您调用 FDConnection1.Open 时创建,所以您会遇到另一个问题 - 如果您的用户删除您的 db 文件并打开一个新的空白文件怎么办?
  • 感谢迈克尔花时间回答。是的,驱动程序已设置,数据库文件在指定文件夹中。在这个问题上摸不着头脑。
  • 如果重命名文件会发生什么?是否创建了一个新的?如果是这样,您尝试打开的那个可能已损坏,或者由于某种原因 FireDAC 无法读取。如果没有,应用程序写入该路径可能会有一些麻烦(该子文件夹必须存在,它没有被创建)。
  • @MichaelGendelev 和 Victoria 你说的都是对的,虽然没有找到数据库,但创建了一个新的空数据库,这让我意识到问题出在我指定的路径上。我发布了我的解决方案,作为未来可能遇到相同问题的用户的答案。感谢您的提示

标签: sqlite delphi relative-path firedac


【解决方案1】:

当我找到自己的解决方案时,我决定将它发布在这里,以供将来可能遇到相同问题的用户使用(也就是说,Delphi 初学者级别和需要链接相对于他们的项目 exe 文件的数据库文件)。

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

第二步 一旦数据模块添加到项目中,因为我的主应用程序表单在创建时调用数据库,我必须确保首先创建数据模块。为了实现这一点,我去了 Project -> Options -> Forms 并将数据模块拖到自动创建的 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;

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

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

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

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

数学

PS:请注意,我没有将我的答案作为最佳答案进行检查,这不公平:-)

【讨论】:

  • 我认为您不需要在启动应用程序时创建所有这些表单。需要时创建表单,不再需要时销毁它
  • @ValMarinov 感谢您的建议(再次感谢您提供的其他非常有用的答案,尤其是关于指定数据库参数而不是添加它的评论)。在启动时创建所有表单的缺点是什么?内存使用情况 ?我假设不在启动时创建它们不会影响 exe 大小,所以它只是一个内存问题吗?对不起,如果这个问题看起来有点“noobish”:)
  • 内存,是的。这也可能导致应用程序启动时间延长,尤其是。如果表单包括它自己的数据库访问。还有更多的东西。一般来说,接受它作为一种让你的生活更轻松的好习惯。 :) 看到这个:ayton.id.au/gary/it/Delphi/D_Forms1.htm
【解决方案2】:

简介

恕我直言

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

为什么?

  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');

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

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

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

改为使用:

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

您可以在例如 OnDataModuleCreateFDConnectionBeforeConnect 事件

我希望这会有用。

【讨论】:

  • 非常感谢解释和代码,谢谢。帮助我改进了我的解决方案。
【解决方案3】:

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

使用以下表达式,它适用于我:

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

看我没有使用子文件夹“数据”。你可以先试试简单的。

【讨论】:

  • 您好里卡多,感谢您的提示。但是 getHomePath 指向 Appdata/Roaming 文件夹,并且数据库存储在应用程序文件夹的子文件夹中,这就是我使用 ExtractFilePath 命令的原因。该应用程序是可移植的,用户可以将文件夹放在任何地方。
【解决方案4】:

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

进入Project->Resources and Images菜单,添加你的文件。

您可以在运行时使用 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;

【讨论】:

  • 感谢您的提示,我一定会在未来的项目中对此进行研究。在这一点上,图像和图标等资源被嵌入到共享单元上的 TImageList 中
猜你喜欢
  • 1970-01-01
  • 2015-10-15
  • 2022-01-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多