Swoft ORM AR 活动记录

什么是ORM?

ORM(Object Relational Mapping, ORM, O/RM, O/R mapping)对象关系映射用来实现面向对象编程语言中不同类型系统的数据之间的转换。

Swoft ORM AR 活动记录_第1张图片

ORM

ORM是一种为了解决面向对象与关系数据库存在互补匹配现象的技术。

Swoft ORM AR 活动记录_第2张图片

ORM

简单来说,ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系数据库中。从本质上来讲,就是将数据从一种形式转换到另一种形式。

Swoft ORM AR 活动记录_第3张图片

ORM

ORM中数据库操作分为两种:

基础的活动记录ActiveRecord用于快速开发和常规查询

高级的数据映射DataMapping用于事务和复杂业务查询

ActiveRecord

每个数据表对应创建一个类,类的每个对象实例对应数据表中的一行记录,通常表的每个字段在类中都有对应的字段。

ActiveRecord同时负责将自己持久化,在ActiveRecord中封装了对数据库的访问即CURD。

ActiveRecord是一种领域模型(Domain Model, DM)封装了部分业务逻辑

DataMapping

DataMapping数据映射是指给定两个数据模型,在模型之间建立起数据元素的对应关系,将此过程称为数据映射。

Swoft ORM AR 活动记录_第4张图片

数据映射

在简单应用中,领域模型(Domain Model)是一种和数据库结构一致的简单模型,对应的每个数据表都有一个领域类。在这种情况下,有必要让每个对象负责数据库的存取过程,这也就是活动记录(ActiveRecord)。领域对象直接与数据表进行交互,这带来了一个问题,随着领域逻辑变得越来越负责,它就慢慢转变成了一个更大的领域模型,此时简单活动记录就会逐渐无法满足需求。当领域类和数据表一对一匹配也开始随着将领域逻辑放入更小的类而失效,由于关系数据库无法处理继承,因此使用策略模式等面向对象模式非常困难。一种更好的办法是把数据库与数据库完全独立,让间接层完全领域对象和数据表之间的映射关系,这个映射类也称为数据映射器(DataMapper)。这个映射类处理数据库和领域模型之间所有的存取操作,并且允许双方都能独立变化。如果领域逻辑非常简单且数据表十分一致,使用简单的ActiveRecord活动记录就足够了,但如果领域逻辑比较复杂且是长线项目,DataMapper数据映射器则可能是需要的。

Swoft查询器

ORM有多种设计模式,Swoft采用的是DataMapping,将业务于实体分开,但也实现了类似ActiveRecord的操作方式,其实都是同一个实现的。

Swoft查询器,提供可使用面向对象的方法操作数据库。

Swoft提供了一套基础查询,类ActiveRecord方法,方便快捷的实现数据库操作,但实体必须先继承Model类,且不能使用事务。如果需要使用事务,需使用高级查询。

Swoft基础查询提供延迟收包和非延迟收包两种方式,非延迟收包一般用于并发使用。

配置数据库

创建数据表

CREATETABLE`user`(`id`int(11)unsignedNOTNULLAUTO_INCREMENT,`name`varchar(20)CHARACTERSETutf8mb4COLLATEutf8mb4_unicode_ciDEFAULT''COMMENT'名称',`age`tinyint(1)unsignedDEFAULT'0'COMMENT'年龄',`sex`tinyint(1)unsignedDEFAULT'0'COMMENT'性别',`description`varchar(255)CHARACTERSETutf8mb4COLLATEutf8mb4_unicode_ciDEFAULT''COMMENT'描述',  PRIMARYKEY(`id`))ENGINE=InnoDBAUTO_INCREMENT=3DEFAULTCHARSET=utf8mb4COLLATE=utf8mb4_unicode_ciCOMMENT='用户表';

配置数据库连接

$ vim .env

# 数据库主节点配置# 连接池节点名称用于服务发现DB_NAME=dbMaster# 连接地址信息DB_URI=192.168.99.100:3306/test?user=jc&password=junchow&charset=utf8,192.168.99.100:3306/test?user=jc&password=junchow&charset=utf8# 最小活跃连接数DB_MIN_ACTIVE=5# 最大活跃连接数DB_MAX_ACTIVE=10# 最大等待连接DB_MAX_WAIT=20# 最大等待时间DB_MAX_WAIT_TIME=3# 连接最大空闲时间DB_MAX_IDLE_TIME=60# 连接超时时间DB_TIMEOUT=2

如果对数据库主从不是很清晰,可参见《MySQL读写分离主从复制》。

创建实体模型

实体模型继承自Model类,位于swoft/db/src/Model.php。

实体模型主要针对单表简单CURD操作,不支持事务,但支持并发查询。

创建实体命令帮助

$ php bin/swoft entity:create -hDescription:  Auto create entity by table structureUsage:  entity:create -d[|--database]   entity:create -d[|--database] [table]  entity:create -d[|--database] -i[|--include]   entity:create -d[|--database] -i[|--include]   entity:create -d[|--database] -i[|--include] -e[|--exclude]   entity:create -d[|--database] -i[|--include] -e[|--exclude] Options:  -d                    数据库  --database            数据库  -i                    指定特定的数据表,多表之间用逗号分隔  --include            指定特定的数据表,多表之间用逗号分隔  -e                    排除指定的数据表,多表之间用逗号分隔  --exclude            排除指定的数据表,多表之间用逗号分隔  --remove-table-prefix 去除表前缀  --entity-file-path    实体路径(必须在以@app开头并且在app目录下存在的目录,否则将会重定向到@app/Models/Entity)Example:  php bin/swoft entity:create -d test

使用命令行创建实体

php bin/swoft entity:create -d test user

注意:如果命令中缺少-d参数则会报错databases doesn't not empty!。

创建成功后会生成模型的实体文件

$ vim app/Models/Entity/User.php

id;    }/**    *@paramint|null $id    */publicfunctionsetId($id){$this->id = $id;    }/**    *@returnnull|string    */publicfunctiongetName(){return$this->name;    }/**    *@paramnull|string $name    */publicfunctionsetName($name){$this->name = $name;    }/**    *@returnint    */publicfunctiongetAge():int{return$this->age;    }/**    *@paramint $age    */publicfunctionsetAge(int $age){$this->age = $age;    }/**    *@returnint    */publicfunctiongetSex():int{return$this->sex;    }/**    *@paramint $sex    */publicfunctionsetSex(int $sex){$this->sex = $sex;    }/**    *@returnstring    */publicfunctiongetDesc():string{return$this->desc;    }/**    *@paramstring $desc    */publicfunctionsetDesc(string $desc){$this->desc = $desc;    }/**    *@returnmixed    */publicfunctiongetOtherProperty(){return$this->otherProperty;    }/**    *@parammixed $otherProperty    */publicfunctionsetOtherProperty($otherProperty){$this->otherProperty = $otherProperty;    }}

在此需要注意的是虽然数据表中使用是description, 但使用@Column(name="description", type="string")。

/** * 描述 * *@Column(name="description", type="string") *@varstring */private$desc ='';

AR操作

关于ActiveRecord,可以理解为一个不同SQL数据库的Wrapper,同时为上层提供一种简洁优雅的API或DSL,能极大地减轻开发者的负担并提升工作效率。

Swoft ORM AR 活动记录_第5张图片

ActiveRecord

由于每个AR类代表一个数据表或视图,数据表或视图的列在AR类中体现为类的属性,一个AR实例则表示为数据表中的一行。常见的CURD增删改查操作作为AR的方法实现。因此,可以以一种更加面向对象的方式访问数据。

新增

setter使用对象方式插入数据

直接返回结果

//使用ORM的ActiveRecord$model =newUser();$model->setName("junchow");$model->setSex(1);$model->setAge(mt_rand(1,100));$model->setDesc("this is a test");$result = $model->save()->getResult();var_dump($result);//int(3)

容器中开启日志并查看输出

docker@default ~$ docker logs myswoft -ft2019-04-25T09:00:54.596389052Z int(3)

对比观察,发现返回的结果$result与数据库的自增主键id保持一致。

延迟操作

//使用ORM的ActiveRecord$model =newUser();$model->setName("junchow");$model->setSex(1);$model->setAge(mt_rand(1,100));$model->setDesc("this is a test");$result = $model->save(true)->getResult();var_dump($result);

使用数组方式保存数据

//使用ORM的ActiveRecord$model =newUser();//使用数组方式保存数据$model["name"] ="bob";$model["sex"] =1;$model["age"] =20;$model["desc"] ="this is a description demo";$query = $model->save();$result = $query->getResult();//直接输出var_dump($result);

注意:这里使用的是实体属性desc而非数据表中的description。

fill使用数组方式填充数据

//使用ORM的ActiveRecord$model =newUser();//使用数组方式填充数据$data = [];$data["name"] ="alice";$data["sex"] =1;$data["age"] =20;$data["desc"] ="this is a description demo";$query = $model->fill($data)->save();$result = $query->getResult();//直接输出var_dump($result);

注意:这里使用的是实体属性desc而非数据表中的description。

batchInsert使用批量插入方式添加数据

//使用ORM的ActiveRecord$rows = [];//使用数组方式保存数据$row = [];$row["name"] ="bob";$row["sex"] =1;$row["age"] =20;$row["description"] ="this is a description demo";$rows[] = $row;//使用数组方式保存数据$row = [];$row["name"] ="carl";$row["sex"] =1;$row["age"] =21;$row["description"] ="this is a description demo";$rows[] = $row;$query = User::batchInsert($rows);$result = $query->getResult();//批量插入var_dump($result);

注意:

这里使用的是数据表中的description,而非实体属性desc。

批量插入后返回的是第一条记录的自增主键ID

删除

deleteById使用主键删除单条记录

$id =2;$query = User::deleteById($id);$result = $query->getResult();var_dump($result);

返回值:删除记录的条数

deleteByIds使用主键删除多条记录

$ids = [];$ids[] =3;$ids[] =4;$ids[] =5;$query = User::deleteByIds($ids);$result = $query->getResult();var_dump($result);

返回值$result是删除记录的条数

deleteOne删除单条数据

$where = [];$where["name"] ="bob";$where["age"] =20;$query = User::deleteOne($where);$result = $query->getResult();var_dump($result);

删除条件:删除name为bob而且age等于20的记录,注意这里条件是AND的关系。

返回值:删除记录的条数

deleteAll删除多条数据

$where = [];$where["id"] = [6,7];$where["age"] =21;$query = User::deleteAll($where);$result = $query->getResult();var_dump($result);

删除条件:删除用户编号为6或7,而且年龄为21的记录。

返回值:删除记录条数

修改

注意:由于在数据库中获取数据属于读操作,修改、新增属于写操作,因此需要注意配置文件中的主从和读写。

update使用实体的方式更新记录

$id =1;//根据用户ID判断用户是否存在$query = User::findById($id);$user = $query->getResult();if(!empty($user)){    var_dump($user);//重新设置姓名$user->setName("lisa");//更新用户数据$result = $user->update()->getResult();    var_dump($result);}

updateOne更新单条数据

$update = [];$update["name"] ="superman";$where = [];$where["id"] =11;$query = User::updateOne($update, $where);$result = $query->getResult();var_dump($result);

返回值:成功返回受影响行数,上例$result为1。

updateAll更新多条数据

$update = [];$update["name"] ="lucy";$where = [];$where["id"] = [10,20,30];$query =  User::updateAll($update, $where);$result =$query->getResult();var_dump($result);

返回值:成功返回受影响行数,上例$result为3。

查询

使用AR实体查询,返回结果为实体对象,而非数组。

1. 获取单条数据findOne

$fields = ["name","sex"];$query = User::findOne($where, $fields);$result = $query->getResult();var_dump($result);

返回值:

object(App\Models\Entity\User)#1413 (7) {["id":"App\Models\Entity\User":private]=>  int(10)  ["name":"App\Models\Entity\User":private]=>  string(4)"lucy"["age":"App\Models\Entity\User":private]=>  int(21)  ["sex":"App\Models\Entity\User":private]=>  int(1)  ["desc":"App\Models\Entity\User":private]=>  string(26)"this is a description demo"["otherProperty":"App\Models\Entity\User":private]=>NULL["attrs":"Swoft\Db\Model":private]=>array(5) {    ["id"]=>    int(10)    ["name"]=>    string(4)"lucy"["age"]=>    int(21)    ["sex"]=>    int(1)    ["desc"]=>    string(26)"this is a description demo"}}

观察可以发现返回的是一个object实体对象,如果要从实体对象中获取属性,则需使用getter方法。

var_dump($result->getName());//string(4) "lucy"var_dump($result->getSex());//int(1)var_dump($result->getDesc());//string(26) "this is a description demo"

若需要将object实体对象转化为数组,可使用toArray()方法。

$arr = $result->toArray();

2. 查询多条数据findAll

用法

/**

  * 查看多条数据

  * $condition 数组 查询条件

  * $options 数组 额外条件如orderby、limit、offset

  */findAll(array$condition = [],array$options = [])

用法1

$condition = [];$condition["id"] = [20,21,22,23,24,25];$condition["sex"] =1;$options = [];$options["orderby"] = ["age"=>"DESC"];$optoins["limit"] =10;$query = User::findAll($condition, $options);$result = $query->getResult();var_dump($result);

返回值

object(Swoft\Db\Collection)#1421 (1) {["items":protected]=>array(3) {    [0]=>    object(App\Models\Entity\User)#1410 (7) {["id":"App\Models\Entity\User":private]=>      int(21)      ["name":"App\Models\Entity\User":private]=>      string(10)"1556193516"["age":"App\Models\Entity\User":private]=>      int(57)      ["sex":"App\Models\Entity\User":private]=>      int(1)      ["desc":"App\Models\Entity\User":private]=>      string(14)"this is a test"["otherProperty":"App\Models\Entity\User":private]=>NULL["attrs":"Swoft\Db\Model":private]=>array(5) {        ["id"]=>        int(21)        ["name"]=>        string(10)"1556193516"["age"]=>        int(57)        ["sex"]=>        int(1)        ["desc"]=>        string(14)"this is a test"}    }    [1]=>    object(App\Models\Entity\User)#1419 (7) {["id":"App\Models\Entity\User":private]=>      int(22)      ["name":"App\Models\Entity\User":private]=>      string(10)"1556193538"["age":"App\Models\Entity\User":private]=>      int(49)      ["sex":"App\Models\Entity\User":private]=>      int(1)      ["desc":"App\Models\Entity\User":private]=>      string(14)"this is a test"["otherProperty":"App\Models\Entity\User":private]=>NULL["attrs":"Swoft\Db\Model":private]=>array(5) {        ["id"]=>        int(22)        ["name"]=>        string(10)"1556193538"["age"]=>        int(49)        ["sex"]=>        int(1)        ["desc"]=>        string(14)"this is a test"}    }    [2]=>    object(App\Models\Entity\User)#1420 (7) {["id":"App\Models\Entity\User":private]=>      int(23)      ["name":"App\Models\Entity\User":private]=>      string(10)"1556193540"["age":"App\Models\Entity\User":private]=>      int(19)      ["sex":"App\Models\Entity\User":private]=>      int(1)      ["desc":"App\Models\Entity\User":private]=>      string(14)"this is a test"["otherProperty":"App\Models\Entity\User":private]=>NULL["attrs":"Swoft\Db\Model":private]=>array(5) {        ["id"]=>        int(23)        ["name"]=>        string(10)"1556193540"["age"]=>        int(19)        ["sex"]=>        int(1)        ["desc"]=>        string(14)"this is a test"}    }  }}

观察可以发现,返回值是一个Swoft\Db\Collection类型的object。

用法2:排序条件与分页设置

$condition = [];$condition["sex"] =1;$condition[] = ["id",">",30];

分页设置一般是由offset偏移量和分页条数limit共同构成,例如:SQL语句limit 0,10表示从第0条开始向后取10条。

$options = [];$options["orderby"] = ["age"=>"DESC"];$optoins["offset"] =1;$optoins["limit"] =10;

排序条件$options["orderby"] = ["age"=>"DESC"];

偏移量(从第n条开始)$optoins["offset"] = 1;

条数限制$optoins["limit"] = 10;

因此,如果在分页中需要根据页码和每页条数来获取数据。

//分页$page =1;//页码$pagesize =5;//每页显示条数$offset = ($page -1) * $pagesize;$options["offset"] = $offset;$options["limit"] = $pagesize;

3. 根据主键查询单条数据findById

$id = 22;$query = User::findById($id);$result =$query->getResult();var_dump($result);

4. 根据主键查询多条数据findByIds

多个主键以数组方式[]使用

返回值是Swoft\Db\Collection类型的object

$ids = [21,22,23];$options = [];$options["fields"] = ["name","sex"];$options["orderby"] = ["id"=>"DESC"];$options["limit"] =2;$query = User::findByIds($ids, $options);$result = $query->getResult();var_dump($result);

5. 判断主键是否存在exists

若主键存在则返回true否则返回false

$id =10;$query = User::exist($id);$result = $query->getResult();var_dump($result);//bool(true)

6. 查询计数count

返回满足条件的行数

$fields ="id";$options = [];$options["id"] = [10,11];$query = User::count($fields, $options);$result = $query->getResult();var_dump($result);//string(1) "2"

小结

在了解ORM对象关系映射之后,常见使用ORM做数据查询的方式有两种AR和DataMapping,AR主要针对单表简单的查询,DataMapping主要针对复杂的多条查询。这里需要注意的一点的是由于数据库的操作主要分为读操作和写操作,如果提前在配置文件中使用读写分离,将读数据库和写数据库分开后,使用AR是查询操作实际上隶属于读操作,而插入、更新、删除隶属于写操作,这一点将在后续针对性的分析。接下来,需要挖掘的针对多表连表时的操作,DataMapping是如何使用的呢

出自:https://www.jianshu.com/p/0e96fa1a13c1

你可能感兴趣的:(Swoft ORM AR 活动记录)