# 概述
数据库操作是网络编程中非常重要的部分,DCE在与数据库交互的开发比重也是最重的,占了整个框架近一半的代码量。DCE提供了丰富的数据库操作支持,其中最亮点的特性则是编写了 分库中间件,提供了对分布式数据库的读写及简易维护支持。DCE封装了非常灵活且简洁的数据库查询支持,你可以通过查询器拼装各种复杂的查询语句,具体见下述示例及后续章节。
# 普通示例
(目前仅支持MySql,但在设计DCE时考虑了对其他数据库的支持,所以后续若需支持其他数据库,也可以很方便的扩展开发。)
# 数据库配置
单库版配置
'mysql' => [
'host' => '127.0.0.1', // 主机地址
'db_user' => 'root', // 数据库用户名
'db_password' => 'password', // 数据库密码
'db_name' => 'default_db', // 数据库名
'db_port' => 3306, // 数据库端口
'max_connection' => 8, // 最大连接数
],
2
3
4
5
6
7
8
多库版配置
'mysql' => [
'default' => [ // 数据库别名 (上述的单库版在实例化为对象时会自动格式化为default别名的配置,该别名的库为默认查询库,不指定库查询时会查询改配置的库)
'host' => '127.0.0.1', // 主机地址
'db_user' => 'root', // 数据库用户名
'db_password' => 'password', // 数据库密码
'db_name' => 'default_db', // 数据库名
'db_port' => 3306, // 数据库端口
'max_connection' => 8, // 最大连接数
],
'db2' => [ // 数据库别名
'host' => '127.0.0.1',
'db_user' => 'root',
'db_password' => 'password',
'db_name' => 'other_db',
'db_port' => 33065,
'max_connection' => 8,
],
],
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 查询器查询
// 增
$id = (new \dce\db\Query())->table('article')->insert(['title'=>'DCE是啥?', 'content'=>'DCE是一款网络编程开发框架']);
// 删
$affecteds = (new \dce\db\Query())->table('article')->where('id', 1)->delete();
// 查
$list = (new \dce\db\Query())->table('article')->where([['mid', 10000], ['create_time', 'between', ['2021-01-01', '2021-01-31 23:59:59']]])->select();
// 改
$affecteds = (new \dce\db\Query())->table('article')->where('id', 1)->update(['title'=>'DCE是啥']);
// 指定非默认库查询
$article = (new \dce\db\Query('db2'))->table('article')->where(['id', '=', 1])->find();
2
3
4
5
6
7
8
9
10
# 快捷方法查询
使用查询器类查询时需要较长的编码,为了简化编码设计了快捷方法实例化查询器对象
# db()
快捷实例化查询器类的方法
参数
string|null $tableName
待查表名string|null $alias
表别名string|null $dbAlias
目标库别名
返回
\dce\db\Query
# raw()
快捷实例化原始语句类的方法
TIP
为了防止SQL注入,DCE对字段名数据值等做了严格语法校验,但有时我们的查询语句较复杂,并且确定是安全的,却无法通过校验,于是DCE设计了原始语句类,凡是该对象包装的语句,不会被语法校验,方便你编写复杂的查询语句。
当然,首先你需确保使用此方法包装的语句是绝对安全的,否则将可能得不偿失
参数
string $sql
需包装的原始语句bool $autoParenthesis = true
是否用小括号包装array $params = []
占位符对应值的映射表
返回
\dce\db\query\builder\RawBuilder
# 示例
// 增
$id = db('article')->insert(['title'=>'DCE是啥?', 'content'=>'DCE是一款网络编程开发框架']);
// 删
$affecteds = db('article')->where('id', 1)->delete();
// 查
$list = db('article')->where([['mid', 10000], ['create_time', 'between', ['2021-01-01', '2021-01-31 23:59:59']]])->select();
// 改
$affecteds = db('article')->where('id', 1)->update(['title'=>'DCE是啥']);
// 指定非默认库查询
$article = db('article', dbAlias: 'db2')->where(['id', '=', 1])->find();
// 自增查询 (使用raw方法包装表达式式右值)
$affecteds = db('article')->where('id', 1)->update(['hits'=>raw('hits + 1')]);
// 复杂查询
$list = db('article', 'a')->join('member', 'm', ['a.mid', raw('m.mid')])->where([
['a.is_deleted', 0], // 省略'='比较符
[
['a.hits', '>', 10000],
'or',
['m.vip', '>', 0],
],
'and', // 'and'逻辑符可省略
['exists', raw('SELECT 1 FROM activity WHERE mid = m.mid LIMIT 1')] // 无左值比较表达式
])->order('a.hits', 'desc')->limit(10, 0)->select('a.*, m.name member_name', 'id', isAutoRaw: true);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 活动记录式查询
# 定义表实体
<?php
namespace tests\model;
use dce\db\active\DbActiveRecord;
use dce\db\entity\DbField;
use dce\db\entity\schema\FieldType;
use dce\model\Property;
use dce\model\Validator;
/**
* Class Member
* @package tests\model
* @property MemberGroup $group
* @property MemberTag[] $memberTags
* @property Tag[] $tags
*/
class Member extends DbActiveRecord {
public const SCENARIO_REGISTER = 'register';
#[ Property('用户ID'), DbField(FieldType::BIGINT, primary: true, increment: true), ]
public int $mid;
#[ Property('分组ID'), DbField(FieldType::TINYINT), Validator(Validator::RULE_INTEGER), ]
public int $groupId;
#[ Property('用户名'), DbField(FieldType::VARCHAR, 16),
Validator(Validator::RULE_STRING, [self::SCENARIO_DEFAULT, self::SCENARIO_REGISTER], max: 16, min: 3),
Validator(Validator::RULE_REQUIRED, self::SCENARIO_REGISTER),
]
public string $username;
#[ Property('密码'), DbField(FieldType::VARCHAR, 128), ]
public string $password;
#[ Property('注册时间'), DbField(FieldType::DATETIME), Validator(Validator::RULE_DATETIME), ]
public string $registerTime;
public function getGroup() {
return $this->hasOne(MemberGroup::class, ['id' => 'group_id']);
}
public function getMemberTags() {
return $this->hasMany(MemberTag::class, ['mid' => 'mid']);
}
public function getTags() {
return $this->hasMany(Tag::class, ['id' => 'tag_id'], 'memberTags');
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# 查询示例
// 增
$member = new Member(Member::SCENARIO_REGISTER);
$member->username = 'Drunk';
$member->password = sha1('123456');
$member->registerTime = date('Y-m-d H:i:s');
$member->insert();
// 删
$affecteds = Member::find(1)->delete();
// 查
$list = Member::query()->where('group_id', 1)->select();
// 改
$member = Member::find(1);
$member->password = sha1('654321');
$member->update();
// 取关系数据
$member = Member::find(1);
$tags = $member->tags;
// 指定非默认库查询
// 在Member类中定义方法
class Member extends DbActiveRecord {
public function getProxy(): string|DbProxy|null {
return 'db2';
}
}
// 然后Member对象的操作目标库则为'db2'
$member = Member::find(1);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 分库示例
# 分库配置
<?php
return [
'id_generator' => [ // ID生成器配置
'client_rpc_hosts' => [],
],
'mysql' => [ // 数据库配置
'default' => [
'host' => '127.0.0.1',
'db_user' => 'root',
'db_password' => 'password',
'db_name' => 'default_db',
'db_port' => 3306,
'max_connection' => 8,
],
'22:3306' => [
[
'label' => '22:3306',
'host' => '127.0.0.1',
'db_user' => 'root',
'db_password' => 'password',
'db_name' => 'sharding_db',
'db_port' => 3306,
'is_master' => 1, // 是否主库
],
],
'22:33065' => [
[
'label' => '22:33065',
'host' => '127.0.0.1',
'db_user' => 'root',
'db_password' => 'password',
'db_name' => 'sharding_db',
'db_port' => 33065,
],
],
],
'sharding' => [ // 分库规则配置
'member' => [ // 分库别名
'db_type' => 'mysql',
'type' => 'modulo', // 按模查询
'cross_update' => true, // 是否允许跨库更新
'allow_joint' => true, // 是否允许联表查询
'table' => [
'member' => [
'id_column' => 'mid', // 未配置sharding_column时将以id_column作为分库字段
'id_tag' => 'mid',
],
'member_login' => [
'id_column' => 'id', // 若配置了id_column字段, 则将使用生成器生成ID, 若同时配置了sharding_column, 则该字段将作为ID的基因字段
'id_tag' => 'mlid',
'sharding_column' => 'mid', // 若未配置id_column字段, 则将不主动生成ID, 分库将仅以sharding_column字段划分
'sharding_tag' => 'mid'
],
],
'mapping' => [ // 分库规则映射表
'22:3306' => 0,
'22:33065' => 1,
],
],
],
];
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# 分库查询
分库查询和非分库查询的方式完全一样,支持查询器方式、快捷方式及活动记录式查询,只要待查的目标表命中分库配置的表,且查询语句符合分库查询的条件,则会自动进行分库查询并合并结果。分库查询时无法指定目标库查询,只要你的目标表配置为了分库表,则会强制分库查询,如果查询语句不符合分库查询条件则会抛出异常。
要熟练使用分库查询你需要弄清分库查询原理,需要懂得ID生成器及其配置,及分库配置。分库查询有一些限制,如对事务的支持不完整,如连表查询的限制等。对于事务,你可以通过友好的分库规则设计避免,或者自己通过程序逻辑实现;对于连表查询,你也可以通过友好的设计避免,或者可以使用活动记录的关系数据特性弥补,或者自己编写程序代码补充数据。但这些需要你对分库原理有较好的认识,你可以从后续的章节深入的学习理解DCE 分库中间件。