数据库:入门
简介
几乎每个现代 Web 应用程序都会与数据库交互。Laravel 通过各种受支持的数据库,使用原生 SQL、流畅的查询构造器 和 Eloquent ORM,使数据库交互变得极其简单。目前,Laravel 为五种数据库提供第一方支持
此外,MongoDB 通过 mongodb/laravel-mongodb
扩展包获得支持,该扩展包由 MongoDB 官方维护。查阅 Laravel MongoDB 文档以获取更多信息。
配置
Laravel 数据库服务的配置位于应用程序的 config/database.php
配置文件中。在此文件中,你可以定义所有的数据库连接,并指定默认使用的连接。此文件中的大多数配置选项都由应用程序的环境变量值驱动。文件中提供了 Laravel 支持的大多数数据库系统的示例。
默认情况下,Laravel 的示例 环境配置 已准备好与 Laravel Sail 一起使用,Laravel Sail 是用于在本地机器上开发 Laravel 应用程序的 Docker 配置。但是,你可以根据本地数据库的需要自由修改数据库配置。
SQLite 配置
SQLite 数据库包含在文件系统上的单个文件中。你可以使用终端中的 touch
命令创建一个新的 SQLite 数据库:touch database/database.sqlite
。创建数据库后,你可以通过将数据库的绝对路径放在 DB_DATABASE
环境变量中,轻松配置环境变量以指向此数据库。
1DB_CONNECTION=sqlite2DB_DATABASE=/absolute/path/to/database.sqlite
默认情况下,外键约束对于 SQLite 连接是启用的。如果你想禁用它们,你应该将 DB_FOREIGN_KEYS
环境变量设置为 false
。
1DB_FOREIGN_KEYS=false
如果你使用 Laravel 安装器 创建 Laravel 应用程序并选择 SQLite 作为数据库,Laravel 将自动创建一个 database/database.sqlite
文件并为你运行默认的 数据库迁移。
Microsoft SQL Server 配置
要使用 Microsoft SQL Server 数据库,你应该确保已安装 sqlsrv
和 pdo_sqlsrv
PHP 扩展,以及它们可能需要的任何依赖项,例如 Microsoft SQL ODBC 驱动程序。
使用 URL 进行配置
通常,数据库连接使用多个配置值进行配置,例如 host
、database
、username
、password
等。每个配置值都有其对应的环境变量。这意味着在生产服务器上配置数据库连接信息时,你需要管理多个环境变量。
一些托管数据库提供商,例如 AWS 和 Heroku,提供一个包含数据库所有连接信息的单个数据库“URL”字符串。数据库 URL 示例可能如下所示:
1mysql://root:[email protected]/forge?charset=UTF-8
这些 URL 通常遵循标准的模式约定。
1driver://username:password@host:port/database?options
为了方便起见,Laravel 支持使用这些 URL 作为使用多个配置选项配置数据库的替代方法。如果存在 url
(或相应的 DB_URL
环境变量)配置选项,它将用于提取数据库连接和凭据信息。
读写连接
有时你可能希望对 SELECT 语句使用一个数据库连接,而对 INSERT、UPDATE 和 DELETE 语句使用另一个数据库连接。Laravel 使这变得轻而易举,无论你使用原生查询、查询构造器还是 Eloquent ORM,都将始终使用正确的连接。
要了解如何配置读/写连接,让我们看一个例子:
1'mysql' => [ 2 'read' => [ 3 'host' => [ 4 '192.168.1.1', 5 '196.168.1.2', 6 ], 7 ], 8 'write' => [ 9 'host' => [10 '196.168.1.3',11 ],12 ],13 'sticky' => true,14 15 'database' => env('DB_DATABASE', 'laravel'),16 'username' => env('DB_USERNAME', 'root'),17 'password' => env('DB_PASSWORD', ''),18 'unix_socket' => env('DB_SOCKET', ''),19 'charset' => env('DB_CHARSET', 'utf8mb4'),20 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),21 'prefix' => '',22 'prefix_indexes' => true,23 'strict' => true,24 'engine' => null,25 'options' => extension_loaded('pdo_mysql') ? array_filter([26 PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),27 ]) : [],28],
请注意,配置数组中添加了三个键:read
、write
和 sticky
。read
和 write
键具有包含单个键 host
的数组值。read
和 write
连接的其余数据库选项将从主 mysql
配置数组合并。
只有当你希望覆盖主 mysql
数组中的值时,才需要在 read
和 write
数组中放置条目。因此,在这种情况下,192.168.1.1
将用作“读取”连接的主机,而 192.168.1.3
将用作“写入”连接的主机。主 mysql
数组中的数据库凭据、前缀、字符集和所有其他选项将在两个连接之间共享。当 host
配置数组中存在多个值时,将为每个请求随机选择一个数据库主机。
sticky
选项
sticky
选项是一个可选值,可用于允许立即读取在当前请求周期内写入数据库的记录。如果启用了 sticky
选项,并且在当前请求周期内对数据库执行了“写入”操作,则任何进一步的“读取”操作都将使用“写入”连接。这确保了在请求周期内写入的任何数据都可以在同一请求期间立即从数据库中读回。是否需要此行为由你决定。
运行 SQL 查询
配置数据库连接后,你可以使用 DB
facade 运行查询。DB
facade 为每种类型的查询提供了方法:select
、update
、insert
、delete
和 statement
。
运行 Select 查询
要运行基本的 SELECT 查询,你可以使用 DB
facade 上的 select
方法。
1<?php 2 3namespace App\Http\Controllers; 4 5use App\Http\Controllers\Controller; 6use Illuminate\Support\Facades\DB; 7use Illuminate\View\View; 8 9class UserController extends Controller10{11 /**12 * Show a list of all of the application's users.13 */14 public function index(): View15 {16 $users = DB::select('select * from users where active = ?', [1]);17 18 return view('user.index', ['users' => $users]);19 }20}
传递给 select
方法的第一个参数是 SQL 查询,而第二个参数是需要绑定到查询的任何参数绑定。通常,这些是 where
子句约束的值。参数绑定提供了防止 SQL 注入的保护。
select
方法将始终返回一个结果 array
。数组中的每个结果都将是一个 PHP stdClass
对象,表示数据库中的一条记录。
1use Illuminate\Support\Facades\DB;2 3$users = DB::select('select * from users');4 5foreach ($users as $user) {6 echo $user->name;7}
选择标量值
有时,你的数据库查询可能会产生单个标量值。Laravel 允许你直接使用 scalar
方法检索此值,而无需从记录对象中检索查询的标量结果。
1$burgers = DB::scalar(2 "select count(case when food = 'burger' then 1 end) as burgers from menu"3);
选择多个结果集
如果你的应用程序调用返回多个结果集的存储过程,你可以使用 selectResultSets
方法检索存储过程返回的所有结果集。
1[$options, $notifications] = DB::selectResultSets(2 "CALL get_user_options_and_notifications(?)", $request->user()->id3);
使用命名绑定
除了使用 ?
表示参数绑定之外,你还可以使用命名绑定执行查询。
1$results = DB::select('select * from users where id = :id', ['id' => 1]);
运行 Insert 语句
要执行 insert
语句,你可以使用 DB
facade 上的 insert
方法。与 select
一样,此方法接受 SQL 查询作为其第一个参数,绑定作为其第二个参数。
1use Illuminate\Support\Facades\DB;2 3DB::insert('insert into users (id, name) values (?, ?)', [1, 'Marc']);
运行 Update 语句
update
方法应用于更新数据库中的现有记录。该方法返回受语句影响的行数。
1use Illuminate\Support\Facades\DB;2 3$affected = DB::update(4 'update users set votes = 100 where name = ?',5 ['Anita']6);
运行 Delete 语句
delete
方法应用于从数据库中删除记录。与 update
一样,该方法将返回受影响的行数。
1use Illuminate\Support\Facades\DB;2 3$deleted = DB::delete('delete from users');
运行通用语句
一些数据库语句不返回任何值。对于这些类型的操作,你可以使用 DB
facade 上的 statement
方法。
1DB::statement('drop table users');
运行未预处理语句
有时你可能想要执行 SQL 语句而不绑定任何值。你可以使用 DB
facade 的 unprepared
方法来完成此操作。
1DB::unprepared('update users set votes = 100 where name = "Dries"');
由于未预处理语句不绑定参数,因此它们可能容易受到 SQL 注入的攻击。你绝不应该在未预处理语句中允许用户控制的值。
隐式提交
在事务中使用 DB
facade 的 statement
和 unprepared
方法时,你必须小心避免导致 隐式提交 的语句。这些语句将导致数据库引擎间接提交整个事务,使 Laravel 不知道数据库的事务级别。创建数据库表就是这样一个语句的例子。
1DB::unprepared('create table a (col varchar(1) null)');
请参阅 MySQL 手册,获取 触发隐式提交的所有语句的列表。
使用多个数据库连接
如果你的应用程序在 config/database.php
配置文件中定义了多个连接,你可以通过 DB
facade 提供的 connection
方法访问每个连接。传递给 connection
方法的连接名称应与 config/database.php
配置文件中列出的连接之一相对应,或使用 config
辅助函数在运行时配置。
1use Illuminate\Support\Facades\DB;2 3$users = DB::connection('sqlite')->select(/* ... */);
你可以使用连接实例上的 getPdo
方法访问连接的原始底层 PDO 实例。
1$pdo = DB::connection()->getPdo();
监听查询事件
如果你想指定一个闭包,该闭包将在应用程序执行的每个 SQL 查询时被调用,你可以使用 DB
facade 的 listen
方法。此方法对于日志记录查询或调试很有用。你可以在 服务提供者 的 boot
方法中注册你的查询监听器闭包。
1<?php 2 3namespace App\Providers; 4 5use Illuminate\Database\Events\QueryExecuted; 6use Illuminate\Support\Facades\DB; 7use Illuminate\Support\ServiceProvider; 8 9class AppServiceProvider extends ServiceProvider10{11 /**12 * Register any application services.13 */14 public function register(): void15 {16 // ...17 }18 19 /**20 * Bootstrap any application services.21 */22 public function boot(): void23 {24 DB::listen(function (QueryExecuted $query) {25 // $query->sql;26 // $query->bindings;27 // $query->time;28 // $query->toRawSql();29 });30 }31}
监控累计查询时间
现代 Web 应用程序的一个常见性能瓶颈是它们花费在查询数据库上的时间。值得庆幸的是,当 Laravel 在单个请求期间花费太多时间查询数据库时,它可以调用你选择的闭包或回调。要开始使用,请为 whenQueryingForLongerThan
方法提供查询时间阈值(以毫秒为单位)和闭包。你可以在 服务提供者 的 boot
方法中调用此方法。
1<?php 2 3namespace App\Providers; 4 5use Illuminate\Database\Connection; 6use Illuminate\Support\Facades\DB; 7use Illuminate\Support\ServiceProvider; 8use Illuminate\Database\Events\QueryExecuted; 9 10class AppServiceProvider extends ServiceProvider11{12 /**13 * Register any application services.14 */15 public function register(): void16 {17 // ...18 }19 20 /**21 * Bootstrap any application services.22 */23 public function boot(): void24 {25 DB::whenQueryingForLongerThan(500, function (Connection $connection, QueryExecuted $event) {26 // Notify development team...27 });28 }29}
数据库事务
你可以使用 DB
facade 提供的 transaction
方法在数据库事务中运行一组操作。如果在事务闭包中抛出异常,事务将自动回滚,并且异常将被重新抛出。如果闭包成功执行,事务将自动提交。使用 transaction
方法时,你无需担心手动回滚或提交。
1use Illuminate\Support\Facades\DB;2 3DB::transaction(function () {4 DB::update('update users set votes = 1');5 6 DB::delete('delete from posts');7});
处理死锁
transaction
方法接受一个可选的第二个参数,该参数定义发生死锁时应重试事务的次数。一旦这些尝试耗尽,将抛出一个异常。
1use Illuminate\Support\Facades\DB;2 3DB::transaction(function () {4 DB::update('update users set votes = 1');5 6 DB::delete('delete from posts');7}, 5);
手动使用事务
如果你想手动开始一个事务并完全控制回滚和提交,你可以使用 DB
facade 提供的 beginTransaction
方法。
1use Illuminate\Support\Facades\DB;2 3DB::beginTransaction();
你可以通过 rollBack
方法回滚事务。
1DB::rollBack();
最后,你可以通过 commit
方法提交事务。
1DB::commit();
DB
facade 的事务方法控制 查询构造器 和 Eloquent ORM 的事务。
连接到数据库 CLI
如果你想连接到数据库的 CLI,你可以使用 db
Artisan 命令。
1php artisan db
如果需要,你可以指定一个数据库连接名称来连接到非默认连接的数据库连接。
1php artisan db mysql
检查你的数据库
使用 db:show
和 db:table
Artisan 命令,你可以深入了解你的数据库及其关联的表。要查看数据库的概述,包括其大小、类型、打开连接数以及表摘要,你可以使用 db:show
命令。
1php artisan db:show
你可以通过 --database
选项向命令提供数据库连接名称,以指定应检查哪个数据库连接。
1php artisan db:show --database=pgsql
如果你想在命令输出中包含表行计数和数据库视图详细信息,你可以分别提供 --counts
和 --views
选项。在大型数据库上,检索行计数和视图详细信息可能很慢。
1php artisan db:show --counts --views
此外,你可以使用以下 Schema
方法来检查你的数据库。
1use Illuminate\Support\Facades\Schema;2 3$tables = Schema::getTables();4$views = Schema::getViews();5$columns = Schema::getColumns('users');6$indexes = Schema::getIndexes('users');7$foreignKeys = Schema::getForeignKeys('users');
如果你想检查不是应用程序默认连接的数据库连接,你可以使用 connection
方法。
1$columns = Schema::connection('sqlite')->getColumns('users');
表格概述
如果你想获得数据库中单个表的概述,你可以执行 db:table
Artisan 命令。此命令提供数据库表的常规概述,包括其列、类型、属性、键和索引。
1php artisan db:table users
监控你的数据库
使用 db:monitor
Artisan 命令,你可以指示 Laravel 在数据库管理的打开连接数超过指定数量时,调度 Illuminate\Database\Events\DatabaseBusy
事件。
要开始使用,你应该计划 每分钟运行一次 db:monitor
命令。该命令接受你要监视的数据库连接配置的名称,以及在调度事件之前应容忍的最大打开连接数。
1php artisan db:monitor --databases=mysql,pgsql --max=100
单独调度此命令不足以触发通知,提醒你打开连接的数量。当命令遇到打开连接数超过阈值的数据库时,将调度 DatabaseBusy
事件。你应该在应用程序的 AppServiceProvider
中监听此事件,以便向你或你的开发团队发送通知。
1use App\Notifications\DatabaseApproachingMaxConnections; 2use Illuminate\Database\Events\DatabaseBusy; 3use Illuminate\Support\Facades\Event; 4use Illuminate\Support\Facades\Notification; 5 6/** 7 * Bootstrap any application services. 8 */ 9public function boot(): void10{11 Event::listen(function (DatabaseBusy $event) {13 ->notify(new DatabaseApproachingMaxConnections(14 $event->connectionName,15 $event->connections16 ));17 });18}