免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

AWS SDK for Java v2 深度解析:DynamoDB Enhanced Client 的对象映射、扩展机制与异步操作

AWS SDK for Java v2 深度解析:DynamoDB Enhanced Client 的对象映射、扩展机制与异步操作 AWS SDK for Java v2 深度解析DynamoDB Enhanced Client 的对象映射、扩展机制与异步操作【免费下载链接】aws-sdk-java-v2The official AWS SDK for Java - Version 2项目地址: https://gitcode.com/GitHub_Trending/aw/aws-sdk-java-v2本文基于 DynamoDB Enhanced Client 官方文档 系统讲解 AWS SDK for Java v2 中 DynamoDB 中间层客户端的完整用法如何定义 Bean/不可变类与 TableSchema、执行 CRUD 与批量事务操作、查询二级索引、使用异步非阻塞接口以及如何通过beforeWrite/afterRead扩展点实现乐观锁、原子计数器与自动时间戳。读完本文你可以直接在 Java 项目中使用对象化的 DynamoDB 编程模型并结合仓库源码理解其底层映射与扩展机制。模块定位什么是 DynamoDB Enhanced ClientDynamoDB Enhanced Client 是构建在低层DynamoDbClient之上的中间层mid-level映射/抽象层位于仓库的services-custom/dynamodb-enhanced模块。从 模块 POM 可以看到Maven 坐标为software.amazon.awssdk:dynamodb-enhanced依赖同版本的dynamodb服务客户端、sdk-core、aws-core等核心模块基线运行时为 JRE 1.8JAR 的Automatic-Module-Name为software.amazon.awssdk.enhanced.dynamodb测试体系使用 DynamoDB Localcom.amazonaws:DynamoDBLocal WireMock 做功能级验证。其核心抽象在 TableSchema.java一个能把 Java 对象与MapString, AttributeValue相互映射、并持有表结构元数据分区键、排序键、二级索引的映射器。快速上手从 Bean 定义到 CRUD1. 定义 DynamoDb Bean重要前提DynamoDbBean类的字段绝不能声明为final。Enhanced Client 要求字段可变mutable才能正确完成映射。以下示例使用一个虚构的Customer类非库内置类键值均为任意取值DynamoDbBean public class Customer { private String accountId; private int subId; // primitive types are supported private String name; private Instant createdDate; DynamoDbPartitionKey public String getAccountId() { return this.accountId; } public void setAccountId(String accountId) { this.accountId accountId; } DynamoDbSortKey public int getSubId() { return this.subId; } public void setSubId(int subId) { this.subId subId; } // Defines a GSI (customers_by_name) with a partition key of name DynamoDbSecondaryPartitionKey(indexNames customers_by_name) public String getName() { return this.name; } public void setName(String name) { this.name name; } // Defines an LSI (customers_by_date) with a sort key of createdDate and also declares the // same attribute as a sort key for the GSI named customers_by_name DynamoDbSecondarySortKey(indexNames {customers_by_date, customers_by_name}) public Instant getCreatedDate() { return this.createdDate; } public void setCreatedDate(Instant createdDate) { this.createdDate createdDate; } }注解均定义在 mapper/annotations 包下如 DynamoDbBean.java、DynamoDbPartitionKey.java、DynamoDbSecondarySortKey.java。2. 构建 TableSchema注解推断 vs 静态声明最简方式是使用TableSchema.fromClass()它会扫描注解并推断表结构源码中fromClass位于 TableSchema.java#L203static final TableSchemaCustomer CUSTOMER_TABLE_SCHEMA TableSchema.fromClass(Customer.class);如果不想付出 Bean 推断的开销可以用StaticTableSchema的 builder 直接声明 schema此时类不需要遵循 Bean 命名规范、也无需任何注解。下面这个示例与上面的 Bean 示例完全等价static final TableSchemaCustomer CUSTOMER_TABLE_SCHEMA TableSchema.builder(Customer.class) .newItemSupplier(Customer::new) .addAttribute(String.class, a - a.name(account_id) .getter(Customer::getAccountId) .setter(Customer::setAccountId) .tags(primaryPartitionKey())) .addAttribute(Integer.class, a - a.name(sub_id) .getter(Customer::getSubId) .setter(Customer::setSubId) .tags(primarySortKey())) .addAttribute(String.class, a - a.name(name) .getter(Customer::getName) .setter(Customer::setName) .tags(secondaryPartitionKey(customers_by_name))) .addAttribute(Instant.class, a - a.name(created_date) .getter(Customer::getCreatedDate) .setter(Customer::setCreatedDate) .tags(secondarySortKey(customers_by_date), secondarySortKey(customers_by_name))) .build();TableSchema.builder(...)的静态工厂在接口中直接委托给 StaticTableSchema而 TableSchema.java#L45-L46 上的ThreadSafe标注说明 schema 一旦构建完成即可跨线程复用。Javadoc 明确建议由于反射推断是适度昂贵的操作每个类只应创建一次 schema通常在应用启动时完成。3. 创建 Enhanced Client 与 Table 资源// 创建增强客户端用于对多张表重复执行操作 DynamoDbEnhancedClient enhancedClient DynamoDbEnhancedClient.builder() .dynamoDbClient(dynamoDbClient) .build(); // 将物理表 customers_20190205 映射到 schema DynamoDbTableCustomer customerTable enhancedClient.table(customers_20190205, CUSTOMER_TABLE_SCHEMA);table()传入的名称若表已存在必须与实际 DynamoDB 表名一致若表尚不存在该名称会在随后的createTable()中被用作新表名。接口定义见 DynamoDbTable.java 与 DynamoDbEnhancedClient.java。常用基础操作与底层原语一一对应以下操作强映射到同名的 DynamoDB 原语。示例给出的是最简形态每个操作都可以通过传入增强请求对象Enhanced Request进一步定制这些请求对象提供了低层 DynamoDB 客户端的绝大部分能力完整文档见相应接口的 Javadoc// CreateTable customerTable.createTable(); // GetItem Customer customer customerTable.getItem(Key.builder().partitionValue(a123).build()); // UpdateItem Customer updatedCustomer customerTable.updateItem(customer); // PutItem customerTable.putItem(customer); // DeleteItem Customer deletedCustomer customerTable.deleteItem(Key.builder().partitionValue(a123).sortValue(456).build()); // Query PageIterableCustomer customers customerTable.query(keyEqualTo(k - k.partitionValue(a123))); // Scan PageIterableCustomer customers customerTable.scan(); // BatchGetItem BatchGetResultPageIterable batchResults enhancedClient.batchGetItem(r - r.addReadBatch(ReadBatch.builder(Customer.class) .mappedTableResource(customerTable) .addGetItem(key1) .addGetItem(key2) .addGetItem(key3) .build())); // BatchWriteItem batchResults enhancedClient.batchWriteItem(r - r.addWriteBatch(WriteBatch.builder(Customer.class) .mappedTableResource(customerTable) .addPutItem(customer) .addDeleteItem(key1) .addDeleteItem(key1) .build())); // TransactGetItems transactResults enhancedClient.transactGetItems(r - r.addGetItem(customerTable, key1) .addGetItem(customerTable, key2)); // TransactWriteItems enhancedClient.transactWriteItems(r - r.addConditionCheck(customerTable, i - i.key(orderKey) .conditionExpression(conditionExpression)) .addUpdateItem(customerTable, customer) .addDeleteItem(customerTable, key));这些操作在internal/operations包中各有对应的操作实现如 GetItemOperation.java、QueryOperation.java批量与分页结果模型位于 model 包ReadBatch、WriteBatch、PageIterable、BatchGetResultPageIterable等。在二级索引上执行 Query 与 ScanQuery 和 Scan 可以针对二级索引执行示例如下DynamoDbIndexCustomer customersByName customerTable.index(customers_by_name); SdkIterablePageCustomer customersWithName customersByName.query(r - r.queryConditional(keyEqualTo(k - k.partitionValue(Smith)))); PageIterableCustomer pages PageIterable.create(customersWithName);索引对象由 DynamoDbIndex.java 定义查询条件由internal/conditional包下的SingleKeyItemConditional、BetweenConditional、BeginsWithConditional等类型组合而成测试用例见 QueryConditionalUtilsTest.java。与不可变数据类协作Enhanced Client 可以直接映射不可变类不可变类只有 getter并配有一个独立的 Builder 类用于构造实例。注解风格与 Bean 类似核心注解为DynamoDbImmutable(builder ...)DynamoDbImmutable(builder Customer.Builder.class) public class Customer { private final String accountId; private final int subId; private final String name; private final Instant createdDate; private Customer(Builder b) { this.accountId b.accountId; this.subId b.subId; this.name b.name; this.createdDate b.createdDate; } // This method will be automatically discovered and used by the TableSchema public static Builder builder() { return new Builder(); } DynamoDbPartitionKey public String accountId() { return this.accountId; } DynamoDbSortKey public int subId() { return this.subId; } DynamoDbSecondaryPartitionKey(indexNames customers_by_name) public String name() { return this.name; } DynamoDbSecondarySortKey(indexNames {customers_by_date, customers_by_name}) public Instant createdDate() { return this.createdDate; } public static final class Builder { private String accountId; private int subId; private String name; private Instant createdDate; private Builder() {} public Builder accountId(String accountId) { this.accountId accountId; return this; } public Builder subId(int subId) { this.subId subId; return this; } public Builder name(String name) { this.name name; return this; } public Builder createdDate(Instant createdDate) { this.createdDate createdDate; return this; } // This method will be automatically discovered and used by the TableSchema public Customer build() { return new Customer(this); } } }DynamoDbImmutable类必须满足以下约定这些校验由 ImmutableIntrospector.java 在 schema 推断阶段执行不可变类上除Object重写方法、或标注了DynamoDbIgnore的方法外每个方法都必须是记录属性的 getter每个 getter 必须在 builder 类上有大小写匹配的对应 setterbuilder 类必须有公共无参默认构造函数或者不可变类上必须有一个名为builder的公共静态无参方法返回 builder 实例builder 类必须有一个名为build的公共无参方法返回不可变类实例。为不可变类创建 schema 时使用专门的静态构造方法static final TableSchemaCustomer CUSTOMER_TABLE_SCHEMA TableSchema.fromImmutableClass(Customer.class);fromImmutableClass的实现入口在 TableSchema.java#L164-L166返回 ImmutableTableSchema。如果项目使用 Lombok 等第三方库生成样板代码只要遵循上述约定即可兼容。借助 Lombok 的onMethod特性可以把基于属性的 DynamoDb 注解复制到生成的 getter 上Value Builder DynamoDbImmutable(builder Customer.CustomerBuilder.class) public static class Customer { Getter(onMethod __({DynamoDbPartitionKey})) private String accountId; Getter(onMethod __({DynamoDbSortKey})) private int subId; Getter(onMethod __({DynamoDbSecondaryPartitionKey(indexNames customers_by_name)})) private String name; Getter(onMethod __({DynamoDbSecondarySortKey(indexNames {customers_by_date, customers_by_name})})) private Instant createdDate; }非阻塞异步操作如果应用需要非阻塞调用可以使用映射器的异步实现。它与同步版本非常相似但有三个关键差异实例化映射表时使用异步版本并搭配 SDK 的异步 DynamoDb 客户端DynamoDbEnhancedAsyncClient enhancedClient DynamoDbEnhancedAsyncClient.builder() .dynamoDbClient(dynamoDbAsyncClient) .build();返回单个数据的操作返回CompletableFuture可以在等待结果期间执行其他工作CompletableFutureCustomer result mappedTable.getItem(r - r.key(customerKey)); // Perform other work here return result.join(); // now block and wait for the result分页列表操作返回SdkPublisher而非SdkIterable可以订阅一个处理器异步处理结果PagePublisherCustomer results mappedTable.query(r - r.queryConditional(keyEqualTo(k - k.partitionValue(Smith)))); results.subscribe(myCustomerResultsProcessor); // Perform other work and let the processor handle the results asynchronously异步客户端与异步表/索引接口分别见 DynamoDbEnhancedAsyncClient.java、DynamoDbAsyncTable.java分页发布模型为 PagePublisher.java。异步路径的功能测试位于functionaltests目录如 AsyncBasicCrudTest.java、AsyncBasicQueryTest.java。扩展机制beforeWrite 与 afterRead 两个钩子映射器支持插件式扩展提供两个钩子beforeWrite()在写入发生前被调用可以改写写操作afterRead()在读取发生后被调用可以改写读结果。像 UpdateItem 这类先写后读的操作会同时触发两个钩子。接口定义见 DynamoDbEnhancedClientExtension.java#L43-L57钩子的上下文与返回值模型为 WriteModification.java 和 ReadModification.java。加载顺序很重要扩展按在 enhanced client builder 中声明的顺序加载因为后一个扩展可能作用于前一个扩展变换后的值。从源码看ExtensionResolver.java#L32-L38 确认了默认扩展列表只包含两个VersionedRecordExtension与AtomicCounterExtension多个扩展会通过内部的ChainExtension元扩展按严格顺序串联resolveExtensions方法同文件 #L61-L71。默认行为可以在 client builder 上覆盖——加载任意自定义扩展或者一个都不加载。示例在默认加载的VersionedRecordExtension之后再加载一个自定义扩展verifyChecksumExtensionDynamoDbEnhancedClientExtension versionedRecordExtension VersionedRecordExtension.builder().build(); DynamoDbEnhancedClient enhancedClient DynamoDbEnhancedClient.builder() .dynamoDbClient(dynamoDbClient) .extensions(versionedRecordExtension, verifyChecksumExtension) .build();VersionedRecordExtension乐观锁该扩展默认加载会为记录维护一个版本号并在每次写入时自动递增。它会给每个写操作附加条件表达式如果数据库中记录的版本号与应用上次读取的值不一致写入即失败。若其他进程在第一进程读取记录与第一进程写回更新之间更新了该记录这次写入就会失败——这实际上为记录更新提供了乐观锁optimistic locking。告诉扩展用哪个属性记录版本号在 TableSchema 中标注一个数值属性DynamoDbVersionAttribute public Integer getVersion() {...}; public void setVersion(Integer version) {...};或者使用 StaticTableSchema 的标签.addAttribute(Integer.class, a - a.name(version) .getter(Customer::getVersion) .setter(Customer::setVersion) // Apply the version tag to the attribute .tags(versionAttribute()))从 VersionedRecordExtension.java 的实现可以看到关键细节版本号属性必须是N数值类型否则在 schema 构建期抛IllegalArgumentException#L123-L127版本计算新记录首次写入的版本号为startAt incrementBy默认startAt0、incrementBy1因此第一版为 1若希望从 0 开始编号可设startAt-1#L58-L60beforeWrite#L145 起中新记录使用attribute_not_exists(...)条件已有记录则生成版本号 上次读取值的条件表达式且当版本号恰好等于startAt时使用OR条件兼容新建/已存在两种情形还带有Long.MAX_VALUE溢出保护#L191-L195。AtomicCounterExtension原子计数器该扩展默认加载每次记录写入时自动递增数值属性起始值和步长均可指定未指定时计数器从 0 开始、每次加 1。在 TableSchema 中将一个Long属性标记为计数器标准值示例DynamoDbAtomicCounter public Long getCounter() {...}; public void setCounter(Long counter) {...};使用 StaticTableSchema 并指定自定义起始值与步长.addAttribute(Integer.class, a - a.name(counter) .getter(Customer::getCounter) .setter(Customer::setCounter) // Apply the atomicCounter tag to the attribute with start and increment values .tags(atomicCounter(10L, 5L)))AtomicCounterExtension.java 的 Javadoc 与实现beforeWrite#L113-L135确认了两个关键行为putItem 时计数器会被重置为起始值addToItem#L143-L147updateItem 时扩展会从待写记录中剥离计数器属性并通过 UpdateExpression 生成if_not_exists(计数器, 起始值) 增量的原子自增表达式手动修改被标记为原子计数器的属性不会生效filterFromItem#L149-L163。AutoGeneratedTimestampRecordExtension自动时间戳该扩展使被选中的属性在每次成功写入时自动更新为当前时间戳。要求属性类型为Instant。与上面两个扩展不同它默认不加载必须在创建 enhanced client 时作为自定义扩展显式指定。通过标注Instant属性告诉扩展要更新哪个属性DynamoDbAutoGeneratedTimestampAttribute public Instant getLastUpdate() {...} public void setLastUpdate(Instant lastUpdate) {...}StaticTableSchema 写法.addAttribute(Instant.class, a - a.name(lastUpdate) .getter(Customer::getLastUpdate) .setter(Customer::setLastUpdate) // Applying the autoGeneratedTimestamp tag to the attribute .tags(autoGeneratedTimestampAttribute()))实现见 AutoGeneratedTimestampRecordExtension.java功能测试见 AutoGeneratedTimestampExtensionTest.java。此外从 extensions/annotations 包可以看到仓库中还提供了DynamoDbAutoGeneratedUuid对应AutoGeneratedUuidExtension这一扩展注解用于自动生成 UUID 属性。高级 TableSchema 特性显式包含/排除属性排除属性用DynamoDbIgnore标记不参与映射的属性private String internalKey; DynamoDbIgnore public String getInternalKey() { return this.internalKey; } public void setInternalKey(String internalKey) { return this.internalKey internalKey;}包含并重命名属性用DynamoDbAttribute(名字)显式指定存储时使用的属性名private String internalKey; DynamoDbAttribute(renamedInternalKey) public String getInternalKey() { return this.internalKey; } public void setInternalKey(String internalKey) { return this.internalKey internalKey;}两个注解分别位于 DynamoDbIgnore.java 与 DynamoDbAttribute.java。控制属性转换Attribute Converter默认情况下TableSchema 通过 DefaultAttributeConverterProvider.java 为所有基本类型和许多常见 Java 类型提供转换器。行为既可以在转换器提供者层面整体调整也可以针对单个属性覆盖。可用转换器的完整清单可参考 AttributeConverter.java 接口的 Javadoc从 internal/converter/attribute 包可见内置实现覆盖面很广包括InstantAsStringAttributeConverter、LocalDateAttributeConverter、MapAttributeConverter、ListAttributeConverter、SetAttributeConverter、SdkBytesAttributeConverter、UriAttributeConverter、UrlAttributeConverter、UuidAttributeConverter、EnumAttributeConverter位于包根目录等。提供自定义转换器提供者可以通过DynamoDbBean的converterProviders注解提供一个或一整个有序链。自定义实现必须继承AttributeConverterProvider接口AttributeConverterProvider.java。注意提供自己的提供者链会覆盖默认提供者DefaultAttributeConverterProvider若仍想使用内置转换器必须把它显式包含在链中也可以用空数组{}关闭所有提供者链此时所有属性都必须自带转换器。单个提供者DynamoDbBean(converterProviders ConverterProvider1.class) public class Customer { }以默认提供者结尾优先级最低的提供者链DynamoDbBean(converterProviders { ConverterProvider1.class, ConverterProvider2.class, DefaultAttributeConverterProvider.class}) public class Customer { }同样可以在 StaticTableSchema 上直接挂接提供者链private static final StaticTableSchemaCustomer CUSTOMER_TABLE_SCHEMA StaticTableSchema.builder(Customer.class) .newItemSupplier(Customer::new) .addAttribute(String.class, a - a.name(name) .getter(Customer::getName) .setter(Customer::setName)) .attributeConverterProviders(converterProvider1, converterProvider2) .build();覆盖单个属性的转换在创建属性时直接提供AttributeConverter即可覆盖该属性上所有来自提供者的转换器。注意这只是给该属性加了自定义转换器同类型的其他属性除非显式指定否则不受影响。DynamoDbBean public class Customer { private String name; DynamoDbConvertedBy(CustomAttributeConverter.class) public String getName() { return this.name; } public void setName(String name) { this.name name;} }StaticTableSchema 对应写法private static final StaticTableSchemaCustomer CUSTOMER_TABLE_SCHEMA StaticTableSchema.builder(Customer.class) .newItemSupplier(Customer::new) .addAttribute(String.class, a - a.name(name) .getter(Customer::getName) .setter(Customer::setName) .attributeConverter(customAttributeConverter)) .build();对应注解为 DynamoDbConvertedBy.java。定制属性的更新行为执行 update 类操作UpdateItem 或 TransactWriteItems 内的 update时可以为单个属性定制更新行为。例如想在记录上保存创建时间但只在数据库中该属性尚无值时才写入就用UpdateBehavior.WRITE_IF_NOT_EXISTSDynamoDbBean public class Customer extends GenericRecord { private String id; private Instant createdOn; DynamoDbPartitionKey public String getId() { return this.id; } public void setId(String id) { this.id id; } DynamoDbUpdateBehavior(UpdateBehavior.WRITE_IF_NOT_EXISTS) public Instant getCreatedOn() { return this.createdOn; } public void setCreatedOn(Instant createdOn) { this.createdOn createdOn; } }等价的静态 schema 写法static final TableSchemaCustomer CUSTOMER_TABLE_SCHEMA TableSchema.builder(Customer.class) .newItemSupplier(Customer::new) .addAttribute(String.class, a - a.name(id) .getter(Customer::getId) .setter(Customer::setId) .tags(primaryPartitionKey())) .addAttribute(Instant.class, a - a.name(createdOn) .getter(Customer::getCreatedOn) .setter(Customer::setCreatedOn) .tags(updateBehavior(UpdateBehavior.WRITE_IF_NOT_EXISTS))) .build();UpdateBehavior的取值与标签工厂定义在 UpdateBehavior.java行为转换最终落到internal/update包的 UpdateExpression 生成逻辑UpdateExpressionConverter.java功能测试见 UpdateBehaviorTest.java。跨类扁平映射Flat Mapping如果表记录的属性分散在多个 Java 对象中继承或组合静态 TableSchema 提供扁平映射能力把它们合并进单一 schema。基于继承唯一要求是两个类都标注为 DynamoDb beanDynamoDbBean public class Customer extends GenericRecord { private String name; private GenericRecord record; public String getName() { return this.name; } public void setName(String name) { this.name name;} public GenericRecord getRecord() { return this.record; } public void setRecord(GenericRecord record) { this.record record;} } DynamoDbBean public abstract class GenericRecord { private String id; private String createdDate; public String getId() { return this.id; } public void setId(String id) { this.id id;} public String getCreatedDate() { return this.createdDate; } public void setCreatedDate(String createdDate) { this.createdDate createdDate;} }StaticTableSchema 使用extend特性达到同样效果Data public class Customer extends GenericRecord { private String name; } Data public abstract class GenericRecord { private String id; private String createdDate; } private static final StaticTableSchemaGenericRecord GENERIC_RECORD_SCHEMA StaticTableSchema.builder(GenericRecord.class) // The partition key will be inherited by the top level mapper .addAttribute(String.class, a - a.name(id) .getter(GenericRecord::getId) .setter(GenericRecord::setId) .tags(primaryPartitionKey())) .addAttribute(String.class, a - a.name(created_date) .getter(GenericRecord::getCreatedDate) .setter(GenericRecord::setCreatedDate)) .build(); private static final StaticTableSchemaCustomer CUSTOMER_TABLE_SCHEMA StaticTableSchema.builder(Customer.class) .newItemSupplier(Customer::new) .addAttribute(String.class, a - a.name(name) .getter(Customer::getName) .setter(Customer::setName)) .extend(GENERIC_RECORD_SCHEMA) // All the attributes of the GenericRecord schema are added to Customer .build();基于组合DynamoDbFlatten注解可以扁平化组合类与 Map 属性DynamoDbBean public class Customer { private String name; private GenericRecord record; public String getName() { return this.name; } public void setName(String name) { this.name name;} DynamoDbFlatten public GenericRecord getRecord() { return this.record; } public void setRecord(GenericRecord record) { this.record record;} } DynamoDbBean public class GenericRecord { private String id; private String createdDate; public String getId() { return this.id; } public void setId(String id) { this.id id;} public String getCreatedDate() { return this.createdDate; } public void setCreatedDate(String createdDate) { this.createdDate createdDate;} }DynamoDbFlatten也可用于把 Map 展开为顶层属性DynamoDbBean public class Customer { private String name; private String city; private String address; private MapString, String detailsMap; public String getName() { return this.name; } public void setName(String name) { this.name name;} public String getCity() { return this.city; } public void setCity(String city) { this.city city;} public String getAddress() { return this.address; } public void setAddress(String address) { this.address address;} DynamoDbFlatten public MapString, String getDetailsMap() { return this.detailsMap; } public void setDetailsMap(MapString, String detailsMap) { this.detailsMap detailsMap;} }对象扁平化约束可以扁平化任意多个符合条件的类唯一限制是属性合并后名称不能重复且整个结构中最多只能有一个分区键、一个排序键、一个表名。Map 扁平化约束一条记录含整个类层次及被组合/扁平化的类最多只能有一个作用于 Map 属性的DynamoDbFlatten被扁平化的 Map 必须使用String作为键和值类型MapString, String其他类型不受支持Map 键生成的属性名不能与记录已有属性冲突冲突会抛异常存在多个被扁平化的 Map 时schema 创建阶段会抛异常DynamoDbUpdateBehavior等其他注解不支持用于被扁平化的 Map与对象扁平化的既有行为保持一致。StaticTableSchema 扁平化组合对象时需要额外提供 getter/setter 让映射器知道如何访问该组合对象Data public class Customer{ private String name; private GenericRecord recordMetadata; //getters and setters for all attributes } Data public class GenericRecord { private String id; private String createdDate; //getters and setters for all attributes } private static final StaticTableSchemaGenericRecord GENERIC_RECORD_SCHEMA StaticTableSchema.builder(GenericRecord.class) .addAttribute(String.class, a - a.name(id) .getter(GenericRecord::getId) .setter(GenericRecord::setId) .tags(primaryPartitionKey())) .addAttribute(String.class, a - a.name(created_date) .getter(GenericRecord::getCreatedDate) .setter(GenericRecord::setCreatedDate)) .build(); private static final StaticTableSchemaCustomer CUSTOMER_TABLE_SCHEMA StaticTableSchema.builder(Customer.class) .newItemSupplier(Customer::new) .addAttribute(String.class, a - a.name(name) .getter(Customer::getName) .setter(Customer::setName)) // Because we are flattening a component object, we supply a getter and setter so the // mapper knows how to access it .flatten(GENERIC_RECORD_SCHEMA, Customer::getRecordMetadata, Customer::setRecordMetadata) .build();与注解用法一样builder 模式下也可以扁平化任意多个符合条件的类。Map 扁平化使用flattenMapData public class Customer { private String name; private String city; private String address; private MapString, String detailsMap; //getters and setters for all attributes } private static final StaticTableSchemaCustomer CUSTOMER_TABLE_SCHEMA StaticTableSchema.builder(Customer.class) .newItemSupplier(Customer::new) .addAttribute(String.class, a - a.name(name) .getter(Customer::getName) .setter(Customer::setName)) // Because we are flattening a Map object, we supply a getter and setter so the mapper knows how to access it .flattenMap(Customer::getDetailsMap, Customer::setDetailsMap) .build();从测试看行为验证该模块的功能测试基于 DynamoDB Local 运行为文档中的每项能力提供了可执行的验证依据基础 CRUD、Query、ScanBasicCrudTest.java、BasicQueryTest.java、IndexQueryTest.java乐观锁与原子计数器VersionedRecordTest.java、AtomicCounterTest.java以及扩展单测 VersionedRecordExtensionTest.java扁平化FlattenTest.java、FlattenMapTest.java扩展默认加载策略ExtensionResolverTest.java。小结DynamoDB Enhanced Client 通过DynamoDbBean/DynamoDbImmutable注解 TableSchema 映射器 Client/Table/Index 资源对象三层结构把 DynamoDB 的 AttributeValue 级 API 提升为对象化的 Java 编程模型。掌握其要点后可以做到用TableSchema.fromClass()快速映射 Bean用StaticTableSchema.builder()获得编译期确定、零反射开销的 schema用统一的customerTable/enhancedClientAPI 完成 CRUD、Query/Scan、批量与事务操作并可切换到DynamoDbEnhancedAsyncClient获得CompletableFuture/SdkPublisher的完全非阻塞体验利用beforeWrite/afterRead扩展点获得乐观锁VersionedRecordExtension、原子计数AtomicCounterExtension、自动时间戳AutoGeneratedTimestampRecordExtension等横切能力并理解扩展按声明顺序串联执行这一关键语义通过DynamoDbIgnore、DynamoDbAttribute、自定义 Converter 链、UpdateBehavior与 Flat Mapping 精细控制属性在库表中的映射形态。所有用法以 services-custom/dynamodb-enhanced/README.md 为准实现细节可在 dynamodb-enhanced 模块源码 中逐一对应查证。【免费下载链接】aws-sdk-java-v2The official AWS SDK for Java - Version 2项目地址: https://gitcode.com/GitHub_Trending/aw/aws-sdk-java-v2创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表