Oracle 12c+ 中 DBMS_MAIL 已被彻底移除,调用即报 ORA-00904;替代方案为 UTL_MAIL(需安装脚本、配置 smtp_out_server 参数并确保网络可达)或 UTL_SMTP(需手动处理 STARTTLS、字符编码及邮件头格式)。
DBMS_MAIL 在 Oracle 12c+ 中根本不可用
oracle 官方从 12c 开始彻底移除了 dbms_mail 包,调用会直接报错 ora-00904: "dbms_mail": invalid identifier。这不是权限或安装问题,是包本身已被删除。别浪费时间查文档或授予权限——它不存在了。
替代方案只有两个:用 UTL_SMTP 手写 SMTP 协议交互,或者用更现代的 UTL_MAIL(需数据库启用并配置)。
UTL_MAIL 配置失败的三个常见原因
UTL_MAIL 看起来简单,但实际启用时 80% 的失败集中在以下三点:
- 数据库未启用该包:需 DBA 运行
@?/rdbms/admin/utlmail.sql和@?/rdbms/admin/prvtmail.plb(路径可能因版本略有差异) -
SMTP_OUT_SERVER参数未设或设错:ALTER SYSTEM SET smtp_out_server = 'smtp.example.com:587' SCOPE=BOTH;,注意必须带端口,且不能有http://前缀 - 防火墙或网络策略阻断出站 587/465 端口:DB 服务器本身要能直连邮件服务器,和应用服务器能否发信无关
检查是否生效:运行 SELECT value FROM v$parameter WHERE name = 'smtp_out_server';,返回非空即配置成功。
UTL_MAIL.SEND 调用时参数顺序和字符集陷阱
UTL_MAIL.SEND 的参数顺序固定,且对中文支持脆弱。常见错误是主题或正文乱码,根源在字符集没显式声明:
- 必须指定
mime_type => 'text/plain; charset=utf-8'(HTML 同理),否则默认用数据库字符集(如 WE8ISO8859P1),中文变问号 -
subject和message参数长度上限为 32767 字节,超长会报ORA-29260: network error: TNS:connection refused(误导性错误) - 收件人多个时,用逗号分隔字符串(
'a@x.com,b@y.com'),不是数组或集合
最小可用示例:
BEGIN
UTL_MAIL.SEND(
sender => 'noreply@db.example.com',
recipients => 'user@example.com',
subject => '测试',
message => '这是一封测试邮件',
mime_type => 'text/plain; charset=utf-8'
);
END;
UTL_SMTP 手动发信的必要步骤和 TLS 注意点
当 UTL_MAIL 不可用或需要自定义认证(如 OAuth2、客户端证书),只能上 UTL_SMTP。但它不自动处理 TLS/SSL 协商,容易卡在连接后无响应:
- 用
UTL_SMTP.OPEN_CONNECTION时,若邮件服务器要求 STARTTLS,必须先发EHLO,再发STARTTLS命令,然后重新协商连接(Oracle 12.1+ 支持UTL_SMTP.STARTTLS函数) - 密码明文传入
UTL_SMTP.AUTHENTICATE,切勿硬编码;建议用加密函数或外部凭证表 - 每行邮件头(From/To/Subject)结尾必须是
CRLF(CHR(13)||CHR(10)),少一个字符都可能被拒收
真正麻烦的不是写代码,而是调试——UTL_SMTP 报错信息极简,ORA-29279: SMTP permanent error 这类错误得靠抓包或开邮件服务器日志定位。
复杂点在于:TLS 版本兼容性、中间代理重写、反垃圾策略(比如要求 SPF 记录匹配发信 IP),这些都不是 PL/SQL 层能控制的。发不出去时,先确认 DB 服务器 IP 是否在邮件服务商白名单里。










