原文地址: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. SQL临时表

    临时表就是那些名称以井号 (#) 开头的表.如果当用户断开连接时没有除去临时表,SQL Server 将自动除去临时表.临时表不存储在当前数据库内,而是存储在系统数据库 tempdb 内.  临时表有 ...

  2. 解决『Manifest merger failed with multiple errors, see 』

    Error:Execution failed for task ':app:processDebugManifest'.> Manifest merger failed with multipl ...

  3. SharePoint下在Feature中动态Register/Remove HttpModule

    在SharePoint开发时,你会遇到这样一个问题,Global.asax去哪儿?怎样添加一个Global.asax?怎样在Application_Start这个事件处理程序里设置初始化?似乎在Vis ...

  4. java中基础数据类型的应用

    1.float 与 double float是单精度类型,占用4个字节的存储空间  double是双精度类型,占用8个字节的存储空间  1)当你不声明的时候,默认小数都用double来表示,所以如果要 ...

  5. [转]如何在Windows 10中更改文件夹背景颜色

    ini文件.我们甚至可以使用相同的技术将图片设置为文件夹背景. 已有工具可以更改Windows 7中Windows资源管理器背景的颜色,并将图像设置为Windows 7中的文件夹背景,但这些工具与Wi ...

  6. Java 8 – TemporalAdjusters examples

    1. TemporalAdjusters Example to move a date to firstDayOfMonth, firstDayOfNextMonth, next Monday and ...

  7. YMP运行初始化步骤

    , Version.VersionType.Release); private static final Log _LOG = LogFactory.getLog(YMP.class); privat ...

  8. iphone5 jail break

    dfu恢复恢复备份越狱afc2add删除2个文件,如果只有1个也没问题/var/mobile/Library/Caches/com.apple.mobile.installation.plist/va ...

  9. Matlab的集合运算[转]

    今天遇到一个问题:有向量a和向量b,b是a的子向量(元素全部来自a),求向量a去掉向量b后剩下的元素构成的向量. 这么一个简单的问题,搜了半天也没有得到结果,因为找不到合适的关键词来描述这个问题. 在 ...

  10. 记一次mysql的存储过程改写

    最近在对公司以前的老项目做整理,发现以前同事在程序中许多模块都是多次调用几个分散的存储过程..这样做无疑消耗了连接池的连接数,甚至会导致连接不够的时候创建连接池导致数据库处理的消耗..以及到处调用连接 ...