简介
几乎所有的应用程序都需要和数据库进行交互。Laravel 为此提供了一套非常简单易用的数据库交互方式。开发者可以使用原生 SQL,查询构造器,以及Eloquent ORM,等方式与数据库交互。目前,Laravel 为以下五种数据库提供了官方支持:
MariaDB 10.3+ ( 策略版本)
MySQL 5.7+ ([ 策略版本)
PostgreSQL 10.0+ ([ 策略版本)
SQLite 3.35.0+
SQL Server 2017+ ([ 策略版本)
配置
Laravel 数据库服务的配置位于应用程序的 php config/database.php 配置文件中。在此文件中,您可以定义所有数据库连接,并指定默认情况下应使用的连接。此文件中的大多数配置选项由应用程序环境变量的值驱动。本文件提供了 Laravel 支持的大多数数据库系统的示例。
在默认情况下,Laravel 的示例 环境配置 使用了 Laravel Sail,Laravel Sail 是一种用于在本地开发 Laravel 应用的 Docker 配置。但你依然可以根据本地数据库的需要修改数据库配置。
SQLite 配置
SQLite数据库包含在文件系统的单个文件中。你可以在终端上使用 php touch 命令创建一个新的SQLite数据库:php touch database/database.sqlite。创建数据库之后,你可以通过在环境变量 php DB_DATABASE 中设置数据库的绝对路径,轻松地配置环境变量来指向这个数据库:
DB_CONNECTION=sqliteDB_DATABASE=/absolute/path/to/database.sqlite
默认情况下,对SQLite连接启用外键约束。如果你想禁用它们,你应该将 php DB_FOREIGN_KEYS 环境变量设置为 php false:
DB_FOREIGN_KEYS=false
[!注意]
如果你使用Laravel安装程序来创建你的Laravel应用程序,并选择SQLite作为你的数据库,Laravel将自动创建一个php database/database.sqlite文件运行默认的数据库迁移。
Microsoft SQL Server 配置
要使用Microsoft SQL Server数据库,您应该确保安装了 php sqlsrv and php pdo_sqlsrv PHP扩展以及它们可能需要的任何依赖项,例如Microsoft SQL ODBC驱动程序。
使用URL配置
通常,数据库连接使用多个配置值进行配置,例如 php host, php database, php username, php password 等。这些配置值中的每一个都有自己相应的环境变量。这意味着在生产服务器上配置数据库连接信息时,需要管理几个环境变量。
部分数据库托管平台(如 AWS 和 Heroku)会提供了包含所有连接信息的数据库「URL」。它们通常看起来像这样:
mysql://root:[email protected]/forge?charset=UTF-8
这些 URL 通常遵循标准模式约定:
driver://username:password@host:port/database?options
为了方便起见,Laravel 支持使用这些 URL 替代传统的配置项来配置你的数据库。如果配置项 php url (或其对应的环境变量 php DATABASE_URL )存在,那么 Laravel 将会尝试从 URL 中提取数据库连接以及凭证信息。
读写分离
有时候你可能会希望使用一个数据库连接来执行 php SELECT 语句,而 php INSERT、php UPDATE 和 php DELETE 语句则由另一个数据库连接来执行。在 Laravel 中,无论你是使用原生 SQL 查询、查询构造器 或是 php Eloquent ORM,都能轻松实现读写分离。
为了弄明白如何配置读写分离,我们先来看个例子:
'mysql' => ['read' => ['host' => ['192.168.1.1','196.168.1.2',],],'write' => ['host' => ['196.168.1.3',],],'sticky' => true,'database' => env('DB_DATABASE', 'laravel'),'username' => env('DB_USERNAME', 'root'),'password' => env('DB_PASSWORD', ''),'unix_socket' => env('DB_SOCKET', ''),'charset' => env('DB_CHARSET', 'utf8mb4'),'collation' => env('DB_COLLATION', 'utf8mb4_0900_ai_ci'),'prefix' => '','prefix_indexes' => true,'strict' => true,'engine' => null,'options' => extension_loaded('pdo_mysql') ? array_filter([PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),]) : [],],
请注意,我们在数据库配置中加入了三个键,分别是: php read, php write 以及 php sticky 。php read 和 php write 的值是一个只包含 php host 键的数组。这代表其他的数据库选项将会从主 php mysql 配置中获取。
如果你想要覆写主 php mysql 配置,只需要将需要覆写的值放到 php read 和 php write数组里即可。所以,在这个例子中,php 192.168.1.1 将会被用作「读」连接主机,而 php 192.168.1.3 将作为「写」连接主机。这两个连接将共享 php mysql 数组中的各项配置,如数据库凭证(用户名、密码)、前缀、字符编码等。如果 php host 数组中存在多个值,php Laravel 将会为每个连接随机选取所使用的数据库主机。
sticky 选项php sticky 是一个 可选 值,它用于允许 php Laravel 立即读取在当前请求周期内写入到数据库的记录。若 php sticky 选项被启用,且在当前请求周期中执行过「写」操作,那么在这之后的所有「读」操作都将使用「写」连接。这样可以确保同一个请求周期中写入的数据库可以被立即读取到,从而避免主从同步延迟导致的数据不一致。不过是否启用它取决于项目的实际需求。
执行原生 SQL 查询
一旦配置好数据库连接,你就可以使用 php DB Facade 来执行查询。php DB Facade 为每种类型的查询都提供了相应的方法:php select、php update、php insert、php delete 以及 php statement。
执行 SELECT 查询
你可以使用 php DB Facade 的 php select 方法来执行一个基础的 SELECT 查询:
<?phpnamespace App\Http\Controllers;use App\Http\Controllers\Controller;use Illuminate\Support\Facades\DB;use Illuminate\View\View;class UserController extends Controller{/*** 显示所有应用程序用户的列表。*/public function index(): View{$users = DB::select('select * from users where active = ?', [1]);return view('user.index', ['users' => $users]);}}
传递给 php select 方法的第一个参数是一个原生 SQL 查询语句,而第二个参数则是需要绑定到查询中的参数值。通常,这些值用于约束 php where 语句。使用参数绑定可以有效防止 SQL 注入。
php select 方法将始终返回一个包含查询结果的数组。数组中的每个结果都对应一个数据库记录的 php stdClass 对象:
use Illuminate\Support\Facades\DB;$users = DB::select('select * from users');foreach ($users as $user) {echo $user->name;}
选择标量值
有时你的数据库查询可能得到一个单一的标量值。而不是需要从记录对象中检索查询的标量结果,Laravel 允许你直接使用 php scalar 方法检索此值:
$burgers = DB::scalar("select count(case when food = 'burger' then 1 end) as burgers from menu");
选择多个结果集
如果你的应用程序调用返回多个结果集的存储过程,可以使用 php selectResultSets 方法来检索存储过程返回的所有结果集:
[$options, $notifications] = DB::selectResultSets("CALL get_user_options_and_notifications(?)", $request->user()->id);
使用命名绑定
除了使用 php ? 表示参数绑定外,你还可以使用命名绑定的形式来执行一个查询:
$results = DB::select('select * from users where id = :id', ['id' => 1]);
执行 Insert 语句
你可以使用 php DB Facade 的 php insert 方法来执行语句。跟 php select 方法一样,该方法的第一个和第二个参数分别是原生 SQL 语句和绑定的数据:
use Illuminate\Support\Facades\DB;DB::insert('insert into users (id, name) values (?, ?)', [1, 'Marc']);
执行 Update 语句php update 方法用于更新数据库中现有的记录。该方法将会返回受到本次操作影响的记录行数:
use Illuminate\Support\Facades\DB;$affected = DB::update('update users set votes = 100 where name = ?',['Anita']);
执行 Delete 语句php delete 函数被用于删除数据库中的记录。它的返回值与 php update 函数相同,返回本次操作受影响的总行数。
use Illuminate\Support\Facades\DB;$deleted = DB::delete('delete from users');
执行指定的 SQL
部分 SQL 语句不返回任何值。在这种情况下,你可能需要使用 php DB::statement($sql) 来执行你的 SQL 语句。
DB::statement('drop table users');
直接执行 SQL
有时候你可能想执行一段 SQL 语句,但不需要进行 SQL 预处理绑定。这种情况下你可以使用 php DB::unprepared($sql) 来执行你的 SQL 语句。
DB::unprepared('update users set votes = 100 where name = "Dries"');
注意
未经过预处理 SQL 的语句不绑定参数,它们可能容易受到 SQL 注入的攻击。在没有必要的理由的情况下,你不应直接在 SQL 中使用用户传入的数据。
在事务中的隐式提交
在事务中使用 php DB::statement($sql) 与 php DB::unprepared($sql) 时,你必须要谨慎处理,避免 SQL 语句产生 隐式提交。这些语句会导致数据库引擎间接地提交整个事务,让 Laravel 丢失数据库当前的事务级别。创建数据库表就是一个这样的语句的例子:
DB::unprepared('create table a (col varchar(1) null)');
请参考 MySQL 官方手册以了解更多隐式提交的信息。
使用多数据库连接
如果你在配置文件 php config/database.php 中定义了多个数据库连接的话,你可以通过 php DB Facade 的 php connection 方法来使用它们。传递给 php connection 方法的连接名称应该是你在 php config/database.php 里或者通过 php config 助手函数在运行时配置的连接之一:
use Illuminate\Support\Facades\DB;$users = DB::connection('sqlite')->select(/* ... */);
你也可以使用一个连接实例上的 php getPdo 方法来获取底层的 PDO 实例:
$pdo = DB::connection()->getPdo();
监听查询事件
如果你想要获取程序执行的每一条 SQL 语句,可以使用 Db facade 的 listen 方法。该方法对查询日志和调试非常有用,你可以在服务提供者中使用 boot 方法注册查询监听器。
<?phpnamespace App\Providers;use Illuminate\Database\Events\QueryExecuted;use Illuminate\Support\Facades\DB;use Illuminate\Support\ServiceProvider;class AppServiceProvider extends ServiceProvider{/*** 注册任意应用服务*/public function register(): void{// ...}/*** 引导任意应用服务*/public function boot(): void{DB::listen(function (QueryExecuted $query) {// $query->sql;// $query->bindings;// $query->time;});}}
监控累积查询时间
现代Web应用程序的常见性能瓶颈之一是查询数据库所花费的时间。幸运的是,当Laravel在单个请求中花费过多时间查询数据库时,它可以调用您选择的闭包或回调。要开始使用它,请提供查询时间阈值(以毫秒为单位)和闭包给 php whenQueryingForLongerThan 方法。您可以在服务提供者的 php boot 方法中调用此方法:
<?phpnamespace App\Providers;use Illuminate\Database\Connection;use Illuminate\Support\Facades\DB;use Illuminate\Support\ServiceProvider;use Illuminate\Database\Events\QueryExecuted;class AppServiceProvider extends ServiceProvider{/*** 注册任意应用服务*/public function register(): void{// ...}/*** 引导任意应用服务*/public function boot(): void{DB::whenQueryingForLongerThan(500, function (Connection $connection, QueryExecuted $event) {// 通知开发团队...});}}
数据库事务
你可以使用 php DB 门面提供的 php transaction 方法在数据库事务中运行一系列操作。如果在事务闭包内抛出异常,事务将自动回滚并重新抛出异常。如果闭包执行成功,事务将自动提交。在使用 php transaction 方法时,您不需要担心手动回滚或提交:
use Illuminate\Support\Facades\DB;DB::transaction(function () {DB::update('update users set votes = 1');DB::delete('delete from posts');});
处理死锁php transaction 方法接受一个可选的第二个参数,该参数定义发生死锁时事务应重试的次数。一旦这些尝试用尽,就会抛出一个异常:
use Illuminate\Support\Facades\DB;DB::transaction(function () {DB::update('update users set votes = 1');DB::delete('delete from posts');}, 5);
手动执行事务
如果你想要手动处理事务并完全控制回滚和提交,可以使用 php DB facade 提供的 php beginTransaction 方法:
use Illuminate\Support\Facades\DB;DB::beginTransaction();
你可以通过 php rollBack 方法回滚事务:
DB::rollBack();
最后,你可以通过 php commit 方法提交事务:
DB::commit();
技巧
DB 门面的事务方法还可以用于控制查询构造器 和 Eloquent ORM。
连接到数据库 CLI
如果你想连接到数据库的 CLI,则可以使用 php db Artisan 命令:
php artisan db
如果需要,你可以指定数据库连接名称以连接到不是默认连接的数据库连接:
php artisan db mysql
检查你的数据库
使用 php db:show 和 php db:table Artisan 命令,你可以深入了解数据库及其相关表。要查看数据库的概述,包括它的大小、类型、打开的连接数及其表的摘要,你可以使用 php db:show 命令:
php artisan db:show
你可以通过 php --database 选项向命令提供数据库连接名称来指定应该检查哪个数据库连接:
php artisan db:show --database=pgsql
如果希望在命令的输出中包含表行计数和数据库视图详细信息,你可以分别提供 php --counts 和 php --views 选项。在大型数据库上,检索行数和视图详细信息可能很慢:
php artisan db:show --counts --views
此外,你可以使用以下 php Schema 方法来检查您的数据库:
use Illuminate\Support\Facades\Schema;$tables = Schema::getTables();$views = Schema::getViews();$columns = Schema::getColumns('users');$indexes = Schema::getIndexes('users');$foreignKeys = Schema::getForeignKeys('users');
你可以使用 php connection 方法指定数据库连接:
$columns = Schema::connection('sqlite')->getColumns('users');
表的摘要信息
如果你想获得数据库中单张表的概览,你可以执行 php db:table Artisan 命令。这个命令提供了一个数据库表的概览,包括它的列、类型、属性、键和索引:
php artisan db:table users
监视数据库
使用 php db:monitor Artisan 命令,如果你的数据库正在管理超过指定数量的打开连接,可以通过 Laravel 调度触发 php Illuminate\Database\Events\DatabaseBusy 事件。
开始,你应该将 php db:monitor 命令安排为每分钟运行一次。 该命令接受要监视的数据库连接配置的名称,以及在分派事件之前应允许的最大打开连接数:
php artisan db:monitor --databases=mysql,pgsql --max=100
仅调度此命令不足以触发通知提醒你打开连接的数量。当命令遇到一个超过阈值的打开连接数的数据库时,会调度一个 php DatabaseBusy 事件。你应该在应用程序的 php AppServiceProvider 中监听此事件,以便向你或你的开发团队发送通知:
use App\Notifications\DatabaseApproachingMaxConnections;use Illuminate\Database\Events\DatabaseBusy;use Illuminate\Support\Facades\Event;use Illuminate\Support\Facades\Notification;/*** 为应用程序注册任何其他事件。*/public function boot(): void{Event::listen(function (DatabaseBusy $event) {Notification::route('mail', '[email protected]')->notify(new DatabaseApproachingMaxConnections($event->connectionName,$event->connections));});}