Skip to content

Commit ba9f027

Browse files
Configure DynamoDb TableSchema as a bean.
Fixes #674
1 parent a228a36 commit ba9f027

File tree

7 files changed

+162
-155
lines changed

7 files changed

+162
-155
lines changed

spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/dynamodb/DynamoDbAutoConfiguration.java

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import io.awspring.cloud.dynamodb.*;
2323
import java.io.IOException;
2424
import java.util.List;
25-
import java.util.Optional;
2625
import org.springframework.beans.factory.ObjectProvider;
2726
import org.springframework.boot.autoconfigure.AutoConfiguration;
2827
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
@@ -48,6 +47,7 @@
4847
* {@link EnableAutoConfiguration Auto-configuration} for DynamoDB integration.
4948
*
5049
* @author Matej Nedic
50+
* @author Maciej Walkowiak
5151
* @since 3.0.0
5252
*/
5353
@AutoConfiguration
@@ -116,27 +116,24 @@ public DynamoDbEnhancedClient dynamoDbEnhancedClient(DynamoDbClient dynamoDbClie
116116
return DynamoDbEnhancedClient.builder().dynamoDbClient(dynamoDbClient).build();
117117
}
118118

119-
@ConditionalOnMissingBean(DynamoDbOperations.class)
119+
@ConditionalOnMissingBean(DynamoDbTableNameResolver.class)
120120
@Bean
121-
public DynamoDbTemplate dynamoDBTemplate(DynamoDbEnhancedClient dynamoDbEnhancedClient,
122-
Optional<DynamoDbTableSchemaResolver> tableSchemaResolver,
123-
Optional<DynamoDbTableNameResolver> tableNameResolver, List<TableSchema<?>> tableSchemas) {
124-
DynamoDbTableNameResolver tableNameRes = tableNameResolver.orElseGet(DefaultDynamoDbTableNameResolver::new);
125-
126-
DynamoDbTableSchemaResolver tableSchemaRes = tableSchemaResolver
127-
.orElseGet(DefaultDynamoDbTableSchemaResolver::new);
128-
129-
registerTableSchemas(tableSchemas, tableNameRes, tableSchemaRes);
121+
public DefaultDynamoDbTableResolver dynamoDbTableSchemaResolver(
122+
DefaultDynamoDbTableNameResolver dynamoDbTableNameResolver, List<TableSchema<?>> tableSchemas) {
123+
return new DefaultDynamoDbTableResolver(dynamoDbTableNameResolver, tableSchemas);
124+
}
130125

131-
return new DynamoDbTemplate(dynamoDbEnhancedClient, tableSchemaRes, tableNameRes);
126+
@ConditionalOnMissingBean(DynamoDbTableNameResolver.class)
127+
@Bean
128+
public DefaultDynamoDbTableNameResolver dynamoDbTableNameResolver() {
129+
return new DefaultDynamoDbTableNameResolver();
132130
}
133131

134-
private void registerTableSchemas(List<TableSchema<?>> tableSchemas, DynamoDbTableNameResolver tableNameRes,
135-
DynamoDbTableSchemaResolver tableSchemaRes) {
136-
tableSchemas.forEach(schema -> {
137-
var tableName = tableNameRes.resolve(schema.itemType().rawClass());
138-
tableSchemaRes.register(schema, tableName);
139-
});
132+
@ConditionalOnMissingBean(DynamoDbOperations.class)
133+
@Bean
134+
public DynamoDbTemplate dynamoDBTemplate(DynamoDbEnhancedClient dynamoDbEnhancedClient,
135+
DynamoDbTableResolver tableSchemaResolver) {
136+
return new DynamoDbTemplate(dynamoDbEnhancedClient, tableSchemaResolver);
140137
}
141138

142139
static class MissingDaxUrlCondition extends NoneNestedConditions {

spring-cloud-aws-autoconfigure/src/test/java/io/awspring/cloud/autoconfigure/dynamodb/DynamoDbAutoConfigurationTest.java

Lines changed: 30 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,15 @@
1616
package io.awspring.cloud.autoconfigure.dynamodb;
1717

1818
import static org.assertj.core.api.Assertions.assertThat;
19-
import static org.mockito.Mockito.spy;
20-
import static org.mockito.Mockito.verify;
2119

2220
import io.awspring.cloud.autoconfigure.ConfiguredAwsClient;
2321
import io.awspring.cloud.autoconfigure.core.AwsAutoConfiguration;
2422
import io.awspring.cloud.autoconfigure.core.AwsClientCustomizer;
2523
import io.awspring.cloud.autoconfigure.core.CredentialsProviderAutoConfiguration;
2624
import io.awspring.cloud.autoconfigure.core.RegionProviderAutoConfiguration;
25+
import io.awspring.cloud.dynamodb.DefaultDynamoDbTableResolver;
2726
import io.awspring.cloud.dynamodb.DynamoDbTableNameResolver;
28-
import io.awspring.cloud.dynamodb.DynamoDbTableSchemaResolver;
27+
import io.awspring.cloud.dynamodb.DynamoDbTableResolver;
2928
import io.awspring.cloud.dynamodb.DynamoDbTemplate;
3029
import java.net.URI;
3130
import java.time.Duration;
@@ -51,6 +50,7 @@
5150
* Tests for {@link DynamoDbAutoConfiguration}.
5251
*
5352
* @author Matej Nedic
53+
* @author Maciej Walkowiak
5454
*/
5555
class DynamoDbAutoConfigurationTest {
5656

@@ -71,15 +71,14 @@ void dynamoDBAutoConfigurationIsDisabled() {
7171
@Test
7272
void customTableResolverResolverCanBeConfigured() {
7373
contextRunner.withUserConfiguration(CustomDynamoDbConfiguration.class).run(context -> {
74-
DynamoDbTableSchemaResolver dynamoDbTableSchemaResolver = context
75-
.getBean(DynamoDbTableSchemaResolver.class);
74+
DynamoDbTableResolver dynamoDbTableResolver = context.getBean(DynamoDbTableResolver.class);
7675
DynamoDbTableNameResolver dynamoDBDynamoDbTableNameResolver = context
7776
.getBean(DynamoDbTableNameResolver.class);
7877

79-
assertThat(dynamoDbTableSchemaResolver).isNotNull();
78+
assertThat(dynamoDbTableResolver).isNotNull();
8079
assertThat(dynamoDBDynamoDbTableNameResolver).isNotNull();
8180

82-
assertThat(dynamoDbTableSchemaResolver).isInstanceOf(CustomDynamoDBDynamoDbTableSchemaResolver.class);
81+
assertThat(dynamoDbTableResolver).isInstanceOf(CustomDynamoDBDynamoDbTableResolver.class);
8382
assertThat(dynamoDBDynamoDbTableNameResolver)
8483
.isInstanceOf(CustomDynamoDBDynamoDbTableNameResolver.class);
8584

@@ -130,29 +129,16 @@ void customDynamoDbClientConfigurer() {
130129
}
131130

132131
@Test
133-
void tableSchemaBeansGetRegistered() {
134-
135-
contextRunner.withUserConfiguration(TableSchemaConfiguration.class).run(context -> {
136-
var schemaResolver = context.getBean(DynamoDbTableSchemaResolver.class);
137-
var nameResolver = context.getBean(DynamoDbTableNameResolver.class);
138-
139-
assertThat(schemaResolver).isNotNull();
140-
assertThat(nameResolver).isNotNull();
141-
142-
assertTableSchemaRegistered(context.getBean("firstTableSchema", TableSchema.class), nameResolver,
143-
schemaResolver);
144-
assertTableSchemaRegistered(context.getBean("secondTableSchema", TableSchema.class), nameResolver,
145-
schemaResolver);
146-
147-
});
148-
}
149-
150-
private static void assertTableSchemaRegistered(TableSchema<?> tableSchema,
151-
DynamoDbTableNameResolver nameResolver, DynamoDbTableSchemaResolver schemaResolver) {
152-
var tableName = verify(nameResolver).resolve(tableSchema.itemType().rawClass());
153-
verify(schemaResolver).register(tableSchema, tableName);
132+
void tableSchemaBeansRegistered() {
133+
contextRunner.withUserConfiguration(DynamoDbAutoConfigurationTest.TableSchemaConfiguration.class)
134+
.run(context -> {
135+
DefaultDynamoDbTableResolver schemaResolver = context
136+
.getBean(DefaultDynamoDbTableResolver.class);
137+
TableSchema<TableSchemaConfiguration.Person> personTableSchema = schemaResolver
138+
.resolveTableSchema(TableSchemaConfiguration.Person.class);
139+
assertThat(context.getBean("personTableSchema")).isEqualTo(personTableSchema);
140+
});
154141
}
155-
156142
}
157143

158144
@Nested
@@ -252,16 +238,14 @@ void customTableResolverResolverCanBeConfigured() {
252238
.withPropertyValues(
253239
"spring.cloud.aws.dynamodb.dax.url:dax://something.dax-clusters.us-east-1.amazonaws.com")
254240
.withUserConfiguration(CustomDynamoDbConfiguration.class).run(context -> {
255-
DynamoDbTableSchemaResolver dynamoDbTableSchemaResolver = context
256-
.getBean(DynamoDbTableSchemaResolver.class);
241+
DynamoDbTableResolver dynamoDbTableResolver = context.getBean(DynamoDbTableResolver.class);
257242
DynamoDbTableNameResolver dynamoDBDynamoDbTableNameResolver = context
258243
.getBean(DynamoDbTableNameResolver.class);
259244

260-
assertThat(dynamoDbTableSchemaResolver).isNotNull();
245+
assertThat(dynamoDbTableResolver).isNotNull();
261246
assertThat(dynamoDBDynamoDbTableNameResolver).isNotNull();
262247

263-
assertThat(dynamoDbTableSchemaResolver)
264-
.isInstanceOf(CustomDynamoDBDynamoDbTableSchemaResolver.class);
248+
assertThat(dynamoDbTableResolver).isInstanceOf(CustomDynamoDBDynamoDbTableResolver.class);
265249
assertThat(dynamoDBDynamoDbTableNameResolver)
266250
.isInstanceOf(CustomDynamoDBDynamoDbTableNameResolver.class);
267251

@@ -274,8 +258,8 @@ void customTableResolverResolverCanBeConfigured() {
274258
static class CustomDynamoDbConfiguration {
275259

276260
@Bean
277-
DynamoDbTableSchemaResolver tableSchemaResolver() {
278-
return new CustomDynamoDBDynamoDbTableSchemaResolver();
261+
DynamoDbTableResolver tableSchemaResolver() {
262+
return new CustomDynamoDBDynamoDbTableResolver();
279263
}
280264

281265
@Bean
@@ -285,10 +269,15 @@ DynamoDbTableNameResolver tableNameResolver() {
285269

286270
}
287271

288-
static class CustomDynamoDBDynamoDbTableSchemaResolver implements DynamoDbTableSchemaResolver {
272+
static class CustomDynamoDBDynamoDbTableResolver implements DynamoDbTableResolver {
289273

290274
@Override
291-
public <T> TableSchema resolve(Class<T> clazz, String tableName) {
275+
public <T> TableSchema resolveTableSchema(Class<T> clazz) {
276+
return null;
277+
}
278+
279+
@Override
280+
public String resolveTableName(Class<?> aClass) {
292281
return null;
293282
}
294283

@@ -330,25 +319,12 @@ public SdkHttpClient httpClient() {
330319
static class TableSchemaConfiguration {
331320

332321
@Bean
333-
TableSchema<Object> firstTableSchema() {
334-
return StaticTableSchema.builder(Object.class).build();
335-
}
336-
337-
@Bean
338-
TableSchema<String> secondTableSchema() {
339-
return StaticTableSchema.builder(String.class).build();
322+
TableSchema<Person> personTableSchema() {
323+
return StaticTableSchema.builder(Person.class).build();
340324
}
341325

342-
@Bean
343-
DynamoDbTableSchemaResolver schemaResolver() {
344-
return spy(CustomDynamoDBDynamoDbTableSchemaResolver.class);
326+
static class Person {
345327
}
346-
347-
@Bean
348-
DynamoDbTableNameResolver tableNameResolver() {
349-
return spy(CustomDynamoDBDynamoDbTableNameResolver.class);
350-
}
351-
352328
}
353329

354330
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright 2013-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.awspring.cloud.dynamodb;
17+
18+
import java.util.Collections;
19+
import java.util.List;
20+
import java.util.Map;
21+
import java.util.concurrent.ConcurrentHashMap;
22+
import software.amazon.awssdk.enhanced.dynamodb.TableSchema;
23+
24+
/**
25+
* Default implementation with simple cache for {@link TableSchema}.
26+
*
27+
* @author Matej Nedic
28+
* @author Maciej Walkowiak
29+
*/
30+
public class DefaultDynamoDbTableResolver implements DynamoDbTableResolver {
31+
private final DynamoDbTableNameResolver tableNameResolver;
32+
private final List<TableSchema<?>> tableSchemas;
33+
private final Map<String, TableSchema> tableSchemaCache = new ConcurrentHashMap<>();
34+
35+
DefaultDynamoDbTableResolver() {
36+
this(new DefaultDynamoDbTableNameResolver(), Collections.emptyList());
37+
}
38+
39+
public DefaultDynamoDbTableResolver(DynamoDbTableNameResolver tableNameResolver,
40+
List<TableSchema<?>> tableSchemas) {
41+
this.tableNameResolver = tableNameResolver;
42+
this.tableSchemas = tableSchemas;
43+
}
44+
45+
public <T> TableSchema<T> resolveTableSchema(Class<T> clazz) {
46+
String tableName = tableNameResolver.resolve(clazz);
47+
return tableSchemaCache.computeIfAbsent(tableName,
48+
entityClassName -> tableSchemas.stream().filter(it -> it.itemType().rawClass().equals(clazz))
49+
.findFirst().orElseGet(() -> TableSchema.fromBean(clazz)));
50+
}
51+
52+
@Override
53+
public String resolveTableName(Class<?> aClass) {
54+
return tableNameResolver.resolve(aClass);
55+
}
56+
57+
Map<String, TableSchema> getTableSchemaCache() {
58+
return tableSchemaCache;
59+
}
60+
}

spring-cloud-aws-dynamodb/src/main/java/io/awspring/cloud/dynamodb/DefaultDynamoDbTableSchemaResolver.java

Lines changed: 0 additions & 39 deletions
This file was deleted.
Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,31 +18,28 @@
1818
import software.amazon.awssdk.enhanced.dynamodb.TableSchema;
1919

2020
/**
21-
* Resolving Class and TableName to {@link TableSchema} class. Should be cached since creating {@link TableSchema} is
22-
* expensive.
21+
* Resolving table schema and table name from a class.
2322
*
2423
* @author Matej Nedic
24+
* @author Maciej Walkowiak
2525
* @since 3.0
2626
*/
27-
public interface DynamoDbTableSchemaResolver {
27+
public interface DynamoDbTableResolver {
2828

2929
/**
30-
* Resolving Class and TableName to {@link TableSchema} class.
30+
* Resolving {@link TableSchema} from {@link Class}.
3131
*
3232
* @param clazz - the class from which table schema is resolved
33-
* @param tableName - the table name
3433
* @return table schema
3534
* @param <T> - type
3635
*/
37-
<T> TableSchema resolve(Class<T> clazz, String tableName);
36+
<T> TableSchema<T> resolveTableSchema(Class<T> clazz);
3837

3938
/**
40-
* Register manually a {@link TableSchema}
39+
* Resolves table name from {@link Class}.
4140
*
42-
* @param tableSchema - the schema to be registered
43-
* @param tableName - the table name
41+
* @param clazz - a class
42+
* @return a table name
4443
*/
45-
default <T> void register(TableSchema<T> tableSchema, String tableName) {
46-
}
47-
44+
String resolveTableName(Class<?> clazz);
4845
}

0 commit comments

Comments
 (0)