数据库入门

未匹配的标注
本文档最新版为 10.x,旧版本可能放弃维护,推荐阅读最新版!

数据库:快速入门

简介

几乎所有的应用程序都需要和数据库进行交互。Laravel 为此提供了一套非常简单易用的数据库交互方式。开发者可以使用原生 SQL,查询构造器,以及Eloquent ORM,等方式与数据库交互。目前,Laravel 为以下五种数据库提供了官方支持:

配置

Laravel 数据库服务的配置位于应用程序的 config/database.php 配置文件中。在此文件中,您可以定义所有数据库连接,并指定默认情况下应使用的连接。此文件中的大多数配置选项由应用程序环境变量的值驱动。本文件提供了 Laravel 支持的大多数数据库系统的示例。

在默认情况下,Laravel 的示例 环境配置 使用了 Laravel Sail,Laravel Sail 是一种用于在本地开发 Laravel 应用的 Docker 配置。但你依然可以根据本地数据库的需要修改数据库配置。

SQLite 配置

SQLite数据库包含在文件系统的单个文件中。你可以在终端上使用 touch 命令创建一个新的SQLite数据库:touch database/database.sqlite。创建数据库之后,你可以通过在环境变量 DB_DATABASE 中设置数据库的绝对路径,轻松地配置环境变量来指向这个数据库:

DB_CONNECTION=sqlite
DB_DATABASE=/absolute/path/to/database.sqlite

默认情况下,对SQLite连接启用外键约束。如果你想禁用它们,你应该将 DB_FOREIGN_KEYS 环境变量设置为 false

DB_FOREIGN_KEYS=false

[!注意]
如果你使用Laravel安装程序来创建你的Laravel应用程序,并选择SQLite作为你的数据库,Laravel将自动创建一个 database/database.sqlite 文件运行默认的数据库迁移

Microsoft SQL Server 配置

要使用Microsoft SQL Server数据库,您应该确保安装了 sqlsrv and pdo_sqlsrv PHP扩展以及它们可能需要的任何依赖项,例如Microsoft SQL ODBC驱动程序。

使用URL配置

通常,数据库连接使用多个配置值进行配置,例如 host, database, username, password 等。这些配置值中的每一个都有自己相应的环境变量。这意味着在生产服务器上配置数据库连接信息时,需要管理几个环境变量。

部分数据库托管平台(如 AWS 和 Heroku)会提供了包含所有连接信息的数据库「URL」。它们通常看起来像这样:

mysql://root:password@127.0.0.1/forge?charset=UTF-8

这些 URL 通常遵循标准模式约定:

driver://username:password@host:port/database?options

为了方便起见,Laravel 支持使用这些 URL 替代传统的配置项来配置你的数据库。如果配置项 url (或其对应的环境变量 DATABASE_URL )存在,那么 Laravel 将会尝试从 URL 中提取数据库连接以及凭证信息。

读写分离

有时候你可能会希望使用一个数据库连接来执行 SELECT 语句,而 INSERTUPDATEDELETE 语句则由另一个数据库连接来执行。在 Laravel 中,无论你是使用原生 SQL 查询、查询构造器 或是 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'),
    ]) : [],
],

请注意,我们在数据库配置中加入了三个键,分别是: read, write 以及 stickyreadwrite 的值是一个只包含 host 键的数组。这代表其他的数据库选项将会从主 mysql 配置中获取。

如果你想要覆写主 mysql 配置,只需要将需要覆写的值放到 readwrite数组里即可。所以,在这个例子中,192.168.1.1 将会被用作「读」连接主机,而 192.168.1.3 将作为「写」连接主机。这两个连接将共享 mysql 数组中的各项配置,如数据库凭证(用户名、密码)、前缀、字符编码等。如果 host 数组中存在多个值,Laravel 将会为每个连接随机选取所使用的数据库主机。

sticky 选项

sticky 是一个 可选 值,它用于允许 Laravel 立即读取在当前请求周期内写入到数据库的记录。若 sticky 选项被启用,且在当前请求周期中执行过「写」操作,那么在这之后的所有「读」操作都将使用「写」连接。这样可以确保同一个请求周期中写入的数据库可以被立即读取到,从而避免主从同步延迟导致的数据不一致。不过是否启用它取决于项目的实际需求。

执行原生 SQL 查询

一旦配置好数据库连接,你就可以使用 DB Facade 来执行查询。DB Facade 为每种类型的查询都提供了相应的方法:selectupdateinsertdelete 以及 statement

执行 SELECT 查询

你可以使用 DB Facade 的 select 方法来执行一个基础的 SELECT 查询:

<?php

namespace 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]);
    }
}

传递给 select 方法的第一个参数是一个原生 SQL 查询语句,而第二个参数则是需要绑定到查询中的参数值。通常,这些值用于约束 where 语句。使用参数绑定可以有效防止 SQL 注入。

select 方法将始终返回一个包含查询结果的数组。数组中的每个结果都对应一个数据库记录的 stdClass 对象:

use Illuminate\Support\Facades\DB;

$users = DB::select('select * from users');

foreach ($users as $user) {
    echo $user->name;
}

选择标量值

有时你的数据库查询可能得到一个单一的标量值。而不是需要从记录对象中检索查询的标量结果,Laravel 允许你直接使用 scalar 方法检索此值:

$burgers = DB::scalar(
    "select count(case when food = 'burger' then 1 end) as burgers from menu"
);

选择多个结果集

如果你的应用程序调用返回多个结果集的存储过程,可以使用 selectResultSets 方法来检索存储过程返回的所有结果集:

[$options, $notifications] = DB::selectResultSets(
    "CALL get_user_options_and_notifications(?)", $request->user()->id
);

使用命名绑定

除了使用 ? 表示参数绑定外,你还可以使用命名绑定的形式来执行一个查询:

$results = DB::select('select * from users where id = :id', ['id' => 1]);

执行 Insert 语句

你可以使用 DB Facade 的 insert 方法来执行语句。跟 select 方法一样,该方法的第一个和第二个参数分别是原生 SQL 语句和绑定的数据:

use Illuminate\Support\Facades\DB;

DB::insert('insert into users (id, name) values (?, ?)', [1, 'Marc']);

执行 Update 语句

update 方法用于更新数据库中现有的记录。该方法将会返回受到本次操作影响的记录行数:

use Illuminate\Support\Facades\DB;

$affected = DB::update(
    'update users set votes = 100 where name = ?',
    ['Anita']
);

执行 Delete 语句

delete 函数被用于删除数据库中的记录。它的返回值与 update 函数相同,返回本次操作受影响的总行数。

use Illuminate\Support\Facades\DB;

$deleted = DB::delete('delete from users');

执行指定的 SQL

部分 SQL 语句不返回任何值。在这种情况下,你可能需要使用 DB::statement($sql) 来执行你的 SQL 语句。

DB::statement('drop table users');

直接执行 SQL

有时候你可能想执行一段 SQL 语句,但不需要进行 SQL 预处理绑定。这种情况下你可以使用 DB::unprepared($sql) 来执行你的 SQL 语句。

DB::unprepared('update users set votes = 100 where name = "Dries"');

注意
未经过预处理 SQL 的语句不绑定参数,它们可能容易受到 SQL 注入的攻击。在没有必要的理由的情况下,你不应直接在 SQL 中使用用户传入的数据。

在事务中的隐式提交

在事务中使用 DB::statement($sql)DB::unprepared($sql) 时,你必须要谨慎处理,避免 SQL 语句产生隐式提交。这些语句会导致数据库引擎间接地提交整个事务,让 Laravel 丢失数据库当前的事务级别。创建数据库表就是一个这样的语句的例子:

DB::unprepared('create table a (col varchar(1) null)');

请参考 MySQL 官方手册以了解更多隐式提交的信息。

使用多数据库连接

如果你在配置文件 config/database.php 中定义了多个数据库连接的话,你可以通过 DB Facade 的 connection 方法来使用它们。传递给 connection 方法的连接名称应该是你在 config/database.php 里或者通过 config 助手函数在运行时配置的连接之一:

use Illuminate\Support\Facades\DB;

$users = DB::connection('sqlite')->select(/* ... */);

你也可以使用一个连接实例上的 getPdo 方法来获取底层的 PDO 实例:

$pdo = DB::connection()->getPdo();

监听查询事件

如果你想要获取程序执行的每一条 SQL 语句,可以使用 Db facade 的 listen 方法。该方法对查询日志和调试非常有用,你可以在服务提供者中使用 boot 方法注册查询监听器。

<?php

namespace 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在单个请求中花费过多时间查询数据库时,它可以调用您选择的闭包或回调。要开始使用它,请提供查询时间阈值(以毫秒为单位)和闭包给 whenQueryingForLongerThan 方法。您可以在服务提供者boot 方法中调用此方法:

<?php

namespace 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) {
            // 通知开发团队...
        });
    }
}

数据库事务

你可以使用 DB 门面提供的 transaction 方法在数据库事务中运行一系列操作。如果在事务闭包内抛出异常,事务将自动回滚并重新抛出异常。如果闭包执行成功,事务将自动提交。在使用 transaction 方法时,您不需要担心手动回滚或提交:

use Illuminate\Support\Facades\DB;

DB::transaction(function () {
    DB::update('update users set votes = 1');

    DB::delete('delete from posts');
});

处理死锁

transaction 方法接受一个可选的第二个参数,该参数定义发生死锁时事务应重试的次数。一旦这些尝试用尽,就会抛出一个异常:

use Illuminate\Support\Facades\DB;

DB::transaction(function () {
    DB::update('update users set votes = 1');

    DB::delete('delete from posts');
}, 5);

手动执行事务

如果你想要手动处理事务并完全控制回滚和提交,可以使用 DB facade 提供的 beginTransaction 方法:

use Illuminate\Support\Facades\DB;

DB::beginTransaction();

你可以通过 rollBack 方法回滚事务:

DB::rollBack();

最后,你可以通过 commit 方法提交事务:

DB::commit();

技巧
DB 门面的事务方法还可以用于控制查询构造器Eloquent ORM

连接到数据库 CLI

如果你想连接到数据库的 CLI,则可以使用 db Artisan 命令:

php artisan db

如果需要,你可以指定数据库连接名称以连接到不是默认连接的数据库连接:

php artisan db mysql

检查你的数据库

使用 db:showdb:table Artisan 命令,你可以深入了解数据库及其相关表。要查看数据库的概述,包括它的大小、类型、打开的连接数及其表的摘要,你可以使用 db:show 命令:

php artisan db:show

你可以通过 --database 选项向命令提供数据库连接名称来指定应该检查哪个数据库连接:

php artisan db:show --database=pgsql

如果希望在命令的输出中包含表行计数和数据库视图详细信息,你可以分别提供 --counts--views 选项。在大型数据库上,检索行数和视图详细信息可能很慢:

php artisan db:show --counts --views

此外,你可以使用以下 Schema 方法来检查您的数据库:

use Illuminate\Support\Facades\Schema;

$tables = Schema::getTables();
$views = Schema::getViews();
$columns = Schema::getColumns('users');
$indexes = Schema::getIndexes('users');
$foreignKeys = Schema::getForeignKeys('users');

你可以使用 connection 方法指定数据库连接:

$columns = Schema::connection('sqlite')->getColumns('users');

表的摘要信息

如果你想获得数据库中单张表的概览,你可以执行 db:table Artisan 命令。这个命令提供了一个数据库表的概览,包括它的列、类型、属性、键和索引:

php artisan db:table users

监视数据库

使用 db:monitor Artisan 命令,如果你的数据库正在管理超过指定数量的打开连接,可以通过 Laravel 调度触发 Illuminate\Database\Events\DatabaseBusy 事件。

开始,你应该将 db:monitor 命令安排为每分钟运行一次。 该命令接受要监视的数据库连接配置的名称,以及在分派事件之前应允许的最大打开连接数:

php artisan db:monitor --databases=mysql,pgsql --max=100

仅调度此命令不足以触发通知提醒你打开连接的数量。当命令遇到一个超过阈值的打开连接数的数据库时,会调度一个 DatabaseBusy 事件。你应该在应用程序的 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', 'dev@example.com')
                ->notify(new DatabaseApproachingMaxConnections(
                    $event->connectionName,
                    $event->connections
                ));
    });
}

本文章首发在 LearnKu.com 网站上。

本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://learnku.com/docs/laravel/11.x/da...

译文地址:https://learnku.com/docs/laravel/11.x/da...

上一篇 下一篇
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
贡献者:8
讨论数量: 0
发起讨论 只看当前版本


暂无话题~