mirror of
https://github.com/zhamao-robot/zhamao-framework.git
synced 2026-07-03 23:05:37 +08:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bb28a07c93 | ||
|
|
731e1d81da | ||
|
|
8866c1de11 |
@@ -213,7 +213,7 @@ container()->get('xxx');
|
||||
|
||||
### db()
|
||||
|
||||
获取 Database 操作类。
|
||||
获取 Database 数据库连接操作类。
|
||||
|
||||
- 定义:`db(string $name = '')`
|
||||
- 返回:`ZM\Store\Database\DBWrapper`
|
||||
@@ -245,6 +245,16 @@ $result = db('mydb')->fetchAllAssociative('SELECT * FROM users WHERE username =
|
||||
var_dump($result[0]); // 假设数据库表只有 id 和 username 两列,这里返回了 ['id' => 1, 'username' => 'jerry']
|
||||
```
|
||||
|
||||
有关此处数据库更详细的内容,请看 [SQL 数据库组件](/components/store/mysql.md)。
|
||||
|
||||
### zm_sqlite()
|
||||
|
||||
> 仅限于炸毛框架 3.2.0 及以上版本使用。
|
||||
|
||||
获取一个便捷 SQLite 模式的数据库操作对象。
|
||||
|
||||
有关此处数据库更详细的内容,请看 [SQL 数据库组件](/components/store/mysql.md)。
|
||||
|
||||
### sql_builder()
|
||||
|
||||
使用 SQL 语句构建器构建一个查询。
|
||||
@@ -261,6 +271,16 @@ $result = sql_builder('mydb')->select('*')->from('users')->where('username = :us
|
||||
// 结果与上方相同
|
||||
```
|
||||
|
||||
有关此处数据库更详细的内容,请看 [SQL 数据库组件](/components/store/mysql.md)。
|
||||
|
||||
### zm_sqlite_builder()
|
||||
|
||||
> 仅限于炸毛框架 3.2.0 及以上版本使用。
|
||||
|
||||
获取一个便捷 SQLite 模式的数据库 SQL 语句构造器。
|
||||
|
||||
有关此处数据库更详细的内容,请看 [SQL 数据库组件](/components/store/mysql.md)。
|
||||
|
||||
### redis()
|
||||
|
||||
获取 Redis 操作类。有关 Redis 的更多详情和配置,见 [Redis 数据库组件](/components/store/redis)。
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
| 1 | jack | man | 2021-10-12 |
|
||||
| 2 | rose | woman | 2021-10-11 |
|
||||
|
||||
## 配置
|
||||
## 连接池
|
||||
|
||||
炸毛框架的数据库组件支持原生 SQL、查询构造器,去掉了复杂的对象模型关联,同时默认为数据库连接池,使开发变得简单。
|
||||
|
||||
@@ -46,9 +46,9 @@ $config['database'] = [
|
||||
|
||||
在设置了 enable 为 true 后,将创建对应数据库的连接池。在框架所有插件加载后启用前会创建连接池。
|
||||
|
||||
## 执行 SQL 语句
|
||||
## 连接池模式
|
||||
|
||||
框架对于不同种类的 SQL 采用了统一的 wrapper 层,保证不同数据库调用时的接口尽可能相同。获取数据库操作对象很简单,通过方法 `db()`:
|
||||
框架对于不同种类的 SQL 采用了统一的 wrapper 层,保证不同数据库调用时的接口尽可能相同。从连接池拿取对象很简单,通过方法 `db()`:
|
||||
|
||||
```php
|
||||
// 获取 default 名称的数据库连接
|
||||
@@ -57,6 +57,34 @@ $db = db();
|
||||
$sqlite = db('sqlite_db1');
|
||||
```
|
||||
|
||||
返回的对象为 `DBWrapper` 对象。
|
||||
|
||||
## 便捷 SQLite 模式
|
||||
|
||||
对于 SQLite 数据库来说,使用连接池可能较为笨重,而且在开发者使用框架开发炸毛框架的插件分发时,可能需要使用 SQLite 数据库,但是又不想使用连接池。
|
||||
|
||||
框架在 3.2.0 版本开始提供了便捷 SQLite 访问,无需任何配置,仅需 `zm_sqlite('dbname.db')` 方式即可创建和访问一个 SQLite 数据库。
|
||||
|
||||
```php
|
||||
// 连接一个 SQLite 数据库,在相对路径下,文件会保存到 zm_data/db/ 目录
|
||||
$db = zm_sqlite('a.db');
|
||||
// 连接一个 SQLite 数据库,可以是任意绝对路径
|
||||
$db = zm_sqlite('/home/zhamao/a.db');
|
||||
// 在连接 SQLite 文件时,如果设置了 create_new 参数为 False,文件不存在时将会抛出异常
|
||||
$db = zm_sqlite('a.db', create_new: false);
|
||||
// 在连接 SQLite 文件时,如果设置了 keep_alive 参数为 False,框架将不会缓存已经打开的 PDO 对象,而是每次都会重新打开。(默认为 True,为了提升性能)
|
||||
$db = zm_sqlite('a.db', keep_alive: false);
|
||||
```
|
||||
|
||||
返回的对象为 `DBWrapper` 对象。
|
||||
|
||||
::: tip 提示
|
||||
|
||||
无论是使用连接池的 `db()` 还是便捷 SQLite 模式的 `zm_sqlite()`,获取的都是 `DBWrapper` 对象,文档只是为了书写方便。
|
||||
实际使用过程中如果要使用便捷 SQLite 模式只需将 `db` 替换为 `zm_sqlite` 即可。
|
||||
|
||||
:::
|
||||
|
||||
### 执行预处理 SQL 语句
|
||||
|
||||
预处理查询很巧妙地解决了 SQL 注入问题,并且可以方便地绑定参数进行查询。
|
||||
@@ -241,13 +269,17 @@ $resultSet = sql_builder()->select(['username', 'gender'])->from('users')->where
|
||||
|
||||
### 获取 SQL Builder
|
||||
|
||||
使用全局函数 `sql_builder()` 即可。
|
||||
连接池的访问模式,使用全局函数 `sql_builder()` 即可。便捷 SQLite 模式,使用全局函数 `zm_sqlite_builder()` 即可。
|
||||
|
||||
```php
|
||||
// 获取 default 名称的数据库连接的 builder
|
||||
$queryBuilder = sql_builder();
|
||||
// 获取对应名称的数据库连接的 builder,名称等于上方配置中的键名
|
||||
$queryBuilder = sql_builder('sqlite_db1');
|
||||
// 使用便捷 SQLite 模式获取 builder
|
||||
$queryBuilder = zm_sqlite_builder('mydb.db');
|
||||
// 在使用便捷 SQLite 模式时,也可以传入 create_new 参数和 keep_alive 参数
|
||||
$queryBuilder = zm_sqlite_builder('/home/a/d.db', create_new: false, keep_alive: false);
|
||||
```
|
||||
|
||||
### 构建一个普通查询
|
||||
|
||||
@@ -1,7 +1,24 @@
|
||||
# 更新日志
|
||||
|
||||
|
||||
> 本页面由框架命令 `./zhamao generate:text update-log-md` 自动生成
|
||||
|
||||
## v3.1.14
|
||||
|
||||
> 更新时间:2023-05-31
|
||||
|
||||
* 抽象数据库连接池的方法 by [@crazywhalecc](https://github.com/crazywhalecc) in [PR#359](https://github.com/zhamao-robot/zhamao-framework/pull/359)
|
||||
|
||||
**源码变更记录**: <https://github.com/zhamao-robot/zhamao-framework/compare/3.1.13...3.1.14>
|
||||
|
||||
## v3.1.13
|
||||
|
||||
> 更新时间:2023-05-26
|
||||
|
||||
* 修复使用框架的计时器和计时器注解时内部使用协程导致报错的 Bug by [@crazywhalecc](https://github.com/crazywhalecc) in [PR#358](https://github.com/zhamao-robot/zhamao-framework/pull/358)
|
||||
|
||||
**源码变更记录**: <https://github.com/zhamao-robot/zhamao-framework/compare/3.1.12...3.1.13>
|
||||
|
||||
## v3.1.12
|
||||
|
||||
> 更新时间:2023-05-24
|
||||
|
||||
@@ -55,6 +55,9 @@ const ZM_PLUGIN_TYPE_COMPOSER = 3; // Composer 依赖的插件
|
||||
const LOAD_MODE_SRC = 0; // 从 src 加载
|
||||
const LOAD_MODE_VENDOR = 1; // 从 vendor 加载
|
||||
|
||||
const ZM_DB_POOL = 1; // 数据库连接池
|
||||
const ZM_DB_PORTABLE = 2; // SQLite 便携数据库
|
||||
|
||||
/* 定义工作目录 */
|
||||
define('WORKING_DIR', getcwd());
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ use ZM\Plugin\OneBot\BotMap;
|
||||
use ZM\Plugin\ZMPlugin;
|
||||
use ZM\Schedule\Timer;
|
||||
use ZM\Store\Database\DBException;
|
||||
use ZM\Store\Database\DBPool;
|
||||
use ZM\Store\Database\DBQueryBuilder;
|
||||
use ZM\Store\Database\DBWrapper;
|
||||
use ZM\Store\KV\KVInterface;
|
||||
@@ -254,6 +255,32 @@ function sql_builder(string $name = ''): DBQueryBuilder
|
||||
return (new DBWrapper($name))->createQueryBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取一个便携 SQLite 操作类
|
||||
*
|
||||
* @param string $name 使用的 SQLite 连接文件名
|
||||
* @param bool $create_new 是否在文件不存在时创建新的
|
||||
* @param bool $keep_alive 是否维持 PDO 对象以便优化性能
|
||||
* @throws DBException
|
||||
*/
|
||||
function zm_sqlite(string $name, bool $create_new = true, bool $keep_alive = true): DBWrapper
|
||||
{
|
||||
return DBPool::createPortableSqlite($name, $create_new, $keep_alive);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取便携 SQLite 操作类的 SQL 语句构造器
|
||||
*
|
||||
* @param string $name 使用的 SQLite 连接文件名
|
||||
* @param bool $create_new 是否在文件不存在时创建新的
|
||||
* @param bool $keep_alive 是否维持 PDO 对象以便优化性能
|
||||
* @throws DBException
|
||||
*/
|
||||
function zm_sqlite_builder(string $name, bool $create_new = true, bool $keep_alive = true): DBQueryBuilder
|
||||
{
|
||||
return zm_sqlite($name, $create_new, $keep_alive)->createQueryBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 Redis 操作类
|
||||
*
|
||||
|
||||
@@ -95,7 +95,7 @@ class WorkerEventListener
|
||||
});
|
||||
|
||||
// 注册各种池子
|
||||
$this->initConnectionPool();
|
||||
$this->initDBConnections();
|
||||
|
||||
// 加载用户代码资源
|
||||
$this->initUserPlugins();
|
||||
@@ -144,6 +144,7 @@ class WorkerEventListener
|
||||
if (is_a(config('global.kv.use', \LightCache::class), LightCache::class, true)) {
|
||||
LightCache::saveAll();
|
||||
}
|
||||
DBPool::resetPortableSQLite();
|
||||
logger()->debug('{is_task}Worker 进程 #{id} 正在停止', ['is_task' => ProcessStateManager::isTaskWorker() ? 'Task' : '', 'id' => ProcessManager::getProcessId()]);
|
||||
|
||||
if (Framework::getInstance()->getDriver()->getName() !== 'swoole') {
|
||||
@@ -260,9 +261,10 @@ class WorkerEventListener
|
||||
*
|
||||
* @throws DBException|RedisException
|
||||
*/
|
||||
private function initConnectionPool(): void
|
||||
private function initDBConnections(): void
|
||||
{
|
||||
DBPool::resetPools();
|
||||
DBPool::resetPortableSQLite();
|
||||
RedisPool::resetPools();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ class Framework
|
||||
public const VERSION_ID = 720;
|
||||
|
||||
/** @var string 版本名称 */
|
||||
public const VERSION = '3.1.14';
|
||||
public const VERSION = '3.2.0';
|
||||
|
||||
/**
|
||||
* @var RuntimePreferences 运行时偏好(环境信息&参数)
|
||||
|
||||
@@ -8,25 +8,49 @@ namespace ZM\Store\Database;
|
||||
|
||||
use Doctrine\DBAL\Driver\Connection;
|
||||
use Doctrine\DBAL\ParameterType;
|
||||
use ZM\Store\FileSystem;
|
||||
|
||||
class DBConnection implements Connection
|
||||
{
|
||||
private int $db_type;
|
||||
|
||||
/** @var \PDO */
|
||||
private object $conn;
|
||||
|
||||
private $pool_name;
|
||||
|
||||
public function __construct($params)
|
||||
public function __construct(private array $params)
|
||||
{
|
||||
logger()->debug('Constructing...');
|
||||
$this->conn = DBPool::pool($params['dbName'])->get();
|
||||
$this->pool_name = $params['dbName'];
|
||||
$this->db_type = $params['dbType'] ?? ZM_DB_POOL;
|
||||
if ($params['dbType'] === ZM_DB_POOL) {
|
||||
// 默认连接池的形式,
|
||||
logger()->debug('Constructing...');
|
||||
$this->conn = DBPool::pool($params['dbName'])->get();
|
||||
$this->pool_name = $params['dbName'];
|
||||
} elseif ($params['dbType'] === ZM_DB_PORTABLE) {
|
||||
$connect_str = 'sqlite:{filename}';
|
||||
if (FileSystem::isRelativePath($params['filename'])) {
|
||||
$params['filename'] = zm_dir(config('global.data_dir') . '/db/' . $params['filename']);
|
||||
FileSystem::createDir(zm_dir(config('global.data_dir') . '/db'));
|
||||
}
|
||||
$table = [
|
||||
'{filename}' => $params['filename'],
|
||||
];
|
||||
// 如果文件不存在则创建,但如果设置了 createNew 为 false 则不创建,不存在就直接抛出异常
|
||||
if (!file_exists($params['filename']) && ($params['createNew'] ?? true) === false) {
|
||||
throw new DBException("Database file {$params['filename']} not found!");
|
||||
}
|
||||
$connect_str = str_replace(array_keys($table), array_values($table), $connect_str);
|
||||
$this->conn = new \PDO($connect_str);
|
||||
}
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
logger()->debug('Destructing!!!');
|
||||
DBPool::pool($this->pool_name)->put($this->conn);
|
||||
if ($this->db_type === ZM_DB_POOL) {
|
||||
logger()->debug('Destructing!!!');
|
||||
DBPool::pool($this->pool_name)->put($this->conn);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -126,4 +150,14 @@ class DBConnection implements Connection
|
||||
{
|
||||
return $this->pool_name;
|
||||
}
|
||||
|
||||
public function getDbType(): int
|
||||
{
|
||||
return $this->db_type;
|
||||
}
|
||||
|
||||
public function getParams(): array
|
||||
{
|
||||
return $this->params;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,11 @@ class DBPool
|
||||
*/
|
||||
private static array $pools = [];
|
||||
|
||||
/**
|
||||
* @var array<string, DBWrapper> 持久化的便携 SQLite 连接对象缓存
|
||||
*/
|
||||
private static array $portable_cache = [];
|
||||
|
||||
/**
|
||||
* 重新初始化连接池,有时候连不上某个对象时候可以使用,也可以定期调用释放链接
|
||||
*
|
||||
@@ -43,6 +48,16 @@ class DBPool
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新初始化所有的便携 SQLite 连接(其实就是断开)
|
||||
*/
|
||||
public static function resetPortableSQLite(): void
|
||||
{
|
||||
foreach (self::$portable_cache as $name => $wrapper) {
|
||||
unset(self::$portable_cache[$name]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过配置文件创建一个 MySQL 连接池
|
||||
*
|
||||
@@ -180,4 +195,23 @@ class DBPool
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个便携的 SQLite 处理类
|
||||
*
|
||||
* @param string $name SQLite 文件名
|
||||
* @param bool $create_new 如果数据库不存在,是否创建新的库
|
||||
* @throws DBException
|
||||
*/
|
||||
public static function createPortableSqlite(string $name, bool $create_new = true, bool $keep_alive = true): DBWrapper
|
||||
{
|
||||
if ($keep_alive && isset(self::$portable_cache[$name])) {
|
||||
return self::$portable_cache[$name];
|
||||
}
|
||||
$db = new DBWrapper($name, ['dbType' => ZM_DB_PORTABLE, 'createNew' => $create_new]);
|
||||
if ($keep_alive) {
|
||||
self::$portable_cache[$name] = $db;
|
||||
}
|
||||
return $db;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,17 +18,25 @@ class DBWrapper
|
||||
* DBWrapper constructor.
|
||||
* @throws DBException
|
||||
*/
|
||||
public function __construct(string $name)
|
||||
public function __construct(string $name, array $options = [])
|
||||
{
|
||||
// 初始化配置
|
||||
$db_type = $options['dbType'] ?? ZM_DB_POOL;
|
||||
try {
|
||||
$db_list = config()->get('global.database');
|
||||
if (isset($db_list[$name]) || (is_countable($db_list) ? count($db_list) : 0) === 1) {
|
||||
if ($name === '') {
|
||||
$name = array_key_first($db_list);
|
||||
if ($db_type === ZM_DB_POOL) {
|
||||
// pool 为连接池格式
|
||||
$db_list = config()->get('global.database');
|
||||
if (isset($db_list[$name]) || (is_countable($db_list) ? count($db_list) : 0) === 1) {
|
||||
if ($name === '') {
|
||||
$name = array_key_first($db_list);
|
||||
}
|
||||
$this->connection = DriverManager::getConnection(['driverClass' => $this->getConnectionClass($db_list[$name]['type']), ...$options]);
|
||||
} else {
|
||||
throw new DBException('Cannot find database config named "' . $name . '" !');
|
||||
}
|
||||
$this->connection = DriverManager::getConnection(['driverClass' => $this->getConnectionClass($db_list[$name]['type']), 'dbName' => $name]);
|
||||
} else {
|
||||
throw new DBException('Cannot find database config named "' . $name . '" !');
|
||||
} elseif ($db_type === ZM_DB_PORTABLE) {
|
||||
// portable 为sqlite单文件模式
|
||||
$this->connection = DriverManager::getConnection(['driverClass' => SQLiteDriver::class, 'filename' => $name, ...$options]);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
throw new DBException($e->getMessage(), $e->getCode(), $e);
|
||||
@@ -38,6 +46,7 @@ class DBWrapper
|
||||
public function __destruct()
|
||||
{
|
||||
$this->connection->close();
|
||||
$this->connection->close();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user