mcn-healthcare / dynamodb-odm
DynamoDb的ODM
Requires
- aws/aws-sdk-php: >=3.107
- doctrine/annotations: >=1.6
- symfony/console: >=5.1
- symfony/finder: >=5.1
Requires (Dev)
- liip/functional-test-bundle: >=3.0.0
- mockfromyaml/mockfromyaml: >=2.0.1
- phpunit/phpunit: >=8
- symfony/console: >=5.4
- symfony/phpunit-bridge: >=5.1
- symfony/yaml: >=5.1
This package is auto-updated.
Last update: 2024-09-06 01:20:56 UTC
README
mcn-healthcare/dynamodb-odm 是一个 ODM(对象数据映射)库,用于轻松使用 AWS 强大的键值数据库:DynamoDb。
这是一个从: https://github.com/oasmobile/php-dynamodb-odm 分支出来的版本。
此次主要变更包括直接使用 AWS DynamoDb 客户端,以及在 DynamoDb 层面开始记录更改。
注意:本文档假设您对 DynamoDB 有一定的了解,以及 DynamoDB 与传统 RDBMS(例如 MySQL)之间的区别。本文档中讨论的一些术语和概念是 DynamoDB 特有的,将不会在本文档中解释。要学习 DynamoDB,请参阅官方开发指南
安装和配置
要获取 oasis/dynamodb-odm,您可以通过 composer 简单地引入它
$ composer require mcn-healthcare/dynamodb-odm
Linux, BSD, Mac
- 使用以下命令更新环境
$ export AWS_SECRET_ACCESS_KEY=mysecrettestaccesskey $ export AWS_SECRET_ACCESS_KEY_ID=mysecrettestaccesskeyid
- 使用以下值编辑
~/.aws/credentials
[test-profile]
aws_session_token=test-token
aws_access_key_id=test-key-id
aws_secret_access_key=mysecrettestaccesskey
aws_secret_access_key_id=mysecrettestaccesskeyid
类加载
DynamoDb ODM 的自动加载由 composer 处理。您只需在项目中包含 composer 自动加载文件即可
<?php require_once "vendor/autoload.php";
获取 ItemManager
一旦您准备好了类加载,您就可以获取一个 ItemManager 实例。ItemManager 类是库提供的 ODM 功能的主要访问点。
<?php use McnHealthcare\ODM\Dynamodb\ItemManager; $client = new \Aws\DynamoDb\DynamoDbClient( [ 'version' => 'latest', 'region' => 'us-west-2', 'endpoint' => 'https://:8000', 'credentials' => [ 'key' => 'my-access-key-id', 'secret' => 'my-secret-access-key', ] ] ); $tablePrefix = "odm-"; $itemNamespace = 'McnHealthcare\ODM\Dynamodb\Ut'; // in practice, this usually looks like: My\Root\Namespace\Items $itemSrcDir = __DIR__ . "/ut"; // in practice, this usually points to src/Items directory $im = new ItemManager( $client, $tablePrefix, $reader, $logger ); $im->addNamespace( $itemNamespace, $itemSrcDir );
以下是对每个参数的解释
注意:Item 类定义了 ODM 管理的项目类型。一些典型示例包括:User、Order、GameRoom 和 Card
设置命令行工具
DynamoDb ODM 随带了一些非常有用的命令行工具,在开发过程中非常有帮助。您可以从 Composer 二进制目录调用此命令
$ ./vendor/bin/oasis-dynamodb-odm
您需要将应用程序的 ItemManager 注册到控制台工具中,以便使用内置命令。这通过在调用目录下创建一个 odm-config.php 文件来完成,内容如下
<?php use McnHealthcare\ODM\Dynamodb\Console\ConsoleHelper; // replace with file to your own project bootstrap require_once 'bootstrap.php'; // replace with your own mechanism to retrieve the item manager $itemManager = GetItemManager(); return new ConsoleHelper($itemManager);
详细信息请见后续部分
映射
ODM 库的基本功能是将对象模型(即类)映射到数据库结构。DynamoDb ODM 通过注释提供了一种方便的方法来建立这种映射
<?php use McnHealthcare\ODM\Dynamodb\Annotations\Field; use McnHealthcare\ODM\Dynamodb\Annotations\Item; /** * @Item( * table="users", * primaryIndex={"id"} * ) */ class User { /** * @var int * @Field(type="number") */ protected $id; /** * @var string * @Field(type="string") */ protected $name; /** * Last updated is set to unix timestamp on every update * * @var int * @Field(type="int", cas="timestamp") */ protected $lastUpdated; }
上面的类声明了一个简单的 User 模型,它将被映射到 DynamoDb 表 "users"(可能带有前缀)。注释如下
Item
您想使用 ODM 保存到数据库的每个 PHP 对象都称为 "Item"。为了将对象描述为项,我们必须描述该对象的类。
带有 @Item 注解的类将由 ItemManager 管理。Item 接受以下属性
- table:对象的表名
- primaryIndex:主键,可以是键的数组,或 @Index 注解对象
- globalSecondaryIndices:全局二级索引的数组;全局二级索引可以是键的数组,或 @Index 注解对象
- localSecondaryIndices:局部二级索引的数组;局部二级索引可以是键的数组,或 @Index 注解对象
- repository:存储库类名称;默认情况下,使用
\McnHealthcare\ODM\Dynamodb\ItemRepository - 预期:判断这个条目是否仅为预期。 预期条目不可更新(允许删除操作)。当读取(即获取/查询/扫描)一个预期条目时,仅从DynamoDB获取此条目的属性。
字段
将PHP类作为项目创建后,下一步是将其属性映射到DynamoDb的属性上。
我们使用@Field注解来描述类属性,这些属性是DynamoDb属性。 @Field注解支持以下属性
- type:属性的属性类型,可以是以下之一
- 字符串(默认)
- 数字
- 二进制
- 布尔值
- 空值
- 列表
- 映射
- name:DynamoDb属性的名称,如果它与属性名称不同。默认值是
null,表示属性键与属性名称相同。
日志记录
当想要记录表上的更改时,请在实体类上使用带有布尔值的@ActivityLogging注解。
<?php /** * @ActivityLogging(enable="true") */ class myEntity { //... entity details }
- changedBy:在表上执行更改的人
- loggedTable:正在更改的表
- offset:UTC时间的日期/时间偏移量(秒)
警告不要将此值添加到您要记录的表中,它将导致循环失败!
设置活动日志详情方法
<?php $ald = new ActivityLoggingDetails($changedBy, $loggedTable, $offset);
分区哈希键
分区哈希键是用于查询中哈希键的字段的扩展。有些情况下,您可能只想查询该字段的单个值(或非常少的值)。DynamoDB的性质只会利用分配的读取容量的很小一部分,因此限制了系统性能。然而,通过使用该字段的分区哈希键,原始字段的每个值将在分区哈希键字段中有多个映射值。通过并行查询这些分区值(请参阅多查询),在此用例中,查询性能应该会显著提高。
我们使用@PartitionedHashKey注解来描述分区哈希键字段。支持的属性包括
- baseField:原始字段名称
- hashField:用作哈希源的值所在的字段,始终使用分布良好的字段作为哈希字段(例如,项目的唯一ID)
- size:分区大小,默认为16
索引
在声明不同的索引时,我们可以使用@Index注解来使文档块更易于阅读。一个@Index由两个键组成
- hash:哈希键名称
- range:范围键名称,如果此索引没有范围键,则留空
- name:索引名称,如果您希望ODM为您自动生成一个,则留空。(注意:主索引不需要名称)
以下是我们向其中添加全局二级索引的User类声明
/** * @Item( * table="users", * primaryIndex={"id"}, * globalSecondaryIndices={ * @Index(hash="class", range="age", name="class-age-gsi") * } * ) */ class User { // ... /** * @var string * @Field() */ protected $class; /** * @var int * @Field(type="number") */ protected $age; }
检查和设置
一个字段可以声明为检查和设置字段,使用@Field注解的"cas"属性。
检查和设置字段是ODM使用以确保没有单个项目在相同时间被不同的工作者更新/插入超过一次的字段。
"cas"属性值可以是以下之一
- disabled:这是默认值,具有"cas"禁用的字段在更新/插入项目时将不会被检查
- enabled:更新项目时将检查此字段的老值。在插入项目时,此字段必须具有NULL值,或不存在。
- 时间戳:这是一种特殊的启用cas属性。每次更新/插入项目时,此字段的值将自动设置为当前时间戳。
以下是一个使用时间戳在每次创建/更新后进行更新的示例。
<?php class User { // ... /** * @var int * @Field(type="int", cas="timestamp") */ protected $lastUpdated; }
注意:仅在调用
ItemManger#flush()时进行检查和设置验证。如果未能满足检查和设置条件,将引发McnHealthcare\ODM\Dynamodb\Exceptions\DataConsistencyException异常。
与对象协同工作
ODM中所有对象(项目)都受管理。对象操作像对象级事务一样进行管理。一旦对象被管理,无论是作为新对象持久化还是从数据库中检索,其管理状态都将存储在ItemManager中。对象上的任何更改都将记录在内存中。然后可以通过在ItemManager上调用ItemManager#flush()方法来提交对象更改。
可以通过调用ItemManager#clear()手动清除ItemManager。然而,尚未提交的任何更改都将丢失。
注意:非常重要的一点是理解,只有
ItemManager#flush()才会对数据库执行写操作。任何其他方法,如ItemManager#persist($item)或ItemManager#remove($item),只会通知ItemManager在刷新期间执行这些操作。不调用ItemManager#flush()将导致请求期间的所有更改丢失。
持久化项目
可以通过传递给ItemManager#persist($item)方法来使项目持久化。通过在某个项目上应用持久化操作,该项目变为管理的,这意味着从现在起,其持久化由ItemManager管理。因此,此类项目的持久化状态将在调用ItemManager#flush()时与数据库正确同步。
示例
<?php /** @var ItemManger $im */ $user = new User(); $user->setName('Mr.Right'); $im->persist($user); $im->flush();
移除项目
可以通过传递给ItemManager#remove($item)方法从持久化存储中移除项目。通过在某个项目上应用移除操作,该项目变为已移除的,这意味着一旦调用ItemManager#flush(),其持久化状态将被删除。
示例
<?php /** @var ItemManger $im */ /** @var User $user */ $im->remove($user); $im->flush();
分离项目
可以通过在项目上调用ItemManager#detach($item)方法将其从ItemManager中分离,从而不再由其管理。如果对已分离的项目进行了更改(包括删除项目),则在项目分离后,这些更改(包括删除项目)将不会同步到数据库。
DynamoDb ODM不会保留对已分离项目的任何引用。
示例
<?php /** @var ItemManger $im */ /** @var User $user */ $im->detach($user); $user->setName('Mr.Left'); $im->flush(); // changes to $user will not be synchronized
与数据库同步
ItemManager的刷新时,持久化项目的状态会与数据库同步。同步涉及将任何更新写入持久化项目到数据库。当调用ItemManager#flush()时,ODM将检查所有管理、新和已删除的项目,并执行以下操作
- 在数据库中创建新对象
- 更新数据库中管理的项目的更改属性
- 从数据库中删除已删除的项目
获取项目
DynamoDb ODM提供以下方式,按照功能和灵活性的增加顺序,来获取持久化对象。您应该始终从适合您需求的最简单的一种开始。
通过主索引
获取持久化对象的最基本方法是使用ItemManager#get($itemClass, $primayKeys)方法通过其主索引。以下是一个示例
<?php /** @var ItemManager $im */ $user = $im->get(User::class, ["id" => 1]);
返回值是找到的项目实例,或者如果没有找到具有给定标识符的实例,则返回null。
本质上,ItemManager#get()只是以下方法的快捷方式
/** @var ItemManager $im */ /** @var ItemRepository $userRepo */ $userRepo = $im->getRepository(User::class); $user = $userRepo->get(["id" => 1]);
基于可查询索引上的简单条件
要基于简单条件查询一个或多个项目,请在存储库上使用ItemManager#query()和ItemManager#queryAndRun()方法,如下所示
/** @var ItemManager $im */ /** @var ItemRepository $userRepo */ $userRepo = $im->getRepository(User::class); /** @var Users[] $users */ $users = $userRepo->query( "#class = :class AND #age >= :minAge", [ ":class" => "A", ":minAge" => 25, ], "class-age-index" );
注意:简单条件是只使用一个索引的条件。如果使用的索引包含哈希键和范围键,则只有当在条件中也提供哈希键时,才能使用范围键。此外,只能对哈希键执行相等测试操作。
使用分区哈希键的多查询
要根据分区哈希键查询一个或多个项目,请在存储库上使用 ItemManager#multiQueryAndRun() 方法,如下所示
/** @var ItemManager $im */ /** @var ItemRepository $userRepo */ $userRepo = $im->getRepository(User::class); /** @var Users[] $users */ $users = $userRepo->multiQueryAndRun( function ($item) { // each item returned can be accessed here in the callback }, "classPartition", // PartitionedHashKey field name "A", // value expected in the base field (not the partition field) "#age >= :minAge", // only range conditions here [ ":minAge" => 25, ], "class-partition-age-index" // index for PartitionedHashKey );
基于不可查询索引的过滤器
要查询没有关联索引的一个或多个项目,请在存储库上使用 ItemManager#scan() 和 ItemManager#scanAndRun() 方法,如下所示
/** @var ItemManager $im */ /** @var ItemRepository $userRepo */ $userRepo = $im->getRepository(User::class); /** @var Users[] $users */ $users = $userRepo->scan( "#class = :class AND #age >= :minAge AND #name = :name", [ ":class" => "A", ":minAge" => 25, ":name" => "John", ], );
使用命令行工具
DynamoDb ODM 随库一起提供了一个可执行工具。安装后,以下是一些内置命令,可以帮助您管理项目的数据库模式
创建
$ ./vendor/bin/oasis-dynamodb-odm odm:schema-tool:create
创建命令将遍历所有管理的项目并创建相应的表。所有主索引、LSI 和 GSI 都会创建。
注意:如果在同一前缀下已存在具有相同名称的表,将抛出异常。在这种情况下不会创建任何表。
注意:如果您想要跳过创建现有的表(即仅创建不存在的表),可以使用 “--skip-existing-table” 选项
更新
$ ./vendor/bin/oasis-dynamodb-odm odm:schema-tool:update
更新命令实际上是创建命令的一个更强大(但速度也更慢)版本。它检查所有管理的项目,并在表不存在时创建表。此外,如果表已存在但定义了不同的 GSI,更新命令将相应地更新 GSI。
注意:由于 DynamoDb 的特性,在表已创建的情况下无法更新主索引或 LSI。在开发环境中,建议在需要时删除表并重新创建。
注意:如果您只想查看数据库模式的变化而不执行实际更新,可以在命令行中指定 “--dry-run” 选项。程序将仅提示可能的变化而不会实际执行。
删除
$ ./vendor/bin/oasis-dynamodb-odm odm:schema-tool:drop
删除命令将删除所有与管理的项目关联的表。不要在生产环境中运行此命令!