原文地址:http://blog.onlywan.cc/14843810761202.html

Laravel Eloquent使用小记

今天由于开发数据库业务中间层须要。開始研究Laravel Eloquent,由于刚開始使用laravel框架的时候,都是使用query,查询构建器来写sql相似于

DB::connection('mydb')->table('mylove')
->where( 'name', 'guowan' )
->get();

复杂一点的sql使用db::raw

DB::connection('mydb')->table('mylove')->select( DB::RAW( 'count("name") as mylovecount' ) )
->where( 'name', 'guowan' )
->get();

本着在工作中学习的态度開始研究Eloquent,对着laravel中文文档。開始设计Eloquent Model。这里给出表大概字段(因兼容老系统要求。表字段设计与当前业务不相符,这里不与讨论~)

表结构

CREATE TABLE `user_ext` (
`user_id` int(10) NOT NULL,
`realname` varchar(255) DEFAULT NULL,
`gender` int(11) NOT NULL DEFAULT '0',
`birthday` datetime DEFAULT NULL,
`comefrom` varchar(255) DEFAULT NULL,
`qq` varchar(255) DEFAULT NULL,
`weibo` varchar(255) DEFAULT NULL,
`blog` varchar(255) DEFAULT NULL,
`mobile` varchar(255) DEFAULT NULL,
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 CREATE TABLE `user` (
`user_id` int(10) NOT NULL AUTO_INCREMENT,
`username` varchar(100) DEFAULT NULL,
`email` varchar(255) DEFAULT NULL,
`user_img` varchar(255) DEFAULT NULL,
) ENGINE=InnoDB DEFAULT CHARSET=utf8

创建Eloqueue Model

  • user
<?php

namespace App\Http\Models\Eloquent;

use Illuminate\Database\Eloquent\Model;

class CUser extends Model
{
/**
* 与模型关联的数据表。
*
* @var string
*/
protected $table = 'user'; /*
* 数据库表主键
*
* @var string
*/
protected $primaryKey = 'user_id'; /*
* 取消自己主动维护create_at,update_at字段
*
* @var string
*/
public $timestamps = false; /*
* 获取与指定用户相关联的扩展信息记录
*/
public function hasOneExt()
{
return $this->hasOne( 'App\Http\Models\Eloquent\CUserExt', 'user_id', 'user_id' );
}
}
  • user_ext
<?php

namespace App\Http\Models\Eloquent;

use Illuminate\Database\Eloquent\Model;

class CUserExt extends Model
{ /**
* 与模型关联的数据表。
*
* @var string
*/
protected $table = 'ac_user_ext'; /*
* 数据库表主键
*
* @var string
*/
protected $primaryKey = 'user_id'; /*
* 取消自己主动维护create_at,update_at字段
*
* @var string
*/
public $timestamps = false; public function acUser()
{
return $this->belongsTo( 'App\Http\Models\Eloquent\CUser' );
}
}

user与user_ext表为1对1关系。

注意

user model中的hasOneExt方法。之所以使用hasOneExt方法名,是通过方法命名,在调用方法的时候就能够知道和userExt表的关系。

hasOne函数,第一个參数是类路径;第二个參数外键。也就是userExt表的主键;第三个參数才是user表主键。自己使用的时候,没有指定第二个和第三个參数,会出现错误

问题

以下才是今天记录的主要内容,在使用过程中,出现一些问题。以及问题对应的解决方法,可能有些问题还没有解决或者解决的不好,这里记录一下,添加一下印象,也能够和其它同学一块讨论一下

1. 依赖方法hasOneExt

调用以下方法

$oUser = CUser::find( $sUMId )->hasOneExt();

结果居然返回UserExt表中数据。

我的本意本来想做对应的关联查询。查出两个表的数据。然后在网上各种搜索Eloquent两表联查。返回两表字段。

最终解决方式例如以下:

$oUser = CAcUser::with( 'hasOneExt' )->find( $sUMId );

查询结果:

Array
(
[user_id] => 1
[username] => admin
[email] => wanguowan521@163.com
[user_img] => 201303/26132122j2lg.jpg
[has_one_ext] => Array
(
[user_id] => 1
[realname] => 瞌睡
[gender] => 1
[birthday] =>
[comefrom] => **,不限
[qq] =>
[weibo] =>
[blog] =>
[mobile] =>
) )

这里依赖表数据用法名作为key成为返回结果的一部分,这个对于业务接口,须要一维数组的时候还得须要翻译。

幸好对于业务层来说。希望屏蔽底层数据层字段细节。本来就须要做一次翻译。所以这里也就不是什么大问题。

这里with语法。是Eloquent中所谓的预载入语法,主要是为了解决ORM(Object Relation Mapping) n+1次查询问题–具体说明。在网上查询过程中,这里尽管是1对1关系,可是假设这样解决,会将一次join查询,变成两次查询。对于将来高并发场景来说,有点不能接受。

可是不这样解决又没有找到其它解决方法。

无奈,尝试打印Eloquent运行的sql,查看具体的sql语句(打印laravel运行sql方法比較多,能够參考资料),代码例如以下:

DB::enableQueryLog();

$oUser = CUser::with( 'hasOneExt' )->find( $sUMId );

print_r(
DB::getQueryLog()
);

打印结果例如以下:

Array
(
[0] => Array
(
[query] => select * from `user` where `user`.`user_id` = ? limit 1
[bindings] => Array
(
[0] => 1
) [time] => 0.56
) [1] => Array
(
[query] => select * from `user_ext` where `user_ext`.`user_id` in (?)
[bindings] => Array
(
[0] => 1
) [time] => 0.32
) )

能够看出。sql先依据user_id查询到主标数据,然后在去依赖表中做in查询,这样确实攻克了ORM n+1次查询的问题,可是对于直接使用sql,还是多出一次查询。

这里发现一个比較有趣的事情。log里有一个time值,难道这个是sql运行时间。假设这个是运行时间的话。那就能够简单的验证一下sql运行效率问题了,然后開始查询资料。最终在源代码中找到了答案,源代码例如以下:具体链接

    /**
* Run a SQL statement and log its execution context.
*
* @param string $query
* @param array $bindings
* @param Closure $callback
* @return mixed
*
* @throws QueryException
*/
protected function run($query, $bindings, Closure $callback)
{
$start = microtime(true);
// To execute the statement, we'll simply call the callback, which will actually
// run the SQL against the PDO connection. Then we can calculate the time it
// took to execute and log the query SQL, bindings and time in our memory.
try
{
$result = $callback($this, $query, $bindings);
}
// If an exception occurs when attempting to run a query, we'll format the error
// message to include the bindings with SQL, which will make this exception a
// lot more helpful to the developer instead of just the database's errors.
catch (\Exception $e)
{
throw new QueryException($query, $bindings, $e);
}
// Once we have run the query we will calculate the time that it took to run and
// then log the query, bindings, and execution time so we will report them on
// the event that the developer needs them. We'll log time in milliseconds.
$time = $this->getElapsedTime($start);
$this->logQuery($query, $bindings, $time);
return $result;
} /**
* Get the elapsed time since a given starting point.
*
* @param int $start
* @return float
*/
protected function getElapsedTime($start)
{
return round((microtime(true) - $start) * 1000, 2);
}

这里能够看出time就是sql运行时间,并且单位是毫秒.

这里就能够測试单条join和使用eloquent with查询效率对照,代码例如以下:

DB::enableQueryLog();

DB::table(  'user' )
->leftJoin( 'user_ext as ext', 'user.user_id', '=', 'ext.user_id' )
->where( 'user.user_id', 1 )
->get(); $oUser = CUser::with( 'hasOneExt' )->find( $sUMId ); print_r(
DB::getQueryLog()
);

结果例如以下:

Array
(
[0] => Array
(
[query] => select * from `user` as `user` left join `user_ext` as `ext` on `user`.`user_id` = `ext`.`user_id` where `user`.`user_id` = ?
[bindings] => Array
(
[0] => 1
) [time] => 0.65
) [1] => Array
(
[query] => select * from `user` where `user`.`user_id` = ? limit 1
[bindings] => Array
(
[0] => 1
) [time] => 0.35
) [2] => Array
(
[query] => select * from `user_ext` where `user_ext`.`user_id` in (?)
[bindings] => Array
(
[0] => 1
) [time] => 0.35
) )

从结果能够看出,运行一条时间相比运行两条时间,差距不是非常大,可是客观来说,这说明不了什么问题;首先,測试基于本地数据库,一次请求和两次请求的网络影响及延迟会比线上差距要小非常多;其次。本地測试数据库。两个表数据量都在1k。数据量太小,无法反应真实线上数据查询效率。所以这里查询结果仅供參考,后期具体结果,会在本地伪造100w左右数据量进行測试观察。并咨询公司dba。对于大数据量对连表查询效率影响情况。

总结

对于今天解决这个问题的过程,尽管感觉没有得到完美的答案,可是在查询过程中也学习到不少东西。在这里做一下记录。

以备后期温故学习。

这里记录一下几个小细节:

数据库查询过程中。为了节省应用server与数据库server之间网络流量及数据库server的IO,数据库查询原则是仅仅查询返回实用字段,对于没用的大字段。特别是text等,不须要时。尽量不查询。

数据库查询尽量不要使用selec *

Eloquent 联合查询指定字段

  • 方法1
$oUser = CUser::with( [ 'hasOneExt' => function( $query ) {
$query->select( 'user_id', 'realname', 'gender', 'birthday' );
} ] )->find( $sUMId, [ 'user_id', 'username', 'email', 'user_img' ] );

当中query−>select(′userid′,′realname′,′gender′,′birthday′)为查询扩展表字段。find(sUMId, [ ‘user_id’, ‘username’, ‘email’, ‘user_img’ ] )为查询主表字段

  • 方法2
public function hasOneExt() {
return $this->hasOne( 'App\Http\Models\Eloquent\CUserExt', 'user_id', 'user_id' )
->select( 'user_id', 'realname', 'gender', 'birthday' );
} $oUser = CUser::with( 'hasOneExt' )->find( $sUMId, [ 'user_id', 'username', 'email', 'user_img' ] );

运行sql结果:

Array
(
[0] => Array
(
[query] => select `user_id`, `username`, `email`, `user_img` from `user` where `user`.`user_id` = ? limit 1
[bindings] => Array
(
[0] => 1
) [time] => 0.5
) [1] => Array
(
[query] => select `user_id`, `realname`, `gender`, `birthday` from `user_ext` where `user_ext`.`user_id` in (?)
[bindings] => Array
(
[0] => 1
) [time] => 0.33
) )

欢迎加入公众号:


Laravel Eloquent使用小记的更多相关文章

  1. 判断Laravel Eloquent获取数据结果集是否为空

    在使用Laravel Eloquent模型时,我们可能要判断取出的结果集是否为空,但我们发现直接使用is_null或empty是无法判段它结果集是否为空的. var_dump之后我们很容易发现,即使取 ...

  2. 深入理解 Laravel Eloquent(三)——模型间关系(关联)

    Eloquent是什么 Eloquent 是一个 ORM,全称为 Object Relational Mapping,翻译为 "对象关系映射"(如果只把它当成 Database A ...

  3. Laravel Eloquent 判断取出的结果集是否为空

    在使用Laravel Eloquent模型时,我们可能要判断取出的结果集是否为空,但我们发现直接使用is_null或empty是无法判段它结果集是否为空的. var_dump之后我们很容易发现,即使取 ...

  4. Laravel Eloquent 自定义返回字段

    返回指定字段 Book::select("price", "name")->all(); 返回关系字段关联的属性 Book::select("p ...

  5. laravel Eloquent 查询数据库判断获取的内容是否为空

    原文地址:https://www.cnblogs.com/love-snow/articles/7205338.html 在使用 Laravel Eloquent 模型时,我们要判断取出的结果集是否为 ...

  6. Laravel Eloquent ORM

    Eloquent ORM 简介 基本用法 集体赋值 插入.更新.删除 软删除 时间戳 查询范围 关系 查询关系 预先加载 插入相关模型 触发父模型时间戳 与数据透视表工作 集合 访问器和调整器 日期调 ...

  7. Laravel Eloquent get获取空的数据问题

    在用laravel框架来获取数据,若数据不存在时,以为会返回空,其实不是的,其实是一个 collection 值,会返回如下: object(Illuminate\Database\Eloquent\ ...

  8. 20 个 Laravel Eloquent 必备的实用技巧

    Eloquent ORM 看起来是一个简单的机制,但是在底层,有很多半隐藏的函数和鲜为人知的方式来实现更多功能.在这篇文章中,我将演示几个小技巧. 1. 递增和递减 要代替以下实现: $article ...

  9. Laravel Eloquent ORM 时如何查询表中指定的字段

    导读:在使用Laravel ORM的Model方法find, get, first方法获取数据对象时返回的数据对象的attributes属性数组里会包含数据表中所有的字段对应...原文地址:http: ...

随机推荐

  1. C#程序证书创建工具 (Makecert.exe)

    原文地址:https://msdn.microsoft.com/zh-cn/library/bfsktky3(VS.80).aspx 证书创建工具生成仅用于测试目的的 X.509 证书.它创建用于数字 ...

  2. OpenCV 学习笔记 02 使用opencv处理图像

    1 不同色彩空间的转换 opencv 中有数百种关于不同色彩空间的转换方法,但常用的有三种色彩空间:灰度.BRG.HSV(Hue-Saturation-Value) 灰度 - 灰度色彩空间是通过去除彩 ...

  3. Javscript调用iframe框架页面中函数的方法

    Javscript调用iframe框架页面中函数的方法,可以实现iframe之间传值或修改值了, 访问iframe里面的函数: window.frames['CallCenter_iframe'].h ...

  4. MySQL -- Innodb是如何处理自增列的

    对于那些向带有自增列的表中插入行的语句,Innodb提供一种可配置的锁定机制,这种锁定机制可以显著提高SQL语句的可伸缩性和性能. Innodb中为了使用自增机制,自增列必须是索引的部份,从而可以使用 ...

  5. android 控件各种颜色的半透明效果配置

    格式: android:background="#XXxxxxxx"(颜色可以写在color中) 说明:半透明颜色值不同于平时使用的颜色,半透明颜色值共8位,前2位是透明度,后6位 ...

  6. Android静默安装实现方案,仿360手机助手秒装和智能安装功能

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/47803149 之前有非常多朋友都问过我.在Android系统中如何才干实现静默安装 ...

  7. eclipse 反编译插件 jadclipse

    1. 下载 JadClipse 下载JadClipse:http://jadclipse.sourceforge.net/wiki/index.php/Main_Page#Download 注意选择与 ...

  8. java项目中显示图表:struts2整合jfreechart

    需要的包: struts2-jfreechart-plugin-2.2.1.1.jar jfreechart-1.0.13.jar jcommon-1.0.17.jar 前台jsp页面中可以使用ifr ...

  9. c语言实现xor加密

    异或运算:^ 定义:它的定义是:两个值相同时,返回false,否则返回true.也就是说,XOR可以用来判断两个值是否不同. 特点:如果对一个值连续做两次 XOR,会返回这个值本身. ^ // 第一次 ...

  10. Atitit 项目源码的架构,框架,配置与环境说明模板 规范 标准化

    Atitit 项目源码的架构,框架,配置与环境说明模板  规范 标准化 版本1.0 作者 艾龙 attilax 1. 概述:核心业务: 1 1.1. 功能文档路径 /palmWin/src/docum ...