☰
Delphi 12.3安装mORMot2控件包:ORM与RESTful实践指南
2026/10/26 14:17:14 网站建设 项目流程

简介:本资源是面向Delphi高级开发者与跨平台服务端架构师的mORMot2框架适配包,专为Delphi 12.3环境深度优化,解决现代REST/JSON-RPC服务开发中ORM映射、HTTP服务器集成及SQLite/CURL底层依赖兼容性问题。压缩包共686个文件,涵盖188个Pascal源码(.pas)、67个C头文件(.h)、64个目标文件(.o)、58个Shell脚本(.sh)及48份Markdown文档(.md),辅以DPR工程、DFM界面、LPI/LPK项目配置等完整构建要素;26.49MB体量内集成了libcurl.a、libsqlite3.a、libgcc.a等关键静态库,体现其对Linux/macOS交叉编译与原生动态链接的双重支持能力。已有102人下载学习,可直接导入IDE构建高性能服务端应用,获取含单元测试、示例服务、数据库迁移脚本及多平台部署配置在内的全栈式开发支撑体系。

1. 项目背景与工具链解析

1.1 为什么是 mORMot2 而不是其他 ORM

看到这个标题,老Delphi玩家应该马上就明白了:这是给 Delphi 12.3 准备的一套 mORMot2 控件包,打成了 7z 压缩包,日期标注 08.03。mORMot2 对 Delphi 生态来说,不只是一个 ORM 框架,而是一整套底层基础设施。它涵盖了 ORM、RESTful 服务端/客户端、JSON 序列化、SQLite3 直接封装、日志系统、缓存、加解密,甚至内存网格计算等一堆能力。官方喜欢叫它 "Synopse mORMot 2",但实际你在 GitHub 上搜到的仓库名是mormot2。

我先说个结论:如果你还在纠结用哪个 Delphi ORM,mORMot2 是目前综合能力最强的选择之一,尤其是 12.x 时代,它把很多以前需要拼装多个库才能完成的事情,统一到了一个代码库里。像DBX、FireDAC这种官方方案,底层连的是数据库驱动,而 mORMot2 更像是你自己的数据访问层——你可以直接用它连 SQLite、PostgreSQL、MSSQL,也可以完全不用 SQL,直接用对象映射,再通过 REST 暴露给前端。

相比其他开源 ORM(比如tiOPF、InstantObjects),mORMot2 的性能优势在基准测试里非常明显。尤其是 JSON 序列化这块,它自带的SynCommons里的 JSON 引擎,比 Delphi 自带的System.JSON快了不止一个数量级。这在你做高并发 REST 接口时,差距就是天壤之别。所以,当你拿到这个Delphi 12.3控件之mORMot2(08.03).7z时,不该只把它当成一个控件来装,而应当成一套完整的应用架构底座来用。

1.2 为什么单独为 Delphi 12.3 打包

Delphi 12.3 是 Embarcadero 在 RAD Studio 12 系列里的一个较新版本,编译器对应的是CodeGear\Delphi的 12.3 分支。mORMot2 对 Delphi 版本的支持其实很宽,从老的 Delphi 6 到最新的 12.x 都能编译。但不同版本之间,有几个关键点会影响 API 的可用性:

  • 泛型和匿名方法:老版本 Delphi 对泛型支持不完整,mORMot2 里的很多高级特性(比如TRecord、TJSONSchema)只能在较新的编译器上跑。Delphi 12.3 对泛型、字符串内联、记录类型增强都做得很好,适合把 mORMot2 的全部功能打开。
  • 跨平台编译:mORMot2 在 Windows、Linux、macOS、Android、iOS 上都有对应支持。但 FireMonkey 框架在 12.3 上对 Android 的权限模型、生命周期管理有了新变化,控件包的封装如果没跟上,编译到 Android 时会报错。
  • Win64 + Async 网络层:mORMot2 底层用的是基于 socket 的异步非阻塞模型,在 Win64 下依赖synpze或libcurl。Delphi 12.3 更新了运行库的 CPU 指令集支持,如果控件包不重新编译,容易出现AV(访问冲突)或EConvertError。

所以这个 08.03 的打包日期,大概率是作者基于 mORMot2 官方源码,针对 Delphi 12.3 的 RTL/VCL 头文件做了适配后重新 build 的产物。你在安装时,不要直接拿官方 GitHub 上的最新源码乱编,而应该优先用这个包里面的.dpk工程组,因为它们已经验证过能在这个 IDE 版本上顺利编译。

1.3 7z 压缩包里的常见内容

这类控件包解压后,目录结构一般是这样:

mORMot2 ├── src │ ├── core │ ├── net │ ├── db │ ├── rest │ ├── scripting │ ├── mvc │ └── ... ├── packages │ ├── Delphi │ │ └── 12.3 │ └── ... ├── examples ├── sqlite3 └── ...

其中最重要的就是packages\Delphi\12.3下的.dpk文件。你需要打开的是类似mORMot2Package_Delphi12.groupproj这样的工程组,按顺序 Build 各个运行时包,再安装设计期包。有些定制版还会内置mORMot2.pas这个包含了所有核心单元的合集版本,方便那些不喜欢用包管理、直接引用源码路径的人。

打开压缩包之前,建议你先确认一下自己的 7-Zip 版本。老版本的 7-Zip 解压这种新打包格式偶尔会出现校验错误,推荐用 7-Zip 23.01 以上版本。解压到路径时,也要注意不要解压到带空格或中文字符的目录,比如D:\Project\mORMot2就可以,D:\我的库\mORMot2 正式版这种路径很容易让后续的 IDE 库路径解析出问题。

2. 安装前的准备工作

2.1 确认 Delphi 12.3 的安装状态

动手之前,先打开 Delphi 12.3 的About对话框,确认版本号确实以 12.3 开头。为什么强调这个?因为 RAD Studio 12.0、12.1、12.2 和 12.3 之间,System.Win.SysUtils等单元虽然向下兼容,但个别函数的实现细节变了,特别是涉及TFile、TDirectory这类 IO 操作时,行为可能不同。如果你实际用的是 12.1 却硬装 12.3 编译出来的包,大概率会在运行时出现奇怪的EInOutError。

另外,检查一下你有没有安装额外的第三方库,比如FastMM(内存管理器)、madExcept(异常捕获)。mORMot2 的默认内存管理器在 Windows 下用的是自己的FastMM4分支,如果工程里同时链接了其他内存管理器,会出现重复定义GetMem的问题。我见过不止一次因为装了madExcept而跟 mORMot2 冲突的情况,表现为启动时直接访问违规但栈信息完全对不上。

如果你准备在 Linux 上部署,那还需要确认 Delphi 12.3 的 Linux 编译环境(PAServer)已经配置好。mORMot2 虽然能编译到 Linux,但有几个底层单元(比如mormot.core.os里的/proc读取、epoll封装)在 Linux 上依赖 FPC 风格的 POSIX 头文件,Delphi 12.3 的 Linux 编译器对libc的某些头文件转换并不完整,所以建议先在 Windows 上把包编译通过,再切换到 Linux 编译。

2.2 解压与目录规划

解压这个 7z 包之前,我强烈建议你建一个干净的目录,不要直接丢到C:\Program Files或者系统盘的Users目录下。mORMot2 在编译过程中会自动生成一些.obj、.dcu文件,如果放到受 UAC 保护的目录,每次编译都会因为权限不足而重新编译,浪费时间还容易出奇怪的问题。

我自己的习惯路径是:

D:\Components\mORMot2\08.03

解压后,在 Delphi 的Tools > Options > Environment Variables里添加一个系统变量MORMOT2,值指向解压后的根目录。这样做的好处是,以后在Project Options > Delphi Compiler > Search Path里可以直接用$(MORMOT2)\src\core这种宏来引用,换机器、换路径时不用改工程文件。这一步很多人忽略,但实际维护时能省下大量精力。

2.3 检查配套依赖

mORMot2 的db层支持直连 SQLite3,自带了一个sqlite3目录,里面通常有预编译好的sqlite3.dll和对应单元文件。如果你在 64 位 Windows 上开发,注意需要sqlite3_x64.dll重命名为sqlite3.dll放到执行目录中,否则加载不成功。不同位数的 DLL 不能混用,否则一调用TSQLite3Database就报Entry Point Not Found。

如果你需要使用 mORMot2 连 PostgreSQL、MSSQL、Oracle 等数据库,包内并不包含对应的客户端驱动库,你需要自己准备。例如:

  • PostgreSQL:需要libpq.dll,且要保证 DLL 的位数与你的 EXE 一致。
  • MSSQL:走的是 OleDB 或 ODBC,在 Windows 上通常系统自带,但要注意 mORMot2 的 ODBC 封装在连接超时参数上与其他框架不太一样,需要单独设置。
  • Oracle:需要安装 Oracle Client,且设置TNS_ADMIN。这一块踩坑非常多,建议先绕开,用 SQLite 把功能验证完再切到企业级数据库。

如果你还要用 mORMot2 的 HTTP 客户端(mormot.net.http)去请求外部的 HTTPS 接口,那依赖的就不是数据库驱动了,而是 OpenSSL。Windows 下需要libssl-3-x64.dll和libcrypto-3-x64.dll。Delphi 12.3 在 Win64 下如果没找到这两个 DLL,运行时会报Socket Error # 0或者直接抛ECrtSocket。这个点很阴,因为编译期完全正常,只有运行到HTTP请求那一行才崩。

3. 编译安装的核心环节

3.1 打开工程组并选择正确平台

进入packages\Delphi\12.3目录,找到类似mORMot2Package_Delphi12.groupproj的文件,用 Delphi 12.3 打开。如果目录下同时有_LW(Library Wrapper)版本,但你只是普通应用开发,不需要安装它。

打开后,第一件事不是直接Build,而是到Project Manager里把当前平台切换到Win64。为什么要强调这个?因为 Delphi 默认可能是Win32,而 mORMot2 在 12.3 上针对 Win64 编译更稳妥,特别是当你的项目最终要部署到 64 位 Windows Server 上时,提前用 64 位编译一遍,能提前发现指针截断、内存对齐之类的问题。

工程组里通常包含两类包:

  • 运行时包(.bpl结尾):名字一般叫mORMot2Core、mORMot2Net等。
  • 设计期包(.bpl结尾,但会注册组件到 IDE):通常带Design关键字,或者单独列出。

编译顺序是:先编译所有运行时包,再编译设计期包。不要全选后一次性Build All,因为包之间有依赖关系,而 Delphi 的工程组依赖解析偶尔会抽风,尤其在第一次编译时。按顺序右键每个包,选择Build,逐个来,稳一点。

3.2 处理编译报错:常见坑与对应解法

站在 12.3 的视角,mORMot2 源码在新编译器上直接编译,大概率会遇到下面几个典型报错。我把它们列出来,免得你卡住。

报错 1:E2003 Undeclared identifier: 'RawByteString'

这个一般不会出现在 12.3,因为你用的版本太新了。但如果你同时装了 mORMot 1.18 老版本,两个版本的源码混在一起,就会出现这种奇怪的符号冲突。解决办法是确保Search Path里只加 mORMot2 的目录,不要加老版本 mORMot1 的。

报错 2:[dcc64 Error] mormot.core.os.pas(1234): E2010 Incompatible types: 'PChar' and 'PWideChar'

这是在 Linux 或某些特殊字符集环境下才会出现的。Windows 下 Delphi 12.3 的默认字符串是UnicodeString,但 mORMot2 内部大量使用RawByteString。如果你在 IDE 的项目选项里把String设为UTF-8或者改了DefaultStringCodePage,就容易触发这个错。解决办法是保持默认设置,不要乱改。

报错 3:F2588 Linker error: 'sqlite3.o' contains invalid OMF record type

这种问题多出在sqlite3目录下自带的.obj文件与你当前 CPU 位数不匹配。如果你在 Win64 下编译,但库里放的是 32 位 obj,就会报这个错。解决办法是把src\db\sqlite3里不必要的.obj全部排除,直接改用sqlite3.dll动态加载方式,也就是让mormot.db.sql.sqlite3通过LoadLibrary载入外部的 DLL。动态加载的好处是灵活,避免链接期的一堆麻烦。

报错 4:E2010 Incompatible types: 'TClass' and 'TComponentClass'

这个多发生在自研代码里,而不是 mORMot2 本身。当你用TSQLRecord的子类时,某些属性声明类型写错,就会出现这种问题。建议先把错误定位到具体单元,看是不是自己把某个类的类型写成了另一个不相关的类。

3.3 设置 IDE 库路径

编译通过并安装设计期包之后,还有一个关键步骤:配置全局库路径。打开Tools > Options > Language > Delphi > Library,在Library Path中加入以下目录:

  • mORMot2 根目录下的src\core
  • src\db
  • src\net
  • src\rest
  • src\mvc
  • src\scripting

如果你用的是Runtime Packages方式(也就是把 mORMot2 编成 BPL,在工程里通过Require引用),那可以不设Search Path,因为运行时包已经在 IDE 里注册了。但如果你跟我一样,喜欢把源码直接编进 EXE 里部署(这样发布时不用带一堆 BPL),那Search Path就必须配好。

我个人的习惯是:调试阶段用源码编进 EXE,发布时再切换成 BPL 动态链接。为什么?因为 mORMot2 的源码编译起来不算慢,但每次改一句框架代码都要重新编译整个项目,那体验就很不舒服了。如果你只是应用开发者,不想碰框架源码,那直接用 BPL 方式就最省心。

3.4 验证安装是否成功

装完之后,怎么确认它真的能用了?最快的验证方式:新建一个 VCL 项目,放一个按钮,在点击事件里面写:

uses mormot.core.base, mormot.core.os, mormot.orm.core; procedure TForm1.Button1Click(Sender: TObject); begin with TSynLog.Create do try Add.Log('mORMot2 is running on %', [OSVersionText]); finally Free; end; end;

如果编译通过且运行后生成了日志文件,说明核心库已经没问题。接着再验证一下 ORM 基础能力:定义一个继承TSQLRecord的类,连上 SQLite,增删改查一遍。这一步能跑通,你的环境就算彻底配好了。

集成到真实项目里时,还有一点必须注意:mORMot2 的加密证书路径、OpenSSL 配置等,在不同平台上默认路径不一样。比如 Windows 下它可能去C:\Windows\System32\config\systemprofile查找证书,而 Linux 下则用系统 CA 库。如果你想快速自测,推荐直接用TOSProcess跑一个 HTTPS 请求,看能否正常返回,不能就直接检查 DLL 是否齐全。

4. mORMot2 核心功能梳理与实用场景

4.1 ORM 映射:把数据库表变成 Delphi 类

mORMot2 的 ORM 不是像 FireDAC 那样先写 SQL,再把数据集映射成对象,而是以类定义为中心。你定义一个类,它帮你自动建表、自动生成 SQL、自动序列化 JSON。

举个例子:

type TArticle = class(TSQLRecord) private FTitle: RawUTF8; FContent: RawUTF8; FCreateTime: TDateTime; FViewCount: Integer; published property Title: RawUTF8 read FTitle write FTitle; property Content: RawUTF8 read FContent write FContent; property CreateTime: TDateTime read FCreateTime write FCreateTime; property ViewCount: Integer read FViewCount write FViewCount; end;

这里有一个新手必须记住的坑:mORMot2 的 ORM 属性必须是published而不是public,且类型要选它能识别的类型,比如RawUTF8而不是string。如果你用UnicodeString,在序列化成 JSON 时会变成带 BOM 的 base64,导致其他语言解析失败。我见过太多人卡在这里,纠结为什么存进去是中文,读出来却是乱码。

定义好类之后,连接数据库并执行 CRUD 就很直接了:

var DB: TSQLRest; Article: TArticle; begin DB := TSQLRestServerDB.Create(TArticle, 'test.db3', False); try Article := TArticle.Create(DB, 'Title=?', ['第一个标题']); try Article.Content := '内容...'; DB.Update(Article); finally Article.Free; end; finally DB.Free; end; end;

这一步走通后,你会发现一个很有意思的地方:你根本不需要手写INSERT INTO、UPDATE、DELETE这些 SQL 语句,mORMot2 会帮你全部生成。这不仅是开发效率的提升,更是代码安全性的提升——因为 mORMot2 内部对 SQL 参数做了完整的转义和预编译处理,从根上规避了 SQL 注入。你只需要定义好映射类,剩下的交给框架。

4.2 ORM 与 SQL 查询的取舍:何时该写纯 SQL

mORMot2 提供了TSQLRecord的高级查询方式,比如:

DB.Retrieve('WHERE ViewCount > ? ORDER BY CreateTime DESC', [100]);

这种写法很舒服,但它只能满足 80% 的需求。剩下的 20%——复杂的多表 JOIN、嵌套子查询、窗口函数——你用 ORM 硬写会非常痛苦,所以 mORMot2 也提供了原生 SQL 接口,以及TSQLRequest直接执行 SQL 语句。比较推荐的做法是:基础增删改查用 ORM,复杂分析用 SQL。大部分团队也是这么玩的,ORM 负责提效,SQL 负责兜底。

还要注意 ORM 返回的字段,不一定跟你数据库表里的字段顺序一致。mORMot2 内部按published属性声明的顺序返回,如果你之前用过 FireDAC 里的SELECT *,那心态要调整一下——你不再依赖表的物理结构,而是依赖类的逻辑结构。这在团队协作时特别有用,因为改表结构只需要改一个类定义,不用到处找 SQL。

4.3 RESTful 服务端:真正拉开差距的地方

mORMot2 的 ORM 只是开胃菜,真正的杀手锏是它自带的高性能 RESTful 服务端。你可以用一个类就能把 ORM 资源暴露为 HTTP 接口,客户端直接通过 JSON 访问。比如:

type TMyServer = class(TSQLRestServerDB) public procedure DoSomething(var Ctxt: THttpServerRequest); end;

然后在服务创建时注册路由:

Server.Router.Add('GET', '/api/ping', DoSomething);

启动服务:

HTTPServer := THttpServer.Create('0.0.0.0:8080', nil, nil); HTTPServer.HttpServerRegister(Server);

这样整个 CRUD 也能自动暴露成/api/root/tarticle之类的 REST 端点。配合 mORMot2 内置的TRestHttpClient,Delphi 写的客户端和服务端之间通信非常顺手,还能直接复用定义的 ORM 类。

有一说一,mORMot2 的 RESTful 性能确实非常能打。它的 HTTP 服务器是异步非阻塞模型,在普通 PC 上跑出上万 QPS 毫无压力。之前我用一个普通的 Windows 服务器,开了 mORMot2 的 REST 服务,压测时并发 2000 个请求,内存占用非常平稳,响应时间几乎是一条直线。这和 Indy 那种每个连接一个线程的模型完全不是一个量级。

4.4 JSON 与跨语言互操作

Delphi 圈子里的数据交换,以前总是头疼 JSON 的序列化和反序列化。System.JSON性能一般,而且 TJSONObject 用起来非常啰嗦。mORMot2 自带了一套完整的 JSON 操作工具,主要分两层:

  • 底层:mormot.core.json里的TJsonWriter/TJsonParser,专门处理流式 JSON,性能极高。
  • 高层:基于RTTI的自动序列化,你可以直接把一个类实例转成 JSON 字符串,也能把一个 JSON 字符串变成类实例。

比如:

var JSON: RawUTF8; Article: TArticle; begin Article := TArticle.Create; ... JSON := Article.ToJSON; // 反向 Article.FromJSON(JSON); end;

配合前面说的RawUTF8类型,mORMot2 生成的 JSON 是标准的 UTF-8 编码,没有 UTF-8 BOM 头,这对 Java、Go、Python 后端来说非常友好。我之前做过一个项目,Delphi 客户端 POST JSON 给 Go 后端,Go 那边直接json.Unmarshal就能解析,全程没有任何编码问题。

注意ToJSON默认只序列化published属性。如果你定义了public的辅助方法,里面去读私有字段,那这些字段不会自动出现在 JSON 里。如果你需要加密字段或者不输出的内部状态字段,可以加上stored false或相关的属性说明。

4.5 缓存、日志与底层工具

除了 ORM 和 REST,mORMot2 还内置了非常多实用的底层工具,这块在二次开发时非常香:

  • TSynLog:一套高效的日志框架,支持分级、分文件、压缩,直接输出为 JSON 格式。它的运行速度很快,Debug 环境下不会影响主流程性能。实际生产里,我习惯把sllInfo级别打开,把每个请求的耗时、参数、DB 操作都记下来,出问题时有完整的调用链。
  • TSynCache:一个线程安全的内存缓存容器,适合做热点数据缓存。内部有 LRU 淘汰策略,可以直接替代自己手写的字典加锁结构。
  • TSynTable:内存中的表格结构,适合做复杂的数据筛选和计算。如果你有从数据库读出数据后在内存里再做多层过滤的需求,这个比TStringList+TList可靠得多。
  • TRawByteStringStream:高效处理二进制数据的流类,和TBytesStream比,省掉了多次内存拷贝。

这些工具类在日常开发中使用频率极高。特别是 TSynLog,我们团队几乎所有的项目都把它作为默认日志方案,因为它能无缝衔接 mORMot2 的 ORM,每个数据库操作都自动打点。你要排查线上问题时,直接查日志文件,基本能把问题定位到具体方法和 SQL 语句。

5. 实际项目集成:从零搭建一个带 ORM 的 REST 服务

5.1 场景设定与接口设计

假设我们现在需要做一个简单的“文章发布系统”后端。需求是:前端(可以是 Vue、微信小程序,甚至另一个 Delphi 程序)通过 HTTP JSON 接口发布文章、获取文章列表、查看文章详情,并且要求支持按浏览量排序。

接口设计如下:

  • POST /api/article:发布文章,请求体为 JSON,包含标题、内容、浏览量。
  • GET /api/article:获取文章列表,支持?order=viewcount排序。
  • GET /api/article/{id}:获取文章详情。

这个场景用 mORMot2 的 ORM + REST 框架来做,代码量会非常少。

5.2 服务端实现

打开 Delphi 12.3,新建一个 VCL 项目(也可以建 Console 项目,但 VCL 方便调试界面),然后在FormCreate里初始化服务端:

procedure TMainForm.FormCreate(Sender: TObject); begin // 初始化数据库 FServer := TSQLRestServerDB.Create(TArticle, 'article.db3', False); FServer.CreateMissingTables; // 启动 HTTP 服务 FHTTPServer := THttpServer.Create('8080', nil, nil); FHTTPServer.HttpServerRegister(FServer, true); end;

这里需要注意:TSQLRestServerDB.Create的第三个参数是aCreate。如果传False,那么打开的数据库文件不存在时会自动创建;如果传True,会强制创建一个新库,即使文件已经存在。如果你不想每次启动都覆盖数据,一定要传False。

CreateMissingTables会自动为TArticle类创建对应的数据表,字段类型、长度、索引都会根据类定义自动生成。如果你的类定义中加了indexed属性,这里会帮你建索引。

接下来,注册自定义的 REST 路由。mORMot2 允许你通过Router来注册事件:

FServer.Router.Add('GET', '/api/health', procedure(var Ctxt: THttpServerRequest) begin Ctxt.OutContent := '{"status":"ok"}'; Ctxt.OutContentType := 'application/json'; end);

如果你不想注册路由,也可以用 mORMot2 默认提供的 CRUD 端点,它会把 ORM 类自动映射成/api/root/tarticle。这个默认端点很不显眼,但对一个快速原型来说完全够用。不过要强调一下,这个默认端点名不直观,前端对接时容易晕,所以生产环境我还是建议用Router.Add手动定义一组对业务有意义的接口。

5.3 客户端调用示例

客户端我们用 Delphi 自带的TNetHTTPClient或者 mORMot2 的客户端。

用 mORMot2 客户端的话,可以直接复用服务端的 ORM 类型定义,只需要把TArticle类放到一个公共单元里,客户端和服务端都引用它。然后:

var Client: TSQLRestClientHttp; Article: TArticle; begin Client := TSQLRestClientHttp.Create('http://127.0.0.1:8080', TArticle); try Article := TArticle.Create; try Article.Title := '测试文章'; Article.Content := '内容'; Article.ViewCount := 0; Client.Add(Article, True); finally Article.Free; end; finally Client.Free; end; end;

这一套下来,你连 URL 拼接、JSON 手工解析都省了,服务端和客户端之间直接用 Delphi 的类作为契约。说实话,这种体验在当前的主流后端技术栈里很难找到类似的存在,这也是为什么 mORMot2 在 Delphi 社区的地位一直很稳固。

5.4 搜索与排序的坑:为什么你的索引没生效

很多人在 mORMot2 里做搜索时,喜欢把条件直接写到WHERE里,比如:

DB.Retrieve('WHERE Title LIKE ?', ['%测试%']);

这段代码能跑,但如果数据量大,性能会很差。因为 mORMot2 默认的 SQLite3 在这个查询里做的是全表扫描。如果你真需要全文搜索或模糊查询,建议用 mORMot2 的TSQLRecord配合虚拟表FTS5,或者用外部搜索服务(比如 Elasticsearch)。单纯在 Delphi 层面对LIKE '%xxx%'做优化,上限很低。

排序字段也一样,如果你经常按ViewCount排序,请在TArticle对应属性上加索引声明:

property ViewCount: Integer read FViewCount write FViewCount; // 在 published 前加特性声明

要在类的published前面加上property ViewCount : Integer read FViewCount write FViewCount index ...这类写法来声明索引。不过 mORMot2 的索引声明更简洁,你需要在类定义前加一行TSQLRecord的一个property声明。在 12.3 上更推荐的做法是创建表后手动执行CREATE INDEX,这样可以精确控制索引类型和字段顺序。

综合来看,ORM 能帮你解决 90% 的日常操作,但搜索和排序这类场景,还是要回到关系和索引的本源去思考。

6. 常见问题与排查技巧实录

6.1 每次进入 IDE 都丢失控件,需要重新放置

这个热词在 Delphi 圈里出现频率很高,而且跟 mORMot2 控件包也有关系。如果你装了设计期包后,每次重启 IDE,工具栏上的 mORMot2 组件都没了,必须重新安装一遍,那大概率是 IDE 的Library Path没配对,或者设计期 BPL 没有正确注册到Windows 注册表的 IDE 包列表里。

排查顺序:

  1. 打开Component > Install Packages,看看运行时包是否在列表里。如果在但设计期包不在,说明设计期包没装上,需要右键对应的.dpk文件,选择Install。
  2. 如果时有时无,检查是不是有两个版本的 BPL 同时存在。比如之前装过 mORMot1 的设计期包,再装 mORMot2 时,包名冲突导致 IDE 启动加载失败。
  3. 如果 IDE 报Can't load package ...,先用tlist或者Process Explorer看看是不是有旧的bpl文件被占用,杀掉相关进程再重装。

6.2 编译期正常,运行期报EDBClientError或者表不存在

这个情况一般在TSQLRestServerDB.Create之后没有调用CreateMissingTables。很多老代码是从 1.18 迁移过来的,老版本在某些路径下会自动建表,但 2.x 版本把自动建表改成显式调用。你需要在初始化时加一行:

FServer.CreateMissingTables;

还有另一个可能:数据库文件用的是只读模式,或者目标目录没有写权限。Windows 下如果放到C:\Program Files下面就会出现这种问题,改成放到%LOCALAPPDATA%或者自定义目录即可。

6.3TSQLRecord与TJSONObject的互相转换出现乱码

大概率还是string和RawUTF8混用导致。mORMot2 内部统一使用RawUTF8,但你的业务代码里可能会用UnicodeString接收外部 JSON。mORMot2 提供了一系列转码函数,比如UTF8ToString、StringToUTF8,你可以显式转换。

另外,如果你用了TJSONObject.Parse,一定要指定编码类型,避免系统自动识别时出现偏差:

var JSON := TJSONObject.Parse('{"title":"中文"}');

6.4 日志文件增长过快,磁盘爆了

TSynLog默认是无限增长,你需要在初始化时设置日志保留策略。常用做法是按天切换文件,保留最近 7 天:

TSynLog.Family.FileRotate := 1; // 每天一个文件 TSynLog.Family.FileMaxSize := 10485760; // 单个文件最大 10MB

如果日志量特别大,考虑把日志级别调高,或者把sllDebug关闭,只保留sllInfo和sllError。生产环境日志建议只记录关键操作和异常,不要每条 SQL 都打,否则就算有轮转,也扛不住长时间高并发。

6.5 连接池与多线程:为什么我的服务并发一高就崩

mORMot2 的 ORM 默认不是线程安全的,同一个TSQLRestServerDB实例不能同时在多个线程里执行写操作。你需要使用TSQLRestServerDB自带的ThreadSafe模式,或者为每个线程创建独立连接。

最简单的方式:

FServer := TSQLRestServerDB.Create(TArticle, 'article.db3', False); FServer.OrmThreadSafe := True;

OrmThreadSafe开启后,写操作会自动排队。如果你需要更高的并发读写,可以考虑读写分离:多读实例 + 单写实例,通过数据库层面的复制来同步。

6.6 FireMonkey 与 mORMot2 配合时的坑

如果你是用 FireMonkey 做跨平台界面,比如在 Android PDA 上跑扫码程序,然后用 mORMot2 做本地 SQLite 存储,有几个点要提前避雷:Android 上 SQLite 数据库文件路径不能随便写,要用GetHomePath或TPath.Combine(TPath.GetDocumentsPath, 'app.db3')。直接用'app.db3'会写到沙箱根目录,Android 10 以后基本不可写。另外,FireMonkey 的TThread.Queue和 mORMot2 的回调线程配合时,要注意 UI 更新必须回到主线程。mORMot2 的任何回调都不能直接在 UI 组件里操作,除非你手动TThread.Queue一下。

扫码场景处理时,通常要让 mORMot2 的日志和扫码结果异步绑定。我在实际项目里的做法是:扫码得到字符串后,立即创建一个 mORMot2 的TSQLRecord对象并提交到后台线程池,后台线程负责 ORM 写入,写完之后再Queue回主线程刷新界面。这样不会因为扫码频率高导致 UI 卡顿。

7. 经验总结与长期使用建议

7.1 控制源码版本

mORMot2 迭代非常快,GitHub 上的master分支可能每天都有提交。虽然大部分时候是修 bug 或新增功能,但也不排除某些 commit 会改变 API 行为。你在生产环境使用,最好锁定一个版本,而不是永远追最新。像这个 08.03 的包,装好之后就把对应源码备份到自己的 Git 库,不要随便升级。

如果你确实想升级,建议先在测试环境把整个单元测试跑一遍。mORMot2 提供了非常完整的单元测试套件,在tests目录下。用 Delphi 12.3 打开测试工程,跑一遍TTestSuite,确认你的目标平台(Win64/Linux/Android)全绿,再合并到主分支。

7.2 先建模,后编码

mORMot2 的开发节奏和传统Dataset开发不太一样。它不是拖控件、拉字段,而是先定义业务对象(类),再由对象驱动数据库表和接口设计。在动手写界面之前,你需要把业务实体、属性类型、索引、关联关系先理清楚。

比如文章系统,你得先想清楚TArticle有哪些字段、哪些需要索引、是否需要全文搜索、标签是一对多还是多对多。这些在类定义阶段定型,后期改动会牵连数据库迁移和接口变动。mORMot2 虽然提供了数据库迁移工具,但代价还是很高。

7.3 不要重复造轮子

mORMot2 里已经有TSynLog、TSynTable、TSynCache、TJsonWriter等一堆基础库。很多团队拿到 mORMot2 之后,只用它的 ORM,其他全部自己写,这其实很可惜。用TSynLog替代你自己的日志库、用TSynTable替代你手写的TList<T>,初期可能只是省一点点代码,但长期维护时会体会到巨大的便利——因为它们都经过了 mORMot2 的测试套件验证,兼容性和性能都有保障。

我在实际用 mORMot2 的过程中,最大的体会是它把 Delphi 开发从一个“找控件、拼接口”的节奏,拉回到了“定义模型、专注业务”的节奏。你不需要再关心底层 HTTP 怎么解析、JSON 怎么转义、SQL 怎么拼接,只要把业务类定义好,mORMot2 就能把整个链路串起来。这种体验,说实话,在 Delphi 生态里已经很难得了。如果你还在 Delphi 12.3 上苦于找不到合适的 ORM 或 REST 框架,这个控件包值得你花一晚上好好折腾一下。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询