Skip to content

Commit 1ce6446

Browse files
committed
DDB Enhanced: updates README with bean examples
1 parent 3442bfa commit 1ce6446

File tree

1 file changed

+195
-25
lines changed

1 file changed

+195
-25
lines changed

services-custom/dynamodb-enhanced/README.md

Lines changed: 195 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,6 @@ index. Here's an example of how to do this:
159159
PageIterable<Customer> customersWithName =
160160
customersByName.query(r -> r.queryConditional(equalTo(k -> k.partitionValue("Smith"))));
161161
```
162-
163162
### Non-blocking asynchronous operations
164163
If your application requires non-blocking asynchronous calls to
165164
DynamoDb, then you can use the asynchronous implementation of the
@@ -195,8 +194,7 @@ key differences:
195194
// Perform other work and let the processor handle the results asynchronously
196195
```
197196

198-
199-
### Using extensions
197+
## Using extensions
200198
The mapper supports plugin extensions to provide enhanced functionality
201199
beyond the simple primitive mapped operations. Extensions have two hooks, beforeWrite() and
202200
afterRead(); the former can modify a write operation before it happens,
@@ -221,7 +219,7 @@ DynamoDbEnhancedClient enhancedClient =
221219
.build();
222220
```
223221

224-
#### VersionedRecordExtension
222+
### VersionedRecordExtension
225223

226224
This extension is loaded by default and will increment and track a record version number as
227225
records are written to the database. A condition will be added to every
@@ -248,14 +246,143 @@ Or using a StaticTableSchema:
248246
.tags(versionAttribute())
249247
```
250248

251-
## Advanced StaticTableSchema scenarios
249+
## Advanced table schema features
250+
### Explicitly include/exclude attributes in DDB mapping
251+
#### Excluding attributes
252+
Ignore attributes that should not participate in mapping to DDB
253+
Mark the attribute with the @DynamoDbIgnore annotation:
254+
```java
255+
private String internalKey;
256+
257+
@DynamoDbIgnore
258+
public String getInternalKey() { return this.internalKey; }
259+
public void setInternalKey(String internalKey) { return this.internalKey = internalKey;}
260+
```
261+
#### Including attributes
262+
Change the name used to store an attribute in DBB by explicitly marking it with the
263+
@DynamoDbAttribute annotation and supplying a different name:
264+
```java
265+
private String internalKey;
266+
267+
@DynamoDbAttribute("renamedInternalKey")
268+
public String getInternalKey() { return this.internalKey; }
269+
public void setInternalKey(String internalKey) { return this.internalKey = internalKey;}
270+
```
271+
272+
### Control attribute conversion
273+
By default, the table schema provides converters for all primitive and many common Java types
274+
through a default implementation of the AttributeConverterProvider interface. This behavior
275+
can be changed both at the attribute converter provider level as well as for a single attribute.
276+
277+
#### Provide custom attribute converter providers
278+
You can provide a single AttributeConverterProvider or a chain of ordered AttributeConverterProviders
279+
through the @DynamoDbBean 'converterProviders' annotation. Any custom AttributeConverterProvider must extend the AttributeConverterProvider
280+
interface.
281+
282+
Note that if you supply your own chain of attribute converter providers, you will override
283+
the default converter provider (DefaultAttributeConverterProvider) and must therefore include it in the chain if you wish to
284+
use its attribute converters. It's also possible to annotate the bean with an empty array `{}`, thus
285+
disabling the usage of any attribute converter providers including the default, in which case
286+
all attributes must have their own attribute converters (see below).
287+
288+
Single converter provider:
289+
```java
290+
@DynamoDbBean(converterProviders = ConverterProvider1.class)
291+
public class Customer {
292+
293+
}
294+
```
295+
296+
Chain of converter providers ending with the default (least priority):
297+
```java
298+
@DynamoDbBean(converterProviders = {
299+
ConverterProvider1.class,
300+
ConverterProvider2.class,
301+
DefaultAttributeConverterProvider.class})
302+
public class Customer {
303+
304+
}
305+
```
306+
307+
In the same way, adding a chain of attribute converter providers directly to a StaticTableSchema:
308+
```java
309+
private static final StaticTableSchema<Customer> CUSTOMER_TABLE_SCHEMA =
310+
StaticTableSchema.builder(Customer.class)
311+
.newItemSupplier(Customer::new)
312+
.addAttribute(String.class, a -> a.name("name")
313+
a.getter(Customer::getName)
314+
a.setter(Customer::setName))
315+
.attributeConverterProviders(converterProvider1, converterProvider2)
316+
.build();
317+
```
318+
319+
#### Override the mapping of a single attribute
320+
Supply an AttributeConverter when creating the attribute to directly override any
321+
converters provided by the table schema AttributeConverterProviders. Note that you will
322+
only add a custom converter for that attribute; other attributes, even of the same
323+
type, will not use that converter unless explicitly specified for those other attributes.
324+
325+
Example:
326+
```java
327+
@DynamoDbBean
328+
public class Customer {
329+
private String name;
330+
331+
@DynamoDbConvertedBy(CustomAttributeConverter.class)
332+
public String getName() { return this.name; }
333+
public void setName(String name) { this.name = name;}
334+
}
335+
```
336+
For StaticTableSchema:
337+
```java
338+
private static final StaticTableSchema<Customer> CUSTOMER_TABLE_SCHEMA =
339+
StaticTableSchema.builder(Customer.class)
340+
.newItemSupplier(Customer::new)
341+
.addAttribute(String.class, a -> a.name("name")
342+
a.getter(Customer::getName)
343+
a.setter(Customer::setName)
344+
a.attributeConverter(customAttributeConverter))
345+
.build();
346+
```
347+
252348
### Flat map attributes from another class
253349
If the attributes for your table record are spread across several
254350
different Java objects, either through inheritance or composition, the
255351
static TableSchema implementation gives you a method of flat mapping
256352
those attributes and rolling them up into a single schema.
257353
258-
To accomplish this using inheritance:-
354+
#### Using inheritance
355+
To accomplish flat map using inheritance, the only requirement is that
356+
both classes are annotated as a DynamoDb bean:
357+
358+
```java
359+
@DynamoDbBean
360+
public class Customer extends GenericRecord {
361+
private String name;
362+
private GenericRecord record;
363+
364+
public String getName() { return this.name; }
365+
public void setName(String name) { this.name = name;}
366+
367+
public String getRecord() { return this.record; }
368+
public void setRecord(String record) { this.record = record;}
369+
}
370+
371+
@DynamoDbBean
372+
public abstract class GenericRecord {
373+
private String id;
374+
private String createdDate;
375+
376+
public String getId() { return this.id; }
377+
public void setId(String id) { this.id = id;}
378+
379+
public String getCreatedDate() { return this.createdDate; }
380+
public void setCreatedDate(String createdDate) { this.createdDate = createdDate;}
381+
}
382+
383+
```
384+
385+
For StaticTableSchema, use the 'extend' feature to achieve the same effect:
259386
```java
260387
@Data
261388
public class Customer extends GenericRecord {
@@ -270,53 +397,96 @@ public abstract class GenericRecord {
270397
271398
private static final StaticTableSchema<GenericRecord> GENERIC_RECORD_SCHEMA =
272399
StaticTableSchema.builder(GenericRecord.class)
273-
.attributes(
274-
// The partition key will be inherited by the top level mapper
275-
stringAttribute("id", GenericRecord::getId, GenericRecord::setId).as(primaryPartitionKey()),
276-
stringAttribute("created_date", GenericRecord::getCreatedDate, GenericRecord::setCreatedDate))
277-
.build();
400+
// The partition key will be inherited by the top level mapper
401+
.addAttribute(String.class, a -> a.name("id")
402+
.getter(GenericRecord::getId)
403+
.setter(GenericRecord::setId)
404+
.tags(primaryPartitionKey()))
405+
.addAttribute(String.class, a -> a.name("created_date")
406+
.getter(GenericRecord::getCreatedDate)
407+
.setter(GenericRecord::setCreatedDate))
408+
.build();
278409
279410
private static final StaticTableSchema<Customer> CUSTOMER_TABLE_SCHEMA =
280411
StaticTableSchema.builder(Customer.class)
281412
.newItemSupplier(Customer::new)
282-
.attributes(
283-
stringAttribute("name", Customer::getName, Customer::setName))
413+
.addAttribute(String.class, a -> a.name("name")
414+
.getter(Customer::getName)
415+
.setter(Customer::setName))
284416
.extend(GENERIC_RECORD_SCHEMA) // All the attributes of the GenericRecord schema are added to Customer
285417
.build();
286418
```
419+
#### Using composition
420+
421+
Using composition, the @DynamoDbFlatten annotation flat maps the composite class:
422+
```java
423+
@DynamoDbBean
424+
public class Customer {
425+
private String name;
426+
private GenericRecord record;
427+
428+
public String getName() { return this.name; }
429+
public void setName(String name) { this.name = name;}
430+
431+
@DynamoDbFlatten(dynamoDbBeanClass = GenericRecord.class)
432+
public String getRecord() { return this.record; }
433+
public void setRecord(String record) { this.record = record;}
434+
}
435+
436+
@DynamoDbBean
437+
public class GenericRecord {
438+
private String id;
439+
private String createdDate;
440+
441+
public String getId() { return this.id; }
442+
public void setId(String id) { this.id = id;}
443+
444+
public String getCreatedDate() { return this.createdDate; }
445+
public void setCreatedDate(String createdDate) { this.createdDate = createdDate;}
446+
}
447+
```
448+
You can flatten as many different eligible classes as you like using the flatten annotation.
449+
The only constraints are that attributes must not have the same name when they are being rolled
450+
together, and there must never be more than one partition key, sort key or table name.
451+
452+
Flat map composite classes using StaticTableSchema:
287453
288-
Using composition:
289454
```java
290455
@Data
291456
public class Customer{
292457
private String name;
293458
private GenericRecord recordMetadata;
459+
//getters and setters for all attributes
294460
}
295461
296462
@Data
297463
public class GenericRecord {
298464
private String id;
299465
private String createdDate;
466+
//getters and setters for all attributes
300467
}
301468
302469
private static final StaticTableSchema<GenericRecord> GENERIC_RECORD_SCHEMA =
303470
StaticTableSchema.builder(GenericRecord.class)
304-
.newItemSupplier(GenericRecord::new)
305-
.attributes(
306-
stringAttribute("id", GenericRecord::getId, GenericRecord::setId).as(primaryPartitionKey()),
307-
stringAttribute("created_date", GenericRecord::getCreatedDate, GenericRecord::setCreatedDate))
308-
.build();
471+
.addAttribute(String.class, a -> a.name("id")
472+
.getter(GenericRecord::getId)
473+
.setter(GenericRecord::setId)
474+
.tags(primaryPartitionKey()))
475+
.addAttribute(String.class, a -> a.name("created_date")
476+
.getter(GenericRecord::getCreatedDate)
477+
.setter(GenericRecord::setCreatedDate))
478+
.build();
309479
310480
private static final StaticTableSchema<Customer> CUSTOMER_TABLE_SCHEMA =
311481
StaticTableSchema.builder(Customer.class)
312482
.newItemSupplier(Customer::new)
313-
.attributes(stringAttribute("name", Customer::getName, Customer::setName))
483+
.addAttribute(String.class, a -> a.name("name")
484+
.getter(Customer::getName)
485+
.setter(Customer::setName))
314486
// Because we are flattening a component object, we supply a getter and setter so the
315487
// mapper knows how to access it
316-
.flatten(CUSTOMER_TABLE_SCHEMA, Customer::getRecordMetadata, Customer::setRecordMetadata)
488+
.flatten(GENERIC_RECORD_SCHEMA, Customer::getRecordMetadata, Customer::setRecordMetadata)
317489
.build();
318490
```
319-
You can flatten as many different eligible classes as you like using the
320-
builder pattern. The only constraints are that attributes must not have
321-
the same name when they are being rolled together, and there must never
322-
be more than one partition key, sort key or table name.
491+
Just as for annotations, you can flatten as many different eligible classes as you like using the
492+
builder pattern.

0 commit comments

Comments
 (0)