在 symfony2 中调用数据库存储过程必须使用 doctrine dbal 原生 sql,通过 getconnection() 获取连接后执行 call 或 select 语句,手动处理参数绑定、输出变量及多结果集,并需自行映射结果到对象。

在 Symfony2 中调用数据库存储过程,不能通过 Doctrine ORM 的常规实体查询(如 find() 或 findBy())完成,必须走原生 SQL 路径。核心在于:使用 Doctrine DBAL 的连接对象执行可调用语句,并手动处理输出参数或结果集。
原生调用存储过程的基本方式
Doctrine 不提供类似 JDBC CallableStatement 的抽象层,需依赖底层 PDO 连接。关键操作是获取 $conn = $this->getDoctrine()->getConnection(),再通过 executeStatement() 或 executeQuery() 执行 CALL 语句。
- MySQL 存储过程调用示例:
CALL insert_user(@u_id, 'Tom', 'M', 25); SELECT @u_id;—— 需分两步执行(先 CALL,再 SELECT 输出变量) - PostgreSQL 使用
SELECT * FROM insert_user('Tom', 'M', 25)更自然,因其函数可直接返回表或标量 - SQL Server 推荐写法:
EXEC SalesLT.uspGetCustomerCompany @LastName = ?, @FirstName = ?,用?占位符绑定参数
参数绑定与输出参数处理
Doctrine 原生执行不支持命名参数(如 :name)用于存储过程调用,仅支持位置参数 ?。若存储过程含 OUT/INOUT 参数(如 MySQL 的 OUT u_id INTEGER),PDO 层本身不自动提取,需额外步骤:
- 对 MySQL:先执行
SET @u_id = NULL; CALL insert_user(@u_id, ...);,再用$conn->fetchOne('SELECT @u_id')获取值 - 对 PostgreSQL:函数返回值可直接映射,如
SELECT insert_user('Tom', 'M', 25)返回单值,用fetchColumn() - 避免在事务外单独读取输出变量,否则可能因会话隔离导致取不到值
多结果集的遍历方法
部分存储过程(尤其 Oracle、SQL Server)会返回多个结果集(例如先返回统计行,再返回明细列表)。Doctrine DBAL 默认只取第一个结果集,需手动轮询:
- 调用后使用
$stmt = $conn->prepare('CALL get_report()');→$stmt->execute(); - 首次调用
$stmt->fetchAll()获取第一个结果集 - 接着调用
$stmt->nextRowset()切换到下一结果集,再重复fetchAll() - 注意:此能力依赖底层 PDO 驱动是否启用
PDO::MYSQL_ATTR_USE_BUFFERED_QUERY等选项
结果映射:从数组到对象的衔接
原生查询返回的是纯数组(fetchAssoc() / fetchAll()),若需转为实体对象,不能依赖 ORM 自动映射,须手动构造:
- 简单场景:用
new Product($row['name'], $row['price'])显式实例化 - 复杂场景:借助 Doctrine 的
ResultSetMapping类定义字段→属性映射,再配合$em->createNativeQuery($sql, $rsm)->getResult()实现半自动映射 - 不建议将存储过程结果直接注入 EntityRepository,因其破坏了 ORM 的变更跟踪和生命周期管理逻辑











