在PHP项目开发过程中,经常会遇到需要同时对接多个不同类型数据库的需求,比如核心业务数据存储在MySQL中,临时缓存数据存放在SQLite里,或者需要同时对接不同环境的数据库实例。如果每次操作数据库都手动编写连接逻辑,不仅会产生大量重复代码,还会让后续的配置修改和扩展变得十分麻烦。通过封装数据库工厂类,我们可以统一管理不同数据库的连接配置,实现根据需求动态切换数据库连接的效果,大幅提升代码的可维护性和扩展性。

数据库工厂类的设计思路
工厂类的核心作用是封装对象的创建逻辑,让调用方不需要关心具体对象的实例化过程。对于数据库连接的场景,我们的工厂类需要实现以下几个核心功能:
- 统一管理不同数据库的连接配置,支持从配置文件读取参数
- 根据传入的数据库类型标识,创建对应的数据库连接实例
- 支持动态切换当前使用的数据库连接,不需要修改业务代码
- 对连接实例做简单的缓存,避免重复创建连接浪费资源
基础配置与接口定义
首先我们需要定义数据库连接的通用接口,确保所有数据库的连接实例都遵循相同的操作规范,这样切换连接时业务代码不需要做额外适配。这里我们使用PDO作为底层数据库连接方式,因为PDO原生支持多种数据库类型,适配性更好。
先定义配置文件,存放不同数据库的连接参数:
<?php
// config/database.php 数据库配置文件
return [
'mysql' => [
'driver' => 'mysql',
'host' => '127.0.0.1',
'port' => 3306,
'dbname' => 'test_db',
'username' => 'root',
'password' => '123456',
'charset' => 'utf8mb4',
'options' => [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]
],
'sqlite' => [
'driver' => 'sqlite',
'path' => __DIR__ . '/../data/cache.db',
'options' => [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]
]
];
接下来定义数据库连接接口,规范所有连接实例必须实现的方法:
<?php
// src/Database/DatabaseInterface.php
interface DatabaseInterface
{
// 获取PDO连接实例
public function getConnection(): PDO;
// 执行查询语句
public function query(string $sql, array $params = []): array;
// 执行写操作语句
public function execute(string $sql, array $params = []): int;
}
具体数据库连接实现类
针对不同的数据库类型,我们需要实现对应的连接类,这些类都要实现上面定义的DatabaseInterface接口。这里以MySQL和SQLite两种常见数据库为例。
MySQL连接实现类
<?php
// src/Database/MysqlConnection.php
class MysqlConnection implements DatabaseInterface
{
private PDO $connection;
private array $config;
public function __construct(array $config)
{
$this->config = $config;
}
public function getConnection(): PDO
{
if (!isset($this->connection)) {
$dsn = sprintf(
'mysql:host=%s;port=%d;dbname=%s;charset=%s',
$this->config['host'],
$this->config['port'],
$this->config['dbname'],
$this->config['charset']
);
$this->connection = new PDO(
$dsn,
$this->config['username'],
$this->config['password'],
$this->config['options'] ?? []
);
}
return $this->connection;
}
public function query(string $sql, array $params = []): array
{
$stmt = $this->getConnection()->prepare($sql);
$stmt->execute($params);
return $stmt->fetchAll();
}
public function execute(string $sql, array $params = []): int
{
$stmt = $this->getConnection()->prepare($sql);
$stmt->execute($params);
return $stmt->rowCount();
}
}
SQLite连接实现类
<?php
// src/Database/SqliteConnection.php
class SqliteConnection implements DatabaseInterface
{
private PDO $connection;
private array $config;
public function __construct(array $config)
{
$this->config = $config;
}
public function getConnection(): PDO
{
if (!isset($this->connection)) {
$dsn = 'sqlite:' . $this->config['path'];
$this->connection = new PDO(
$dsn,
null,
null,
$this->config['options'] ?? []
);
}
return $this->connection;
}
public function query(string $sql, array $params = []): array
{
$stmt = $this->getConnection()->prepare($sql);
$stmt->execute($params);
return $stmt->fetchAll();
}
public function execute(string $sql, array $params = []): int
{
$stmt = $this->getConnection()->prepare($sql);
$stmt->execute($params);
return $stmt->rowCount();
}
}
数据库工厂类实现
接下来就是核心的工厂类,它负责根据配置创建对应的数据库连接实例,并且支持动态切换当前使用的连接。
<?php
// src/Database/DatabaseFactory.php
class DatabaseFactory
{
private array $config;
// 缓存已创建的连接实例
private array $connections = [];
// 当前正在使用的连接标识
private string $currentDriver = 'mysql';
public function __construct(array $config)
{
$this->config = $config;
}
// 创建指定类型的数据库连接
private function createConnection(string $driver): DatabaseInterface
{
if (!isset($this->config[$driver])) {
throw new InvalidArgumentException("不支持的数据库类型: {$driver}");
}
$driverConfig = $this->config[$driver];
switch ($driver) {
case 'mysql':
return new MysqlConnection($driverConfig);
case 'sqlite':
return new SqliteConnection($driverConfig);
default:
throw new InvalidArgumentException("暂无{$driver}类型的连接实现");
}
}
// 获取指定类型的数据库连接实例,优先从缓存取
public function getConnection(string $driver): DatabaseInterface
{
if (!isset($this->connections[$driver])) {
$this->connections[$driver] = $this->createConnection($driver);
}
return $this->connections[$driver];
}
// 动态切换当前默认使用的数据库连接
public function switchDriver(string $driver)
{
if (!isset($this->config[$driver])) {
throw new InvalidArgumentException("不支持的数据库类型: {$driver}");
}
$this->currentDriver = $driver;
}
// 获取当前默认使用的数据库连接实例
public function getCurrentConnection(): DatabaseInterface
{
return $this->getConnection($this->currentDriver);
}
}
实际使用示例
完成以上类的封装之后,我们就可以在业务中非常方便地动态切换数据库连接了。下面是具体的使用示例:
<?php
// 加载配置文件和所有类文件(实际项目中可以用自动加载替代)
$config = require __DIR__ . '/config/database.php';
require __DIR__ . '/src/Database/DatabaseInterface.php';
require __DIR__ . '/src/Database/MysqlConnection.php';
require __DIR__ . '/src/Database/SqliteConnection.php';
require __DIR__ . '/src/Database/DatabaseFactory.php';
// 初始化工厂类
$factory = new DatabaseFactory($config);
// 使用默认的MySQL连接查询数据
$mysqlConn = $factory->getCurrentConnection();
$userList = $mysqlConn->query("SELECT id, name FROM users WHERE status = ?", [1]);
echo "MySQL查询到的用户数量:" . count($userList) . PHP_EOL;
// 动态切换到SQLite连接
$factory->switchDriver('sqlite');
$sqliteConn = $factory->getCurrentConnection();
// 创建缓存表
$sqliteConn->execute("CREATE TABLE IF NOT EXISTS page_cache (id INT PRIMARY KEY, content TEXT)");
$sqliteConn->execute("INSERT INTO page_cache (id, content) VALUES (?, ?)", [1, '首页缓存内容']);
$cacheData = $sqliteConn->query("SELECT content FROM page_cache WHERE id = ?", [1]);
echo "SQLite缓存内容:" . $cacheData[0]['content'] . PHP_EOL;
// 也可以直接获取指定类型的连接,不需要切换默认驱动
$mysqlConn2 = $factory->getConnection('mysql');
$orderList = $mysqlConn2->query("SELECT id FROM orders LIMIT 5");
echo "MySQL订单数量:" . count($orderList) . PHP_EOL;
扩展与注意事项
以上实现是一个基础版本,实际项目中可以根据需求做进一步扩展:
- 如果需要支持更多数据库类型,只需要在工厂类的
createConnection方法中添加对应的case分支,实现对应的连接类即可 - 可以给工厂类添加连接池功能,限制同时创建的连接数量,避免连接数过多占用资源
- 配置可以改为从环境变量或者远程配置中心读取,方便不同环境的部署切换
需要注意的点:如果项目中使用了长连接,要合理处理连接的回收和释放,避免数据库连接资源耗尽。另外不同数据库的SQL语法存在差异,切换连接后如果执行了特定数据库的语法,可能会出现兼容性问题,业务层需要做对应的适配处理。