消息队列的两种模式
Java消息服务(Java Message Service,JMS)应用程序接口是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。 点对点与发布订阅最初是由JMS定义的。这两种模式主要区别或解决的问题就是发送到队列的消息能否重复消费(多订阅) 1、定义 JMS规范目前支持两种消息模型:点对点(point to point, queue)和发布/订阅(publish/subscribe,topic)。 1.1、点对点:Queue,不可重复消费 消息生产者生产消息发送到queue中,然后消息消费者从queue中取出并且消费消息。 消息被消费以后,queue中不再有存储,所以消息消费者不可能消费到已经被消费的消息。Queue支持存在多个消费者,但是对一个消息而言,只会有一个消费者可以消费。 1.2、发布/订阅:Topic,可以重复消费 消息生产者(发布)将消息发布到topic中,同时有多个消息消费者(订阅)消费该消息。和点对点方式不同,发布到topic的消息会被所有订阅者消费。 支持订阅组的发布订阅模式: 发布订阅模式下,当发布者消息量很大时,显然单个订阅者的处理能力是不足的。实际上现实场景中是多个订阅者节点组成一个订阅组负载均衡消费topic消息即分组订阅,这样订阅者很容易实现消费能力线性扩展。可以看成是一个topic下有多个Queue,每个Queue是点对点的方式,Queue之间是发布订阅方式。 2、区别 2.1、点对点模式 生产者发送一条消息到queue,一个queue可以有很多消费者,但是一个消息只能被一个消费者接受,当没有消费者可用时,这个消息会被保存直到有 一个可用的消费者,所以Queue实现了一个可靠的负载均衡。 2.2、发布订阅模式 发布者发送到topic的消息,只有订阅了topic的订阅者才会收到消息。topic实现了发布和订阅,当你发布一个消息,所有订阅这个topic的服务都能得到这个消息,所以从1到N个订阅者都能得到这个消息的拷贝。 3、流行模型比较 传统企业型消息队列ActiveMQ遵循了JMS规范,实现了点对点和发布订阅模型,但其他流行的消息队列RabbitMQ、Kafka并没有遵循JMS规范。 3.1、RabbitMQ RabbitMQ实现了AQMP协议,AQMP协议定义了消息路由规则和方式。生产端通过路由规则发送消息到不同queue,消费端根据queue名称消费消息。 RabbitMQ既支持内存队列也支持持久化队列,消费端为推模型,消费状态和订阅关系由服务端负责维护,消息消费完后立即删除,不保留历史消息。 (1)点对点 生产端发送一条消息通过路由投递到Queue,只有一个消费者能消费到。 (2)多订阅 当RabbitMQ需要支持多订阅时,发布者发送的消息通过路由同时写到多个Queue,不同订阅组消费不同的Queue。所以支持多订阅时,消息会多个拷贝。 3.2、Kafka Kafka只支持消息持久化,消费端为拉模型,消费状态和订阅关系由客户端端负责维护,消息消费完后不会立即删除,会保留历史消息。因此支持多订阅时,消息只会存储一份就可以了。但是可能产生重复消费的情况。 (1)点对点&多订阅 发布者生产一条消息到topic中,不同订阅组消费此消息。 from:http://blog.csdn.net/heyutao007/article/details/50131089
View Details数据库中间件 MyCAT
MyCAT 是一个彻底开源的,面向企业应用开发的“大数据库集群” 支持事务、ACID、可以替代Mysql的加强版数据库 ? 一个可以视为“Mysql”集群的企业级数据库,用来替代昂贵的Oracle集群 ? 一个融合内存缓存技术、Nosql技术、HDFS大数据的新型SQL Server ? 结合传统数据库和新型分布式数据仓库的新一代企业级数据库产品 ? 一个新颖的数据库中间件产品。 目标 低成本的将现有的单机数据库和应用平滑迁移到“云”端,解决数据存储和业务规模迅速增长情况下的数据瓶颈问题。 关键特性 支持 SQL 92标准 支持Mysql集群,可以作为Proxy使用 支持JDBC连接ORACLE、DB2、SQL Server,将其模拟为MySQL Server使用 支持galera for mysql集群,percona-cluster或者mariadb cluster,提供高可用性数据分片集群,自动故障切换,高可用性 ,支持读写分离,支持Mysql双主多从,以及一主多从的模式 ,支持全局表,数据自动分片到多个节点,用于高效表关联查询 ,支持独有的基于E-R 关系的分片策略,实现了高效的表关联查询多平台支持,部署和实施简单。 优势 基于阿里开源的Cobar产品而研发,Cobar的稳定性、可靠性、优秀的架构和性能,以及众多成熟的使用案例使得MyCAT一开始就拥有一个很好的起点,站在巨人的肩膀上,我们能看到更远。广泛吸取业界优秀的开源项目和创新思路,将其融入到MyCAT的基因中,使得MyCAT在很多方面都领先于目前其他一些同类的开源项目,甚至超越某些商业产品。MyCAT背后有一只强大的技术团队,其参与者都是5年以上资深软件工程师、架构师、DBA等,优秀的技术团队保证了MyCAT的产品质量。 MyCAT并不依托于任何一个商业公司,因此不像某些开源项目,将一些重要的特性封闭在其商业产品中,使得开源项目成了一个摆设。 MyCat架构 长期规划 在支持Mysql的基础上,后端增加更多的开源数据库和商业数据库的支持,包括原生支持PosteSQL、FireBird等开源数据库,以及通过JDBC等方式间接支持其他非开源的数据库如Oracle、DB2、SQL Server等实现更为智能的自我调节特性,如自动统计分析SQL,自动创建和调整索引,根据数据表的读写频率,自动优化缓存和备份策略等实现更全面的监控管理功能与HDFS集成,提供SQL命令,将数据库装入HDFS中并能够快速分析集成优秀的开源报表工具,使之具备一定的数据分析的能力。 from:https://www.oschina.net/p/mycat
View DetailsSPARK
Apache Spark 是专为大规模数据处理而设计的快速通用的计算引擎。 Spark是UC Berkeley AMP lab (加州大学伯克利分校的AMP实验室)所开源的类Hadoop MapReduce的通用并行框架,Spark,拥有Hadoop MapReduce所具有的优点;但不同于MapReduce的是Job中间输出结果可以保存在内存中,从而不再需要读写HDFS,因此Spark能更好地适用于数据挖掘与机器学习等需要迭代的MapReduce的算法。 Spark 是一种与 Hadoop 相似的开源集群计算环境,但是两者之间还存在一些不同之处,这些有用的不同之处使 Spark 在某些工作负载方面表现得更加优越,换句话说,Spark 启用了内存分布数据集,除了能够提供交互式查询外,它还可以优化迭代工作负载。 Spark 是在 Scala 语言中实现的,它将 Scala 用作其应用程序框架。与 Hadoop 不同,Spark 和 Scala 能够紧密集成,其中的 Scala 可以像操作本地集合对象一样轻松地操作分布式数据集。 尽管创建 Spark 是为了支持分布式数据集上的迭代作业,但是实际上它是对 Hadoop 的补充,可以在 Hadoop 文件系统中并行运行。通过名为 Mesos 的第三方集群框架可以支持此行为。Spark 由加州大学伯克利分校 AMP 实验室 (Algorithms, Machines, and People Lab) 开发,可用来构建大型的、低延迟的数据分析应用程序。
View DetailsEloquent ORM 实例教程 —— 模型删除及软删除相关实现
1、删除模型 1.1 使用delete删除模型 删除模型很简单,先获取要删除的模型实例,然后调用delete方法即可:
|
1 2 3 4 5 6 |
$post = Post::find(5); if($post->delete()){ echo '删除文章成功!'; }else{ echo '删除文章失败!'; } |
该方法返回true或false。 1.2 使用destroy删除模型 当然如果已知要删除的模型id的话,可以用更简单的方法destroy直接删除:
|
1 |
$deleted = Post::destroy(5); |
你也可以一次传入多个模型id删除多个模型:
|
1 |
$deleted = Post::destroy([1,2,3,4,5]); |
调用destroy方法返回被删除的记录数。 1.3 使用查询构建器删除模型 既然前面提到Eloquent模型本身就是查询构建器,也可以使用查询构建器风格删除模型,比如我们要删除所有浏览数为0的文章,可以使用如下方式:
|
1 |
$deleted = Models\Post::where('views', 0)->delete(); |
返回结果为被删除的文章数。 2、软删除及其相关实现 2.1 软删除实现 上述删除方法都会将数据表记录从数据库删除,此外Eloquent模型还支持软删除。 所谓软删除指的是数据表记录并未真的从数据库删除,而是将表记录的标识状态标记为软删除,这样在查询的时候就可以加以过滤,让对应表记录看上去是被”删除“了。Laravel中使用了一个日期字段作为标识状态,这个日期字段可以自定义,这里我们使用deleted_at,如果对应模型被软删除,则deleted_at字段的值为删除时间,否则该值为空。 要让Eloquent模型支持软删除,还要做一些设置。首先在模型类中要使用SoftDeletestrait,该trait为软删除提供一系列相关方法,具体可参考源码Illuminate\Database\Eloquent\SoftDeletes,此外还要设置$date属性数组,将deleted_at置于其中:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; class Post extends Model { use SoftDeletes; //设置表名 public $table = 'posts'; //设置主键 public $primaryKey = 'id'; //设置日期时间格式 public $dateFormat = 'U'; protected $guarded = ['id','views','user_id','updated_at','created_at']; protected $dates = ['delete_at']; } |
然后对应的数据库posts中添加deleted_at列,我们使用迁移来实现,先执行Artisan命令:
|
1 |
php artisan make:migration alter_posts_deleted_at --table=posts |
然后编辑生成的PHP文件如下:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<?php use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class AlterPostsDeletedAt extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::table('posts', function (Blueprint $table) { $table->softDeletes(); }); } ...//其它方法 } |
然后运行:
|
1 |
php artisan migrate |
这样posts中就有了deleted_at列。接下来,我们在控制器中编写测试代码:
|
1 2 3 4 5 6 7 8 |
$post = Post::find(6); $post->delete(); if($post->trashed()){ echo '软删除成功!'; dd($post); }else{ echo '软删除失败!'; } |
在浏览器中访问http://laravel.app:8000/test,页面输出如下: 当我们再次通过下面这段代码获取所有文章:
|
1 2 |
$posts = Post::all(); dd($posts); |
已经看不到id为6的文章的身影了。 2.2 查询结果包含软删除模型 那如果想要在查询结果中包含软删除的记录呢?可以使用SoftDeletes trait上的withTrashed方法:
|
1 2 |
$posts = Post::withTrashed()->get(); dd($posts); |
执行之后页面显示如下: id为6的文章又出现在了查询结果中。有时候我们只想要查看被软删除的模型,这也有招,通过SoftDeletes上的onlyTrashed方法即可:
|
1 2 |
$posts = Post::onlyTrashed()->get(); dd($posts); |
执行后页面显示结果如下: 2.3 软删除恢复 有时候我们需要恢复被软删除的模型,可以使用SoftDeletes提供的restore方法: 恢复单个模型
|
1 2 |
$post = Post::find(6); $post->restore(); |
恢复多个模型
|
1 |
Post::withTrashed()->where('id','>',1)->restore(); |
恢复所有模型
|
1 |
Post::withTrashed()->restore(); |
恢复关联查询模型
|
1 2 |
$post = Post::find(6); $post->history()->restore(); |
2.4 强制删除 如果模型配置了软删除但我们确实要删除改模型对应数据库表记录,则可以使用SoftDeletes提供的forceDelete方法:
|
1 2 |
$post = Post::find(6); $post->forceDelete(); |
查看数据表可以发现id=6的表记录已经被删除,不复存在: 该方法也支持关联模型强制删除,我们后面讲到关联关系的时候再具体说明。 from:http://laravelacademy.org/post/1020.html
View DetailsEloquent ORM 实例教程 —— 模型创建、更新及批量赋值
1、创建模型 1.1 使用save方法创建模型 调用Eloquent模型类的save方法即可创建模型并插入数据到数据库:
|
1 2 3 4 5 6 7 8 9 10 |
$post = new Post; $post->title = 'test 4'; $post->content = 'test content'; $post->user_id = 1; $post->cat_id = 1; if($post->save()){ echo '添加文章成功!'; }else{ echo '添加文章失败!'; } |
在浏览器中访问http://laravel.app:8000/test,如果输出内容为:
|
1 |
添加文章成功! |
则表明插入成功,我们去数据库查看数据,确实新增了一条记录: 1.2 使用create方法插入数据 除此之外还可以使用create方法插入数据,由于该方法中用到了批量赋值(Mass Assignment),所以我们需要在模型类中设置$fillable属性或者$guarded属性,以表明哪些属性可以通过该方法设置,哪些不可以。 开始之前,我们先解释下什么是批量赋值,以及为什么要使用批量赋值。 批量赋值的英文名称是Mass Assignment,所谓的批量赋值是指当我们将一个数组发送到模型类用于创建新的模型实例的时候(通常是表单请求数据),我们可以简单通过如下方式实现:
|
1 |
$post = Post::create(Input::all()); |
而不是像使用save方法那样一个一个的设置属性值,如果模型属性很多的话,使用save简直是噩梦有木有。 但事物总是相对的,使用批量赋值是很方便,但同时也带来了安全隐患,很多时候模型类的某些属性值不是我们所期望通过批量赋值修改的,比如用户模型有个user_type属性,如果用户通过请求数据将其类型修改为管理员类型,这显然是不允许的,正是基于这一考虑,Eloquent模型类为我们提供了$fillable属性和$guarded属性,我们可以将其分别看作“白名单”和“黑名单”,定义在$fillable中的属性可以通过批量赋值进行赋值,而定义在$guarded中的属性在批量赋值时会被过滤掉。 那么如果我们确实想要修改定义在$guarded中的属性怎么办?答案是使用save方法。 此外需要注意的是$fillable和$guarded方法同时只能定义一个,原因嘛很简单,非黑即白,定义了一个另外一个也就确定了。 可见批量赋值不仅为我们创建模型提供了便利,还避免了安全隐患,提高了系统的安全性。 下面我们来演示一下批量赋值的使用。首先在Post模型中定义$guarded属性如下:
|
1 |
protected $guarded = ['views','user_id','updated_at','created_at']; |
然后在控制器中实现创建模型实例的逻辑:
|
1 2 3 4 5 6 7 8 9 |
$input = [ 'title'=>'test 5', 'content'=>'test content', 'cat_id'=>1, 'views'=>100, 'user_id'=>2 ]; $post = Post::create($input); dd($post); |
在浏览器中输入http://laravel.app:8000/test,则页面输出: 可见user_id和views字段都没有插入进去,这正是$guarded发挥了作用,如果要设置这两个值也很简单:
|
1 2 3 4 5 6 7 8 9 10 11 12 |
$input = [ 'title'=>'test 5', 'content'=>'test content', 'cat_id'=>1, 'views'=>100, 'user_id'=>2 ]; $post = Post::create($input); $post->user_id = 2; $post->views = 100; $post->save(); dd($post); |
对应输出如下: 1.3 其他插入数据的方法 Eloquent模型类还支持其它插入数据的方法——firstOrCreate和firstOrNew,两者都是先通过通过传入属性值在数据库中查找匹配记录,如果没有找到则创建一个新的模型实例,不同之处在于后者不会将数据持久化到数据库,需要调用save方法才行。 2、更新模型 2.1 使用save方法更新模型 save方法还可以用于更新模型,要更新模型数据,先要获取该模型实例,然后修改模型属性,再调用save方法保存即可:
|
1 2 3 4 5 6 7 |
$post = Post::find(1); $post->title = 'test 1 title'; if($post->save()){ echo '更新文章成功!'; }else{ echo '更新文章失败!'; } |
在浏览器中访问http://laravel.app:8000/test,如果显示:
|
1 |
更新文章成功! |
则表明数据库对应表记录更新成功: 2.2 使用update方法更新数据 和create相对应的,Eloquent模型类还支持使用update方法更新数据,同样要用到批量赋值:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
$input = [ 'title'=>'test 6 title', 'content'=>'test content 6', 'cat_id'=>1, 'views'=>200, 'user_id'=>1 ]; $post = Post::find(6); if($post->update($input)){ echo '更新文章成功!'; dd($post); }else{ echo '更新文章失败!'; } |
对应输出为: 可见user_id和views并没有更新。 from:http://laravelacademy.org/post/984.html
View Detailshypervisor
Hypervisor是一种运行在物理服务器和操作系统之间的中间软件层,可允许多个操作系统和应用共享一套基础物理硬件,因此也可以看作是虚拟环境中的“元”操作系统,它可以协调访问服务器上的所有物理设备和虚拟机,也叫虚拟机监视器(Virtual Machine Monitor)。Hypervisor是所有虚拟化技术的核心。非中断地支持多工作负载迁移的能力是Hypervisor的基本功能。当服务器启动并执行Hypervisor时,它会给每一台虚拟机分配适量的内存、CPU、网络和磁盘,并加载所有虚拟机的客户操作系统。
View DetailsEloquent ORM 实例教程 —— ORM概述、模型定义及基本查询
1、引子 在正式进入本节的之前,让我们先来看看什么是ORM。 ORM,即 Object-Relational Mapping(对象关系映射),它的作用是在关系型数据库和业务实体对象之间作一个映射,这样,我们在操作具体的 业务对象时,就不需要再去和复杂的SQL语句打交道,只需简单的操作对象的属性和方法即可。 ORM 两种最常见的实现方式是 ActiveRecord 和 DataMapper,ActiveRecord 尤其流行,在很多框架中都能看到它的身影。两者的区别主要在于 ActiveRecord 中模型与数据表一一对应,而 DataMapper 中模型与数据表是完全分离的。 Laravel 中的 Eloquent ORM 使用的也是 ActiveRecord 实现方式,每一个 Eloquent 模型类对应着数据库中的一张表,我们通过调用模型类的相应方法实现对数据库的增删改查。 2、定义模型 2.1 创建模型 我们使用Artisan命令make:model生成模型类,模型类默认位于app目录下,我们也可以在创建时指定生成目录:
|
1 |
php artisan make:model Models/Post |
这样就会在app目录下生成一个Models目录,并且在Models目录下生成一个Post模型类。Laravel 中所有模型类继承自Illuminate\Database\Eloquent\Model类。 2.2 指定表名 如果不手动指定,默认Post对应的数据表为posts,以此类推。也可以通过设置$table属性自定义表名:
|
1 |
public $table = 'posts'; |
2.3 指定主键 Eloquent默认数据表主键为id,当然也可以通过设置$primaryKey属性来自定义主键:
|
1 |
public $primaryKey = 'id'; |
2.4 时间戳设置 默认情况下,Eloquent模型类会自动管理时间戳列create_at和update_at(如果定义迁移时设置了这两列的话),如果要取消自动管理,可以设置$timestamps属性为false:
|
1 |
public $timestamps = false; |
还有,如果你想要设置时间戳的格式,可以使用$dateFormat属性,该属性决定了日期时间以何种格式存入数据库,以及以何种格式显示:
|
1 2 |
//设置日期时间格式为Unix时间戳 protected $dateFormat = 'U'; |
更多关于日期时间格式设置,请参考php官方函数date中format部分。 3、查询数据 3.1 获取多个模型 我们可以使用Eloquent模型上的all方法获取所有模型实例,比如我们通过如下方法获取所有文章:
|
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 |
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Http\Requests; use App\Http\Controllers\Controller; use App\Models\Post; class TestController extends Controller { /** * Display a listing of the resource. * * @return Response */ public function index() { //获取多个Eloquent模型 $posts = Post::all(); dd($posts); } } |
对应输出结果为: 可见输出结果是模型数组集合,每一个$items元素对应一个Post模型实例。 此外,需要了解的是每一个Eloquent模型本身都是一个查询构建器,所有我们可以调用所有查询构建器上的方法,只不过第一个方法调用都要使用静态方法调用:
|
1 2 |
$posts = Post::where('id','<',3)->orderBy('id','desc')->take(1)->get(); dd($posts); |
对应输出结果为: 也许你已经注意到了,模型查询返回结果都是Illuminate\Database\Eloquent\Collection的一个实例,该类实现了ArrayAccess接口,所以我们可以像访问数组一样访问该实例,此外,该Collection类还提供了很多其它有用的方法对查询结果进行处理,详见源码。 既然Eloquent模型是查询构建器,自然也支持分组块获取数据:
|
1 2 3 4 5 |
Post::chunk(2,function($posts){ foreach ($posts as $post) { echo $post->title.'<br>'; } }); |
输出结果如下:
|
1 2 3 |
test 1 test 2 test 3 |
3.2 获取单个模型 可以使用查询构建器方法获取单个模型实例:
|
1 2 |
$post = Post::where('id',1)->first(); dd($post); |
当然也可以通过Eloquent模型类提供的快捷方法find:
|
1 |
$post = Post::find(1); |
两者输出结果一样: 如果没有找到对应的表记录,会输出null,如果我们想要捕获查询结果为空的异常并进行处理,比如跳转到404页面,可以使用findOrFail或者firstOrFail方法,如果表记录存在,两者返回获取到的第一条记录,否则抛出Illuminate\Database\Eloquent\ModelNotFoundException异常。 3.3 聚合函数查询 如果要对查询结果进行计数、统计、最大值/最小值、平均数等聚合运算,可以使用查询构建器上的对应方法,我们我们查询文章总数:
|
1 2 |
$count = Post::where('id','>',0)->count(); echo $count; |
输出结果为3,又或者我们想要获取文章最大阅读数:
|
1 2 |
$views = Post::where('id','>',0)->max('views'); echo $views; |
输出结果为800。 from:http://laravelacademy.org/post/966.html
View DetailsLaravel 服务容器实例教程 —— 深入理解控制反转(IoC)和依赖注入(DI)
容器,字面上理解就是装东西的东西。常见的变量、对象属性等都可以算是容器。一个容器能够装什么,全部取决于你对该容器的定义。当然,有这样一种容器,它存放的不是文本、数值,而是对象、对象的描述(类、接口)或者是提供对象的回调,通过这种容器,我们得以实现许多高级的功能,其中最常提到的,就是 “解耦” 、“依赖注入(DI)”。本文就从这里开始。 IoC 容器 —— Laravel 的核心 Laravel 的核心就是一个 IoC 容器,根据文档,称其为“服务容器”,顾名思义,该容器提供了整个框架中需要的一系列服务。作为初学者,很多人会在这一个概念上犯难,因此,我打算从一些基础的内容开始讲解,通过理解面向对象开发中依赖的产生和解决方法,来逐渐揭开“依赖注入”的面纱,逐渐理解这一神奇的设计理念。 本文一大半内容都是通过举例来让读者去理解什么是 IoC(控制反转) 和 DI(依赖注入),通过理解这些概念,来更加深入。更多关于 laravel 服务容器的用法建议阅读文档即可。 IoC 容器诞生的故事 讲解 IoC 容器有很多的文章,我之前也写过。但现在我打算利用当下的灵感重新来过,那么开始吧。 超人和超能力,依赖的产生 面向对象编程,有以下几样东西无时不刻的接触:接口、类还有对象。这其中,接口是类的原型,一个类必须要遵守其实现的接口;对象则是一个类实例化后的产物,我们称其为一个实例。当然这样说肯定不利于理解,我们就实际的写点中看不中用的代码辅助学习。 怪物横行的世界,总归需要点超级人物来摆平。 我们把一个“超人”作为一个类,
|
1 |
class Superman {} |
我们可以想象,一个超人诞生的时候肯定拥有至少一个超能力,这个超能力也可以抽象为一个对象,为这个对象定义一个描述他的类吧。一个超能力肯定有多种属性、(操作)方法,这个尽情的想象,但是目前我们先大致定义一个只有属性的“超能力”,至于能干啥,我们以后再丰富:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
class Power { /** * 能力值 */ protected $ability; /** * 能力范围或距离 */ protected $range; public function __construct($ability, $range) { $this->ability = $ability; $this->range = $range; } } |
这时候我们回过头,修改一下之前的“超人”类,让一个“超人”创建的时候被赋予一个超能力:
|
1 2 3 4 5 6 7 8 9 |
class Superman { protected $power; public function __construct() { $this->power = new Power(999, 100); } } |
这样的话,当我们创建一个“超人”实例的时候,同时也创建了一个“超能力”的实例,但是,我们看到了一点,“超人”和“超能力”之间不可避免的产生了一个依赖。 所谓“依赖”,就是 “我若依赖你,我就不能离开你”。 在一个贯彻面向对象编程的项目中,这样的依赖随处可见。少量的依赖并不会有太过直观的影响,我们随着这个例子逐渐铺开,让大家慢慢意识到,当依赖达到一个量级时,是怎样一番噩梦般的体验。当然,我也会自然而然的讲述如何解决问题。 一堆乱麻 —— 可怕的依赖 之前的例子中,超能力类实例化后是一个具体的超能力,但是我们知道,超人的超能力是多元化的,每种超能力的方法、属性都有不小的差异,没法通过一种类描述完全。我们现在进行修改,我们假设超人可以有以下多种超能力: 飞行,属性有:飞行速度、持续飞行时间 蛮力,属性有:力量值 能量弹,属性有:伤害值、射击距离、同时射击个数 我们创建了如下类:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
class Flight { protected $speed; protected $holdtime; public function __construct($speed, $holdtime) {} } class Force { protected $force; public function __construct($force) {} } class Shot { protected $atk; protected $range; protected $limit; public function __construct($atk, $range, $limit) {} } |
为了省事儿我没有详细写出 __construct() 这个构造函数的全部,只写了需要传递的参数。 好了,这下我们的超人有点“忙”了。在超人初始化的时候,我们会根据需要来实例化其拥有的超能力吗,大致如下:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
class Superman { protected $power; public function __construct() { $this->power = new Fight(9, 100); // $this->power = new Force(45); // $this->power = new Shot(99, 50, 2); /* $this->power = array( new Force(45), new Shot(99, 50, 2) ); */ } } |
我们需要自己手动的在构造函数内(或者其他方法里)实例化一系列需要的类,这样并不好。可以想象,假如需求变更(不同的怪物横行地球),需要更多的有针对性的新的超能力,或者需要变更超能力的方法,我们必须 重新改造 超人。换句话说就是,改变超能力的同时,我还得重新制造个超人。效率太低了!新超人还没创造完成世界早已被毁灭。 这时,灵机一动的人想到:为什么不可以这样呢?超人的能力可以被随时更换,只需要添加或者更新一个芯片或者其他装置啥的(想到钢铁侠没)。这样的话就不要整个重新来过了。 对,就是这样的。 我们不应该手动在 “超人” 类中固化了他的 “超能力” 初始化的行为,而转由外部负责,由外部创造超能力模组、装置或者芯片等(我们后面统一称为 “模组”),植入超人体内的某一个接口,这个接口是一个既定的,只要这个 “模组” 满足这个接口的装置都可以被超人所利用,可以提升、增加超人的某一种能力。这种由外部负责其依赖需求的行为,我们可以称其为 “控制反转(IoC)”。 工厂模式,依赖转移! 当然,实现控制反转的方法有几种。在这之前,不如我们先了解一些好玩的东西。 我们可以想到,组件、工具(或者超人的模组),是一种可被生产的玩意儿,生产的地方当然是 “工厂(Factory)”,于是有人就提出了这样一种模式: 工厂模式。 工厂模式,顾名思义,就是一个类所依赖的外部事物的实例,都可以被一个或多个 “工厂” 创建的这样一种开发模式,就是 “工厂模式”。 我们为了给超人制造超能力模组,我们创建了一个工厂,它可以制造各种各样的模组,且仅需要通过一个方法:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
class SuperModuleFactory { public function makeModule($moduleName, $options) { switch ($moduleName) { case 'Fight': return new Fight($options[0], $options[1]); case 'Force': return new Force($options[0]); case 'Shot': return new Shot($options[0], $options[1], $options[2]); } } } |
这时候,超人 创建之初就可以使用这个工厂!
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
class Superman { protected $power; public function __construct() { // 初始化工厂 $factory = new SuperModuleFactory; // 通过工厂提供的方法制造需要的模块 $this->power = $factory->makeModule('Fight', [9, 100]); // $this->power = $factory->makeModule('Force', [45]); // $this->power = $factory->makeModule('Shot', [99, 50, 2]); /* $this->power = array( $factory->makeModule('Force', [45]), $factory->makeModule('Shot', [99, 50, 2]) ); */ } } |
可以看得出,我们不再需要在超人初始化之初,去初始化许多第三方类,只需初始化一个工厂类,即可满足需求。但这样似乎和以前区别不大,只是没有那么多 new 关键字。其实我们稍微改造一下这个类,你就明白,工厂类的真正意义和价值了。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
class Superman { protected $power; public function __construct(array $modules) { // 初始化工厂 $factory = new SuperModuleFactory; // 通过工厂提供的方法制造需要的模块 foreach ($modules as $moduleName => $moduleOptions) { $this->power[] = $factory->makeModule($moduleName, $moduleOptions); } } } // 创建超人 $superman = new Superman([ 'Fight' => [9, 100], 'Shot' => [99, 50, 2] ]); |
现在修改的结果令人满意。现在,“超人” 的创建不再依赖任何一个 “超能力” 的类,我们如若修改了或者增加了新的超能力,只需要针对修改 SuperModuleFactory 即可。扩充超能力的同时不再需要重新编辑超人的类文件,使得我们变得很轻松。但是,这才刚刚开始。 […]
View DetailsLaravel 数据库实例教程 —— 使用查询构建器实现对数据库的高级查询
1、连接查询(join) 连接查询指的是将两张表或多张表关联到一起进行查询,获取一个表的行与另一个表的行匹配的数据。常见的连接查询包括内连接(等值连接)、左(外)连接、右(外)连接和交叉连接(完全连接)等。下面这张图形象的展示了这几种连接查询所获取的结果集: 下面我们简单演示下内连接和左连接。我们将用户表users和文章表posts关联到一起进行查询,在此之前,我们先创建posts表,其字段及初始值如下: 其中user_id对应users表中的用户id。 1.1 内连接 内连接用于获取两张表结果集的交集部分,我们可以使用查询构建器的join方法进行内连接查询:
|
1 2 |
$users = DB::table('users')->join('posts','users.id','=','posts.user_id')->get(); dd($users); |
显示结果如下: 1.2 左连接 左连接的结果集指定的左表的所有行,如果左表的某行在右表中没有匹配行,则在相关联的结果集行中右表的所有选择列表列均为空值(null)。我们使用查询构建器的leftJoin方法进行左连接查询:
|
1 2 |
$users = DB::table('users')->leftJoin('posts','users.id','=','posts.user_id')->get(); dd($users); |
对应的输出结果为: 1.3 更加复杂的连接查询 很多时候,连接查询的查询条件往往更为复杂,并不是一个简单的join/leftJoin方法就能搞定的,那么我们如何添加更加复杂的查询条件呢?使用JoinClause代替条件参数:
|
1 2 3 4 5 |
$users = DB::table('users')->join('posts',function($join){ $join->on('users.id','=','posts.user_id') -><a title="View all posts in where" href="http://laravelacademy.org/tags/where" target="_blank">where</a>('posts.id','>',1); })->get(); dd($users); |
输出结果为: 2、联合查询(union) 联合查询用于将两个或更多查询的结果集组合为单个结果集,该结果集包含联合查询中所有查询的全部行。UNION的结果集列名与UNION运算符中第一个Select语句的结果集的列名相同,另一个Select语句的结果集列名将被忽略,且其他查询字段数必须和第一个相同。Laravel查询构建器中我们使用union方法进行联合查询:
|
1 2 3 |
$users = DB::table('users')->where('id','<',3); $users = DB::table('users')->where('id','>',2)->union($users)->get(); dd($users); |
输出结果为: 3、where子句 使用查询构建器上的where方法可以添加自定义查询条件,调用该方法需要传入三个参数:第一个列名,第二个是操作符,第三个是比较值:
|
1 2 |
$user = DB::table('users')->where('name','=','Laravel')->get(); dd($user); |
如果操作符为“=”,该语句也可简化为:
|
1 |
$user = DB::table('users')->where('name','Laravel')->get(); |
输出结果为: 需要注意的是查询构建器支持方法链,这意味着如果有多个查询条件且这个多个条件是AND连接,可以在get之前使用多个where方法。如果多个条件使用OR连接,可以使用orWhere方法:
|
1 |
$user = DB::table('users')->where('name','Laravel')->orWhere('name','Academy')->get(); |
对应输出为: 更多where子句查询条件可查看Illuminate\Database\Query\Builder源码。 4、排序 查询构建器使用orderBy方法对查询结果进行排序:
|
1 2 |
$users = DB::table('users')->orderBy('id','desc')->get(); dd($users); |
根据代码可以看到orderBy方法需要传入两个参数,第一个是排序字段,第二个是排序方向,asc代表升序,desc代表倒序,上述代码输出为: 5、分组 为了更好地演示分组,我们给数据表posts新增两个字段:cat_id和views,代表分类ID和浏览数: 分组一般用于聚合查询,接下来我们使用groupBy方法对查询结果进行分组,比如我们可以统计每个分类下有几篇文章:
|
1 2 |
$posts = DB::table('posts')->select('cat_id',DB::raw('COUNT(id) as num'))->groupBy('cat_id')->get(); dd($posts); |
输出如下: 我们还可以使用having方法为分组加上条件,比如我们可以统计总浏览数大于500的分类:
|
1 2 |
$posts = DB::table('posts')->select('cat_id',DB::raw('SUM(views) as views'))->groupBy('cat_id')->having('views','>',500)->get(); dd($posts); |
输出结果为: 注意:having中的条件字段必须出现在select查询字段中,否则会报错。 6、分页 查询构建器中使用skip和take对查询结果进行分页,相当于SQL语句中的limit语句:
|
1 2 |
$posts = DB::table('posts')->skip(0)->take(2)->get(); dd($posts); |
对应的输出结果为: from:http://laravelacademy.org/post/920.html
View DetailsLaravel 数据库实例教程 —— 使用查询构建器对数据库进行增删改查
获取查询构建器很简单,还是要依赖DB门面,我们使用DB门面的table方法,传入表名,即可获取该表的查询构建器:
|
1 |
$users = DB::table('users'); |
这样我们就获取到了$users表的查询构建器,实际上,底层返回的是Illuminate\Database\Query\Builder的实例,我们对查询构建器的所有操作都是调用该实例对应类上的方法。下面我们就列举查询构建器的一些常用方法,我们还是沿用上一节创建的$users表做演示说明 。 1、新增数据 使用查询构建器的insert方法即可插入一条/多条数据:
|
1 2 3 4 5 |
DB::table('users')->insert([ ['id'=>1,'name'=>'<a title="View all posts in Laravel" href="http://laravelacademy.org/tags/laravel" target="_blank">Laravel</a>','email'=>'laravel@test.com','password'=>'123'], ['id'=>2,'name'=>'Academy','email'=>'academy@test.com','password'=>'123'], ['id'=>3,'name'=>'LaravelAcademy','email'=>'laravel-academy@test.com','password'=>'123'] ]); |
执行成功后即可在数据表$users中插入三条记录。有时候,我们需要插入记录后获取自增ID,可以使用insertGetId方法:
|
1 2 3 |
$insertId = DB::table('users')->insertGetId( ['name'=>'Laravel-Academy','email'=>'laravelacademy@test.com','password'=>'456'] ); |
2、更新数据 更新表记录很简单,使用查询构建器的update方法即可:
|
1 |
$affected = DB::table('users')->where('name','Laravel-Academy')->update(['password'=>'123']); |
该方法返回受影响的函数。 3、删除数据 使用delete方法删除表记录,删除方法和更新方法类似,返回被删除的行数:
|
1 |
$deleted = DB::table('users')->where('id', '>', 3)->delete(); |
如果我们是要删除整个数据表数据,则略去where条件,如果是要清空数据表还要将自增ID置为0,可以使用truncate方法:
|
1 |
DB::table('users')->truncate(); |
4、基本查询 4.1 获取所有表记录 使用get方法即可获取一张表的所有记录:
|
1 2 |
$users = DB::table('users')->get(); dd($users); |
打印结果如下: 如果是获取指定列的数据,则需要加上select条件:
|
1 2 |
$users = DB::table('users')->select('name','email')->get(); dd($users); |
打印结果如下: 4.2 获取单条记录 获取单条记录需要在查询基础上加上first方法:
|
1 2 |
$user = DB::table('users')->where('name','Laravel')->first(); dd($user); |
则对应结果为: 4.3 分组块获取数据 如果数据库包含多条数据,则一次性获取会极大影响性能,对应地,我们可以调用chunk方法分组块获取数据:
|
1 2 3 4 5 6 7 |
DB::table('users')->chunk(2,function($users){ foreach($users as $user){ // if($user->name=='LaravelAcademy') // return false; echo $user->name.'<br>'; } }); |
这里我们指定每次取两条记录。注释部分的意思是我们可以设定查询退出条件,当达到该条件时,查询退出,不再往下执行。 4.4 获取单列的值 上述方法获取的查询结果都是对象实例/对象实例数组,有时候,我们只是想简单获取单列的值,遍历数组获取指定列的值太麻烦,可以使用lists方法获取列值数组:
|
1 2 |
$users = DB::table('users')->lists('name'); dd($users); |
对应的输出为: 这样我们就可以免去遍历对象数组的麻烦。 4.5 原生表达式 此外,查询构建器还支持原生表达式,我们需要调用DB门面的raw方法来实现:
|
1 2 |
$users = DB::table('users')->select(DB::raw('name,email'))->where('id','<',3)->get(); dd($users); |
对应输出为: from:http://laravelacademy.org/post/908.html
View Details