第7章 数据库访问与ORM
https://coding.imooc.com/learn/list/97.html
目录:
- 7-1 数据库操作三种方式之原生SQL 19:09
- 7-2 从一个错误了解Exception的继承关系 13:23
- 7-3 TP5数据库中间层架构解析 23:06
- 7-4 查询构造器 一 13:28
- 7-5 查询构造器 二 13:20
- 7-6 查询构造器 三 11:12
- 7-7 开启SQL日志记录 16:07
- 7-8 ORM与模型 10:19
- 7-9 初识模型 17:47
- 7-10 模型定义总结 08:33
- 7-11 静态调用还是实例对象调用 09:59
- 7-12 几种查询动词的总结与ORM性能问题的探讨 16:47
7-1 数据库操作三种方式之原生SQL 19:09
thinkphp5支持的数据类型:
目前包含了Mysql、SqlServer、PgSQL、Sqlite等数据库的支持。
配置数据库连接信息:
默认是在applicationdatabase.php里配置的,
可以使用多个数据库连接,使动态定义连接信息用
动态定义连接信息:
Db::connect($config)->query('select * from think_user where id=:id',['id'=>8]);
$config是一个单独的数据库配置,支持数组和字符串,也可以是一个数据库连接的配置参数名。
或者使用字符串方式:
Db::connect('mysql://root:1234@127.0.0.1:3306/thinkphp#utf8');
例子:
$user= hinkDb::connect('mysql://root:root@127.0.0.1:3306/tp5#utf8')->query('select * from user where id=?',[$id]);
字符串连接的定义格式为:
数据库类型://用户名:密码@数据库地址:数据库端口/数据库名#字符集
详细参考说明文档:
https://www.kancloud.cn/manual/thinkphp5/118059
thinkphp5,操作数据库的方式
- 使用原生的sql语句查询数据库.
- 使用构造器来查询查询数据库.
- 使用模型以及关连模型来查询数据库
1.使用原生的sql语句查询数据库.
配置了数据库连接信息后,我们就可以直接使用数据库运行原生SQL操作了,支持query
(查询操作)和execute
(写入操作)方法,并且支持参数绑定。
1 Db::query('select * from think_user where id=?',[8]); 2 Db::execute('insert into think_user (id, name) values (?, ?)',[8,'thinkphp']);
也支持命名占位符绑定,例如:
1 Db::query('select * from think_user where id=:id',['id'=>8]); 2 Db::execute('insert into think_user (id, name) values (:id, :name)',['id'=>8,'name'=>'thinkphp']);
这样就不需要use thinkDb; 这句了。
thinkDb::query('select * from think_user where id=?',[8]);
Db::query('select * from think_user where name like :name',['name'=>'%德华%']);
Db::execute('insert into think_user (id, name) values (:id, :name)',['id'=>8,'name'=>'thinkphp']);
1 <?php 2 namespace appindexcontroller; 3 use hinkController; 4 use hinkDb; 5 6 class Index extends Controller 7 { 8 public function index() 9 { 10 $data=Db::query('select * from sys_user where id=?',[1]); 11 dump($data); 12 } 13 }
实例:
要实现的功能:
控制器调用model层,从表里查出数据,model层将查出来的结果传给控制器,
控制器再返回json模式的数据
appapicontrollerv1Banner.php控制器里:
1 <?php 2 /** 3 * Created by PhpStorm. 4 * User: Haima 5 * Date: 2018/7/8 6 * Time: 15:58 7 */ 8 9 namespace appapicontrollerv1; 10 use appapimodelBanner as BannerModel; 11 use appapivalidateIDMustBePostiveInt; 12 use applibexceptionBannerMissException; 13 14 class Banner 15 { 16 /** 17 * 获取指定id的banner信息 18 * @url /banner/:id 19 * @http GET 20 * @id banner的id号 21 */ 22 public function getBanner($id) 23 { 24 25 (new IDMustBePostiveInt())->goCheck(); //验证$id是否为正整数 26 $banner = BannerModel::getBannerById($id);//调用model 27 if (!$banner){ 28 throw new BannerMissException(); //判断结果不存在,抛出异常 29 } 30 return json($banner,200);//返回json格式的结果, 默认就是200状态码,可不写 31 } 32 }
appapimodelBanner.php的模型里:
1 <?php 2 /** 3 * Created by PhpStorm. 4 * User: Haima 5 * Date: 2018/7/12 6 * Time: 1:16 7 */ 8 9 namespace appapimodel; 10 11 12 use thinkDb; 13 14 class Banner 15 { 16 public static function getBannerById($id){ 17 //TODO 根据Banner 的 ID号 获取Banner信息 18 $result=Db::query('select * from banner_item where banner_id = ?',[$id]); 19 return $result; 20 21 } 22 }
访问结果:
查出了四条数据
z.com/api/v1/banner/1?XDEBUG_SESSION_START=13378
7-2 从一个错误了解Exception的继承关系 13:23
7-3 TP5数据库中间层架构解析 23:06
常见模式:
连接上数据库后,直接写增删改查(不白,不推荐)
框架弄的模式(推荐)
Db的作用:
- 是数据库操作的一入口文件,
- 用来连接数据库.
总结:
如果使用原生的SQL语句,Db就就可以使用Collection(连接器类)直接去操作数据库了.
如果使用的Query语句,那么就需要使用Buider编译为原生的SQL语句再通过Collection直接去操作数据库了
Drivers
根据配置文件里找到对应的数据库连接类型,从而操作数据库.
同一个标准的Query查询器,调用不同的Buidle生成器来解析Query查询器,从而来适配不同的数据库
7-4 查询构造器 一 13:28
7-5 查询构造器 二 13:20
table()和where()叫作链式方法或者辅助方法,它不会真正执行sql语句,
它会得到一个query对象,然后再调用里面的执行sql语句方法
select()和find()是查询方法,是真正的执行sql语句方法
执行sql语句方法还有update(),delete(),insert();
tp5里有很多链式方法:
- 不相同的连式方法是没有先后顺序的.
- 相同的链式方法,它们的先后顺序可能会得到不同的结果.例如有两个order.
- 一次Db在调用select()这样的执行sql语句的方法后,它的状态就会被清除了.
- 非链式调用的方法,直到调用select()这样的执行sql语句的方法后,它的状态才会被清除了
1 <?php 2 /** 3 * Created by PhpStorm. 4 * User: Haima 5 * Date: 2018/7/12 6 * Time: 1:16 7 */ 8 9 namespace appapimodel; 10 use thinkDb; 11 12 class Banner 13 { 14 public static function getBannerById($id){ 15 //TODO 根据Banner 的 ID号 获取Banner信息 16 //原生sql语句方法(不建议使用,这样就失去了tp5框架sql和build构造sql的意义了) 17 //$result = Db::query('select * from banner_item where banner_id = ?',[$id]); 18 //table调用 19 //$result = Db::table('banner_item')->where('banner_id','=',$id)->select(); 20 //db助手函数方法 21 //$result = db('banner_item')->where('banner_id','=',$id)->select(); 22 //非链式操作方法 23 Db::table('banner_item'); 24 Db::where('banner_id',$id); 25 $result = Db::select(); //直接这一步才会清除query对象 26 27 return $result; 28 29 } 30 }
执行结果:
7-6 查询构造器 三 11:12
查询表达式
手册:
https://www.kancloud.cn/manual/thinkphp5/135182
查询表达式支持大部分的SQL查询语法,也是ThinkPHP
查询语言的精髓,查询表达式的使用格式:
1 where('字段名','表达式','查询条件'); 2 whereOr('字段名','表达式','查询条件');
表达式不分大小写,支持的查询表达式有下面几种,分别表示的含义是:
表达式 | 含义 |
---|---|
EQ、= | 等于(=) |
NEQ、<> | 不等于(<>) |
GT、> | 大于(>) |
EGT、>= | 大于等于(>=) |
LT、< | 小于(<) |
ELT、<= | 小于等于(<=) |
LIKE | 模糊查询 |
[NOT] BETWEEN | (不在)区间查询 |
[NOT] IN | (不在)IN 查询 |
[NOT] NULL | 查询字段是否(不)是NULL |
[NOT] EXISTS | EXISTS查询 |
EXP | 表达式查询,支持SQL语法 |
> time | 时间比较 |
< time | 时间比较 |
between time | 时间比较 |
notbetween time | 时间比较 |
1 <?php 2 /** 3 * Created by PhpStorm. 4 * User: Haima 5 * Date: 2018/7/12 6 * Time: 1:16 7 */ 8 9 namespace appapimodel; 10 use thinkDb; 11 12 class Banner 13 { 14 public static function getBannerById($id){ 15 //TODO 根据Banner 的 ID号 获取Banner信息 16 //原生sql语句方法(不建议使用,这样就失去了tp5框架sql和build构造sql的意义了) 17 //$result = Db::query('select * from banner_item where banner_id = ?',[$id]); 18 //table调用 19 //$result = Db::table('banner_item')->where('banner_id','=',$id)->select(); 20 //db助手函数方法 21 //$result = db('banner_item')->where('banner_id','=',$id)->select(); 22 //非链式操作方法 23 // Db::table('banner_item'); 24 // Db::where('banner_id',$id); 25 // $result = Db::select(); //直接这一步才会清除query对象 26 27 //构造query对象的三种方法:(表达式,数组,闭包) 28 //表达式 (建议使用这种) 29 // $result = db('banner_item')->where('banner_id','=',$id)->select(); 30 //数组 31 // $result = db('banner_item')->where(['banner_id'=>$id])->select(); 32 //闭包 33 $order='id desc'; 34 $result = Db::select(function($query) use($id,$order){ 35 $query->table('banner_item') 36 ->where('banner_id','=',$id) 37 ->order($order) 38 ->limit(2); 39 }); 40 return $result; 41 42 } 43 }
执行结果:
7-7 开启SQL日志记录 16:07
fetchSql用于直接返回SQL而不是执行查询,适用于任何的CURD操作方法。 例如:
返回要执行的sql语句
1 //数组 2 $result = db('banner_item') 3 ->where(['banner_id'=>$id]) 4 ->fetchSql() //返回要执行的sql语句 5 ->select();
开启thinkphp5默认的sql全局日志保存功能:
database.php里
// 开启数据库调试模式
config里开启调试模试:
type=File全局日志保存记录
LOG_PATH 定义的是
defined('LOG_PATH') or define('LOG_PATH', RUNTIME_PATH . 'log' . DS);
运行会产生log日志
model里
访问一下接口
产生日志:
下面我们自定义全局sql日志:
思路,:所有的请求,都会经过public/index.php,所以在这里初始化自定义sql的全局日志
这里datebase.php里的开启数据库调试模式为ture
config.php里开启debug调适
log里type='test',关闭全局的
path路径设置好日志生成的路径
level等级为sql
public/index.php里
16行,设义修改log日志保存的地方
再访问接口就会生成sql的log日志
在项目的根目录log里的就会产生sql日志
7-8 ORM与模型 10:19
7-9 初识模型 17:47
db属于数据库访问层
model模型不是数据库的访问层,它是建立在数据库访问层之上一个更加抽象的处理业务逻辑的模型层.
use thinkModel;
class Banner extends Model //在model的控制器里继承Model以后,Banner就变成一个模型层了
Model层里,banner.php的代码
1 <?php
2 /**
3 * Created by PhpStorm.
4 * User: Haima
5 * Date: 2018/7/12
6 * Time: 1:16
7 */
8
9 namespace appapimodel;
10 use thinkDb;
11 use thinkModel;
12
13 class Banner extends Model //继承Model以后,Banner就变成一个模型层了
14 {
15 public static function getBannerById($id){
16 //TODO 根据Banner 的 ID号 获取Banner信息
17
18 //构造query对象的三种方法:(表达式,数组,闭包)
19
20 //数组
21 $result = db('banner_item')
22 ->where(['banner_id'=>$id])
23 // ->fetchSql() //返回要执行的sql语句
24 ->select();
25
26 return $result;
27
28 }
29 }
控制器层,Banner.php代码
1 <?php
2 /**
3 * Created by PhpStorm.
4 * User: Haima
5 * Date: 2018/7/8
6 * Time: 15:58
7 */
8
9 namespace appapicontrollerv1;
10 use appapimodelBanner as BannerModel;
11 use appapivalidateIDMustBePostiveInt;
12 use applibexceptionBannerMissException;
13
14 class Banner
15 {
16 /**
17 * 获取指定id的banner信息
18 * @url /banner/:id
19 * @http GET
20 * @id banner的id号
21 */
22 public function getBanner($id)
23 {
24 (new IDMustBePostiveInt())->goCheck(); //验证$id是否为正整数
25
26 //调用model层的方法:
27 //方法一:静态调用(当控制器和类名一样时用别名引入)
28 // 这样调用model层,model的类不需要继承Model类,
29 //但是调用的方法里需要自己个方法,在方法里写代码对库里的表进行操作
30 // $banner = BannerModel::getBannerById($id);//调用model
31
32 //方法二:
33 //这样调用model层,model层里的类要继承Model类
34 //model层里不用自己再写方法和代码对库里的表进行操作了
35 $banner= BannerModel::get($id);
36 if (!$banner){
37 throw new BannerMissException(); //判断结果不存在,抛出异常
38 }
39 return json($banner,200);//返回json格式的结果, 默认就是200状态码,可不写
40 }
41 }
方法一,Db返回的是一个数组
方法二,模型的方法返回的是一个模型的对象,属于orm查询,它是以对象的方式来思考数据库,以对象的方式来调用数据库,结果也是以对象的方式返回
这里要注意的是使用orm模型的方式,去查询, 返回的结果,tp5会自动把结果序列化,不需要再return json($result)了
这里需要注意的是返回的是一上一条记录,说明一个模型对应该它同名的表,这里查出来的是banner表里的数据,所以只查出来的一条结果
config.php 里需要配置一下输出类型为json:
// 默认输出类型
'default_return_type' => 'json',
访问返回的结果依然是json形式的:
7-10 模型定义总结 08:33
- 模型返回的结果就是一个对象
- 模型都需要继承thinkModel的
- 当业务比较简单的时候,通常是一个模型对应着一张表的
- 大一些业务会是一个模型对应着多张表的
- 默认model的类名(也就是模型层)是对应库里同名的表名的(tp5就知道他们之前是有映色的关系).
- 数据库里的每张表看着作一个对象.
在数据库表里有主表和从表的概念.如何把主从表的概念应射到我们现在的orm对象当中来的这种方式f?
tp5就是用关联模式的方案对应表里的主从表.