diff --git a/aws/pom.xml b/aws/pom.xml
index 33fc3b0cba..c62b1b9913 100644
--- a/aws/pom.xml
+++ b/aws/pom.xml
@@ -22,6 +22,8 @@
4.12
2.8.9
3.8.0
+ 1.11.86
+ https://s3-us-west-2.amazonaws.com/dynamodb-local/release
@@ -88,6 +90,13 @@
gson
${gson.version}
+
+
+ com.amazonaws
+ DynamoDBLocal
+ ${dynamodblocal.version}
+ test
+
@@ -108,6 +117,35 @@
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+ 2.10
+
+
+ copy-dependencies
+ test-compile
+
+ copy-dependencies
+
+
+ test
+ so,dll,dylib
+ ${project.basedir}/native-libs
+
+
+
+
+
+
+
+ dynamodb-local
+ DynamoDB Local Release Repository
+ ${dynamodblocal.repository.url}
+
+
+
\ No newline at end of file
diff --git a/aws/src/main/java/com/baeldung/dynamodb/entity/ProductInfo.java b/aws/src/main/java/com/baeldung/dynamodb/entity/ProductInfo.java
new file mode 100644
index 0000000000..9af5d926c9
--- /dev/null
+++ b/aws/src/main/java/com/baeldung/dynamodb/entity/ProductInfo.java
@@ -0,0 +1,51 @@
+package com.baeldung.dynamodb.entity;
+
+import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBAttribute;
+import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBAutoGeneratedKey;
+import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey;
+import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable;
+
+@DynamoDBTable(tableName = "ProductInfo")
+public class ProductInfo {
+
+ private String id;
+ private String msrp;
+ private String cost;
+
+ public ProductInfo() {
+ }
+
+ public ProductInfo(String cost, String msrp) {
+ this.msrp = msrp;
+ this.cost = cost;
+ }
+
+ @DynamoDBHashKey
+ @DynamoDBAutoGeneratedKey
+ public String getId() {
+ return id;
+ }
+
+ @DynamoDBAttribute
+ public String getMsrp() {
+ return msrp;
+ }
+
+ @DynamoDBAttribute
+ public String getCost() {
+ return cost;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public void setMsrp(String msrp) {
+ this.msrp = msrp;
+ }
+
+ public void setCost(String cost) {
+ this.cost = cost;
+ }
+
+}
diff --git a/aws/src/main/java/com/baeldung/dynamodb/repository/AbstractRepository.java b/aws/src/main/java/com/baeldung/dynamodb/repository/AbstractRepository.java
new file mode 100644
index 0000000000..79934c17e9
--- /dev/null
+++ b/aws/src/main/java/com/baeldung/dynamodb/repository/AbstractRepository.java
@@ -0,0 +1,49 @@
+package com.baeldung.dynamodb.repository;
+
+import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper;
+import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBScanExpression;
+
+import java.io.Serializable;
+import java.lang.reflect.ParameterizedType;
+import java.util.List;
+
+public abstract class AbstractRepository {
+
+ protected DynamoDBMapper mapper;
+ protected Class entityClass;
+
+ protected AbstractRepository() {
+ ParameterizedType genericSuperclass = (ParameterizedType) getClass().getGenericSuperclass();
+
+ // This entityClass refers to the actual entity class in the subclass declaration.
+
+ // For instance, ProductInfoDAO extends AbstractDAO
+ // In this case entityClass = ProductInfo, and ID is String type
+ // which refers to the ProductInfo's partition key string value
+ this.entityClass = (Class) genericSuperclass.getActualTypeArguments()[0];
+ }
+
+ public void save(T t) {
+ mapper.save(t);
+ }
+
+ public T findOne(ID id) {
+ return mapper.load(entityClass, id);
+ }
+
+ /**
+ * WARNING: It is not recommended to perform full table scan
+ * targeting the real production environment.
+ *
+ * @return All items
+ */
+ public List findAll() {
+ DynamoDBScanExpression scanExpression = new DynamoDBScanExpression();
+ return mapper.scan(entityClass, scanExpression);
+ }
+
+ public void setMapper(DynamoDBMapper dynamoDBMapper) {
+ this.mapper = dynamoDBMapper;
+ }
+
+}
diff --git a/aws/src/main/java/com/baeldung/dynamodb/repository/ProductInfoRepository.java b/aws/src/main/java/com/baeldung/dynamodb/repository/ProductInfoRepository.java
new file mode 100644
index 0000000000..0dfec8e05c
--- /dev/null
+++ b/aws/src/main/java/com/baeldung/dynamodb/repository/ProductInfoRepository.java
@@ -0,0 +1,6 @@
+package com.baeldung.dynamodb.repository;
+
+import com.baeldung.dynamodb.entity.ProductInfo;
+
+public class ProductInfoRepository extends AbstractRepository {
+}
diff --git a/aws/src/test/java/com/baeldung/dynamodb/ProductInfoRepositoryIntegrationTest.java b/aws/src/test/java/com/baeldung/dynamodb/ProductInfoRepositoryIntegrationTest.java
new file mode 100644
index 0000000000..929a985067
--- /dev/null
+++ b/aws/src/test/java/com/baeldung/dynamodb/ProductInfoRepositoryIntegrationTest.java
@@ -0,0 +1,121 @@
+package com.baeldung.dynamodb;
+
+import com.amazonaws.auth.BasicAWSCredentials;
+import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
+import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient;
+import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper;
+import com.amazonaws.services.dynamodbv2.model.CreateTableRequest;
+import com.amazonaws.services.dynamodbv2.model.ProvisionedThroughput;
+import com.amazonaws.services.dynamodbv2.model.ResourceInUseException;
+import com.baeldung.dynamodb.entity.ProductInfo;
+import com.baeldung.dynamodb.repository.ProductInfoRepository;
+import com.baeldung.dynamodb.rule.LocalDbCreationRule;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URISyntaxException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.Optional;
+import java.util.Properties;
+
+import static org.hamcrest.Matchers.greaterThan;
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsEqual.equalTo;
+import static org.junit.Assert.assertThat;
+
+public class ProductInfoRepositoryIntegrationTest {
+
+ @ClassRule
+ public static LocalDbCreationRule dynamoDB = new LocalDbCreationRule();
+
+ private static DynamoDBMapper dynamoDBMapper;
+ private static AmazonDynamoDB amazonDynamoDB;
+
+ private ProductInfoRepository repository;
+
+ private static final String DYNAMODB_ENDPOINT = "amazon.dynamodb.endpoint";
+ private static final String AWS_ACCESSKEY = "amazon.aws.accesskey";
+ private static final String AWS_SECRETKEY = "amazon.aws.secretkey";
+
+ private static final String EXPECTED_COST = "20";
+ private static final String EXPECTED_PRICE = "50";
+
+ @BeforeClass
+ public static void setupClass() {
+ Properties testProperties = loadFromFileInClasspath("test.properties")
+ .filter(properties -> !isEmpty(properties.getProperty(AWS_ACCESSKEY)))
+ .filter(properties -> !isEmpty(properties.getProperty(AWS_SECRETKEY)))
+ .filter(properties -> !isEmpty(properties.getProperty(DYNAMODB_ENDPOINT)))
+ .orElseThrow(() -> new RuntimeException("Unable to get all of the required test property values"));
+
+ String amazonAWSAccessKey = testProperties.getProperty(AWS_ACCESSKEY);
+ String amazonAWSSecretKey = testProperties.getProperty(AWS_SECRETKEY);
+ String amazonDynamoDBEndpoint = testProperties.getProperty(DYNAMODB_ENDPOINT);
+
+ amazonDynamoDB = new AmazonDynamoDBClient(new BasicAWSCredentials(amazonAWSAccessKey, amazonAWSSecretKey));
+ amazonDynamoDB.setEndpoint(amazonDynamoDBEndpoint);
+ dynamoDBMapper = new DynamoDBMapper(amazonDynamoDB);
+ }
+
+ @Before
+ public void setup() {
+ try {
+ repository = new ProductInfoRepository();
+ repository.setMapper(dynamoDBMapper);
+
+ CreateTableRequest tableRequest = dynamoDBMapper.generateCreateTableRequest(ProductInfo.class);
+
+ tableRequest.setProvisionedThroughput(new ProvisionedThroughput(1L, 1L));
+
+ amazonDynamoDB.createTable(tableRequest);
+ } catch (ResourceInUseException e) {
+ // Do nothing, table already created
+ }
+
+ // TODO How to handle different environments. i.e. AVOID deleting all entries in ProductInfo on table
+ dynamoDBMapper.batchDelete((List) repository.findAll());
+ }
+
+ @Test
+ public void givenItemWithExpectedCost_whenRunFindAll_thenItemIsFound() {
+
+ ProductInfo productInfo = new ProductInfo(EXPECTED_COST, EXPECTED_PRICE);
+ repository.save(productInfo);
+
+ List result = (List) repository.findAll();
+ assertThat(result.size(), is(greaterThan(0)));
+ assertThat(result.get(0).getCost(), is(equalTo(EXPECTED_COST)));
+ }
+
+ private static boolean isEmpty(String inputString) {
+ return inputString == null || "".equals(inputString);
+ }
+
+ private static Optional loadFromFileInClasspath(String fileName) {
+ InputStream stream = null;
+ try {
+ Properties config = new Properties();
+ Path configLocation = Paths.get(ClassLoader.getSystemResource(fileName).toURI());
+ stream = Files.newInputStream(configLocation);
+ config.load(stream);
+ return Optional.of(config);
+ } catch (Exception e) {
+ return Optional.empty();
+ } finally {
+ if (stream != null) {
+ try {
+ stream.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ }
+
+}
diff --git a/aws/src/test/java/com/baeldung/dynamodb/rule/LocalDbCreationRule.java b/aws/src/test/java/com/baeldung/dynamodb/rule/LocalDbCreationRule.java
new file mode 100644
index 0000000000..45a107103d
--- /dev/null
+++ b/aws/src/test/java/com/baeldung/dynamodb/rule/LocalDbCreationRule.java
@@ -0,0 +1,35 @@
+package com.baeldung.dynamodb.rule;
+
+import com.amazonaws.services.dynamodbv2.local.main.ServerRunner;
+import com.amazonaws.services.dynamodbv2.local.server.DynamoDBProxyServer;
+import org.junit.rules.ExternalResource;
+
+public class LocalDbCreationRule extends ExternalResource {
+
+ protected DynamoDBProxyServer server;
+
+ public LocalDbCreationRule() {
+ System.setProperty("sqlite4java.library.path", "native-libs");
+ }
+
+ @Override
+ protected void before() throws Exception {
+ String port = "8000";
+ this.server = ServerRunner.createServerFromCommandLineArgs(new String[]{"-inMemory", "-port", port});
+ server.start();
+ }
+
+ @Override
+ protected void after() {
+ this.stopUnchecked(server);
+ }
+
+ protected void stopUnchecked(DynamoDBProxyServer dynamoDbServer) {
+ try {
+ dynamoDbServer.stop();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+}
diff --git a/aws/src/test/resources/test.properties b/aws/src/test/resources/test.properties
new file mode 100644
index 0000000000..a7051d0244
--- /dev/null
+++ b/aws/src/test/resources/test.properties
@@ -0,0 +1,3 @@
+amazon.dynamodb.endpoint=http://localhost:8000/
+amazon.aws.accesskey=key
+amazon.aws.secretkey=key2
\ No newline at end of file