1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.socialsignin.spring.data.dynamodb.core;
17
18 import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
19 import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper;
20 import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper.FailedBatch;
21 import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperConfig;
22 import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperTableModel;
23 import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBQueryExpression;
24 import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBScanExpression;
25 import com.amazonaws.services.dynamodbv2.datamodeling.KeyPair;
26 import com.amazonaws.services.dynamodbv2.datamodeling.PaginatedQueryList;
27 import com.amazonaws.services.dynamodbv2.datamodeling.PaginatedScanList;
28 import com.amazonaws.services.dynamodbv2.model.QueryRequest;
29 import com.amazonaws.services.dynamodbv2.model.QueryResult;
30 import com.amazonaws.services.dynamodbv2.model.Select;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
33 import org.socialsignin.spring.data.dynamodb.mapping.event.AfterDeleteEvent;
34 import org.socialsignin.spring.data.dynamodb.mapping.event.AfterLoadEvent;
35 import org.socialsignin.spring.data.dynamodb.mapping.event.AfterQueryEvent;
36 import org.socialsignin.spring.data.dynamodb.mapping.event.AfterSaveEvent;
37 import org.socialsignin.spring.data.dynamodb.mapping.event.AfterScanEvent;
38 import org.socialsignin.spring.data.dynamodb.mapping.event.BeforeDeleteEvent;
39 import org.socialsignin.spring.data.dynamodb.mapping.event.BeforeSaveEvent;
40 import org.socialsignin.spring.data.dynamodb.mapping.event.DynamoDBMappingEvent;
41 import org.springframework.beans.BeansException;
42 import org.springframework.context.ApplicationContext;
43 import org.springframework.context.ApplicationContextAware;
44 import org.springframework.context.ApplicationEventPublisher;
45 import org.springframework.lang.Nullable;
46 import org.springframework.util.Assert;
47
48 import java.util.List;
49 import java.util.Map;
50 import java.util.function.Function;
51 import java.util.stream.Collectors;
52
53 public class DynamoDBTemplate implements DynamoDBOperations, ApplicationContextAware {
54 private static final Logger LOGGER = LoggerFactory.getLogger(DynamoDBTemplate.class);
55 private final DynamoDBMapper dynamoDBMapper;
56 private final AmazonDynamoDB amazonDynamoDB;
57 private final DynamoDBMapperConfig dynamoDBMapperConfig;
58 private ApplicationEventPublisher eventPublisher;
59
60
61
62
63
64
65
66
67
68
69 public DynamoDBTemplate(AmazonDynamoDB amazonDynamoDB, DynamoDBMapperConfig dynamoDBMapperConfig) {
70 this(amazonDynamoDB, dynamoDBMapperConfig, null);
71 }
72
73
74
75
76
77
78
79
80
81 public DynamoDBTemplate(AmazonDynamoDB amazonDynamoDB, DynamoDBMapper dynamoDBMapper) {
82 this(amazonDynamoDB, null, dynamoDBMapper);
83 }
84
85
86
87
88
89
90
91
92 public DynamoDBTemplate(AmazonDynamoDB amazonDynamoDB) {
93 this(amazonDynamoDB, null, null);
94 }
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110 public DynamoDBTemplate(AmazonDynamoDB amazonDynamoDB, DynamoDBMapperConfig dynamoDBMapperConfig,
111 DynamoDBMapper dynamoDBMapper) {
112 Assert.notNull(amazonDynamoDB, "amazonDynamoDB must not be null!");
113 this.amazonDynamoDB = amazonDynamoDB;
114
115 if (dynamoDBMapperConfig == null) {
116 this.dynamoDBMapperConfig = DynamoDBMapperConfig.DEFAULT;
117 } else {
118
119
120
121
122
123
124
125
126
127 DynamoDBMapperConfig.Builder emptyBuilder = DynamoDBMapperConfig.builder();
128
129 if (dynamoDBMapperConfig.getConversionSchema() == null) {
130 LOGGER.warn(
131 "No ConversionSchema set in the provided dynamoDBMapperConfig! Merging with DynamoDBMapperConfig.DEFAULT - Please see https://git.io/DynamoDBMapperConfig");
132
133 emptyBuilder.withConversionSchema(DynamoDBMapperConfig.DEFAULT.getConversionSchema());
134 }
135
136 if (dynamoDBMapperConfig.getTypeConverterFactory() == null) {
137 LOGGER.warn(
138 "No TypeConverterFactory set in the provided dynamoDBMapperConfig! Merging with DynamoDBMapperConfig.DEFAULT - Please see https://git.io/DynamoDBMapperConfig");
139
140 emptyBuilder.withTypeConverterFactory(DynamoDBMapperConfig.DEFAULT.getTypeConverterFactory());
141 }
142
143
144 this.dynamoDBMapperConfig = new DynamoDBMapperConfig(dynamoDBMapperConfig, emptyBuilder.build());
145 }
146
147 if (dynamoDBMapper == null) {
148 this.dynamoDBMapper = new DynamoDBMapper(amazonDynamoDB, dynamoDBMapperConfig);
149 } else {
150 this.dynamoDBMapper = dynamoDBMapper;
151 }
152 }
153
154 @Override
155 public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
156 this.eventPublisher = applicationContext;
157 }
158
159 @Override
160 public <T> int count(Class<T> domainClass, DynamoDBQueryExpression<T> queryExpression) {
161 return dynamoDBMapper.count(domainClass, queryExpression);
162 }
163
164 @Override
165 public <T> PaginatedQueryList<T> query(Class<T> domainClass, DynamoDBQueryExpression<T> queryExpression) {
166 PaginatedQueryList<T> results = dynamoDBMapper.query(domainClass, queryExpression);
167 maybeEmitEvent(results, AfterQueryEvent::new);
168 return results;
169 }
170
171 @Override
172 public <T> int count(Class<T> domainClass, DynamoDBScanExpression scanExpression) {
173 return dynamoDBMapper.count(domainClass, scanExpression);
174 }
175
176 @Override
177 public <T> T load(Class<T> domainClass, Object hashKey, Object rangeKey) {
178 T entity = dynamoDBMapper.load(domainClass, hashKey, rangeKey);
179 maybeEmitEvent(entity, AfterLoadEvent::new);
180
181 return entity;
182 }
183
184 @Override
185 public <T> T load(Class<T> domainClass, Object hashKey) {
186 T entity = dynamoDBMapper.load(domainClass, hashKey);
187 maybeEmitEvent(entity, AfterLoadEvent::new);
188
189 return entity;
190 }
191
192 @Override
193 public <T> PaginatedScanList<T> scan(Class<T> domainClass, DynamoDBScanExpression scanExpression) {
194 PaginatedScanList<T> results = dynamoDBMapper.scan(domainClass, scanExpression);
195 maybeEmitEvent(results, AfterScanEvent::new);
196 return results;
197 }
198
199 @SuppressWarnings("unchecked")
200 @Override
201 public <T> List<T> batchLoad(Map<Class<?>, List<KeyPair>> itemsToGet) {
202 return dynamoDBMapper.batchLoad(itemsToGet).values().stream().flatMap(v -> v.stream()).map(e -> (T) e)
203 .map(entity -> {
204 maybeEmitEvent(entity, AfterLoadEvent::new);
205 return entity;
206 }).collect(Collectors.toList());
207 }
208
209 @Override
210 public <T> T save(T entity) {
211 maybeEmitEvent(entity, BeforeSaveEvent::new);
212 dynamoDBMapper.save(entity);
213 maybeEmitEvent(entity, AfterSaveEvent::new);
214 return entity;
215
216 }
217
218 @Override
219 public List<FailedBatch> batchSave(Iterable<?> entities) {
220 entities.forEach(it -> maybeEmitEvent(it, BeforeSaveEvent::new));
221
222 List<FailedBatch> result = dynamoDBMapper.batchSave(entities);
223
224 entities.forEach(it -> maybeEmitEvent(it, AfterSaveEvent::new));
225 return result;
226 }
227
228 @Override
229 public <T> T delete(T entity) {
230 maybeEmitEvent(entity, BeforeDeleteEvent::new);
231 dynamoDBMapper.delete(entity);
232 maybeEmitEvent(entity, AfterDeleteEvent::new);
233 return entity;
234 }
235
236 @Override
237 public List<FailedBatch> batchDelete(Iterable<?> entities) {
238 entities.forEach(it -> maybeEmitEvent(it, BeforeDeleteEvent::new));
239
240 List<FailedBatch> result = dynamoDBMapper.batchDelete(entities);
241
242 entities.forEach(it -> maybeEmitEvent(it, AfterDeleteEvent::new));
243 return result;
244 }
245
246 @Override
247 public <T> PaginatedQueryList<T> query(Class<T> clazz, QueryRequest queryRequest) {
248 QueryResult queryResult = amazonDynamoDB.query(queryRequest);
249 return new PaginatedQueryList<T>(dynamoDBMapper, clazz, amazonDynamoDB, queryRequest, queryResult,
250 dynamoDBMapperConfig.getPaginationLoadingStrategy(), dynamoDBMapperConfig);
251 }
252
253 @Override
254 public <T> int count(Class<T> clazz, QueryRequest mutableQueryRequest) {
255 mutableQueryRequest.setSelect(Select.COUNT);
256
257
258 int count = 0;
259 QueryResult queryResult = null;
260 do {
261 queryResult = amazonDynamoDB.query(mutableQueryRequest);
262 count += queryResult.getCount();
263 mutableQueryRequest.setExclusiveStartKey(queryResult.getLastEvaluatedKey());
264 } while (queryResult.getLastEvaluatedKey() != null);
265
266 return count;
267 }
268
269 @Override
270 public <T> String getOverriddenTableName(Class<T> domainClass, String tableName) {
271 if (dynamoDBMapperConfig.getTableNameOverride() != null) {
272 if (dynamoDBMapperConfig.getTableNameOverride().getTableName() != null) {
273 tableName = dynamoDBMapperConfig.getTableNameOverride().getTableName();
274 } else {
275 tableName = dynamoDBMapperConfig.getTableNameOverride().getTableNamePrefix() + tableName;
276 }
277 } else if (dynamoDBMapperConfig.getTableNameResolver() != null) {
278 tableName = dynamoDBMapperConfig.getTableNameResolver().getTableName(domainClass, dynamoDBMapperConfig);
279 }
280
281 return tableName;
282 }
283
284
285
286
287 @Override
288 public <T> DynamoDBMapperTableModel<T> getTableModel(Class<T> domainClass) {
289 return dynamoDBMapper.getTableModel(domainClass, dynamoDBMapperConfig);
290 }
291
292 protected <T> void maybeEmitEvent(@Nullable T source, Function<T, DynamoDBMappingEvent<T>> factory) {
293 if (eventPublisher != null) {
294 if (source != null) {
295 DynamoDBMappingEvent<T> event = factory.apply(source);
296
297 eventPublisher.publishEvent(event);
298 }
299 }
300
301 }
302 }