DynamoDB - это облачный сервис AWS, который предоставляет базу данных «ключ-значение» и документов со встроенным автоматическим масштабированием, отказоустойчивостью с помощью многорегиональных глобальных таблиц и отличной поддержкой безопасности, резервного копирования и восстановления. На данный момент это одно из лучших решений для хранения веб-приложений.
Сегодня я расскажу, как быстро создать CRUD API для вашего приложения с DynamoDB и Spring Boot.
- Используйте Spring Initializr (https://start.spring.io/) для создания проекта начальной загрузки Spring. Добавьте Spring Web Starter в качестве зависимостей с помощью опций ниже. Я также решил добавить ломбок, потому что это поможет нам уменьшить шаблонный код.
2. Откройте проект в вашей любимой IDE. Импорт спецификации, чтобы все ваши пакеты aws sdk использовали одну и ту же совместимую версию:
<dependencyManagement> <dependencies> <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-java-sdk-bom</artifactId> <version>1.11.614</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
Добавьте зависимость aws sdk Dynamodb.
<dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-java-sdk-dynamodb</artifactId> </dependency>
Обратите внимание, что я использую версию 1.11.X aws java sdk. Сейчас доступен sdk версии 2.0. Он обеспечивает лучшие возможности асинхронного программирования, что теоретически поможет с производительностью, когда объем действительно велик. Однако в нем по-прежнему отсутствуют многие функции 1.11.X, что очень затрудняет разработку с использованием SDK 2.0. Поэтому пока я все еще рекомендую использовать 1.11.X, пока они 2.0 не достигнут паритета функций.
Вот проблема для добавления модуля отображения Dynamodb в версию 2.0, если вам интересно: https://github.com/aws/aws-sdk-java-v2/issues/35
3. Создайте клиент DynamoDB и средство сопоставления DynamoDB.
@Configuration public class DynamoDBConfig { @Bean public DynamoDBMapper dynamoDBMapper() { AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard() .withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials("XXX", "XXX"))) .withRegion(Regions.US_WEST_2) .build(); return new DynamoDBMapper(client, DynamoDBMapperConfig.DEFAULT); } }
Для быстрого старта достаточно использовать идентификатор ключа доступа пользователя IAM и секретный ключ доступа. Но позже будет сложно решить, как зашифровать и где хранить эту информацию. Позже рекомендуется использовать аутентификацию на основе ролей IAM и получить временный токен для подключения к DynamoDB.
Ссылка: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles.html
4. Создайте класс сущности (объект POJO). Мы воспользуемся API более высокого уровня под названием DynamoDBMapper. Это позволяет нам аннотировать наш класс POJO в режиме гибернации и легко сопоставлять наш класс с объектом db. Список аннотаций, доступных через DynamoDBMapper, можно найти здесь: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBMapper.Annotations.html
Я также использую здесь ломбок, чтобы уменьшить шаблонный код. Он автоматически создаст для нас геттеры, сеттеры и конструкторы.
@Getter @Setter @Builder @NoArgsConstructor @AllArgsConstructor @DynamoDBTable(tableName = "dev-users") public class User { @DynamoDBHashKey(attributeName = "userId") @DynamoDBAutoGeneratedKey private String userId; @DynamoDBAttribute(attributeName = "userName") private String userName; @DynamoDBAttribute(attributeName = "email") private String email; @DynamoDBAttribute(attributeName = "phoneNumber") private String phoneNumber; }
5. Создайте Rest Controller для обработки запросов, я обрабатываю исключения, которые aws может генерировать, когда что-то пошло не так, и возвращаю клиенту соответствующий статус http. Для получения информации о том, какие исключения может генерировать Dynamodb: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Programming.Errors.html
@RestController public class UserCrudController { private UserCrudService userCrudService; @Autowired public UserCrudController(UserCrudService userCrudService) { this.userCrudService = userCrudService; } @RequestMapping(value = "/users", produces = {"application/json"}, method = RequestMethod.POST) public ResponseEntity createUser(@RequestBody User user) { try { User response = userCrudService.createUser(user); return ResponseEntity.status(HttpStatus.CREATED).body(response); } catch (AmazonServiceException e) { throw new ResponseStatusException(HttpStatus.valueOf(e.getStatusCode()), e.getMessage(), e); } catch (AmazonClientException e) { throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, e.getMessage(), e); } } @RequestMapping(value = "/users/{userId}", produces = {"application/json"}, method = RequestMethod.GET) public ResponseEntity readUser(@PathVariable String userId) { try { User response = userCrudService.readUser(userId); return ResponseEntity.status(HttpStatus.OK).body(response); } catch (AmazonServiceException e) { throw new ResponseStatusException(HttpStatus.valueOf(e.getStatusCode()), e.getMessage(), e); } catch (AmazonClientException e) { throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, e.getMessage(), e); } } @RequestMapping(value = "/users", produces = {"application/json"}, method = RequestMethod.PUT) public ResponseEntity updateUser(@RequestBody User user) { try { User response = userCrudService.updateUser(user); return ResponseEntity.status(HttpStatus.OK).body(response); } catch (AmazonServiceException e) { throw new ResponseStatusException(HttpStatus.valueOf(e.getStatusCode()), e.getMessage(), e); } catch (AmazonClientException e) { throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, e.getMessage(), e); } } @RequestMapping(value = "/users/{userId}", produces = {"application/json"}, method = RequestMethod.DELETE) public ResponseEntity deleteUser(@PathVariable String userId) { try { userCrudService.deleteUser(userId); return ResponseEntity.status(HttpStatus.OK).body(null); } catch (AmazonServiceException e) { throw new ResponseStatusException(HttpStatus.valueOf(e.getStatusCode()), e.getMessage(), e); } catch (AmazonClientException e) { throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, e.getMessage(), e); } } }
6. Создайте уровень сервиса для выполнения некоторой бизнес-логики и вызовите dao для фактического чтения и записи данных.
@Service public class UserCrudServiceImpl implements UserCrudService { private UserCrudDao userCrudDao; @Autowired public UserCrudServiceImpl(UserCrudDao userCrudDao) { this.userCrudDao = userCrudDao; } @Override public User createUser(User user) { return userCrudDao.createUser(user); } @Override public User readUser(String userId) { return userCrudDao.readUser(userId); } @Override public User updateUser(User user) { return userCrudDao.updateUser(user); } @Override public void deleteUser(String userId) { userCrudDao.deleteUser(userId); } }
В настоящее время наш уровень обслуживания очень прост, в основном он просто пересылает информацию для чтения и записи dao. Позже мы можем добавить проверку, вызовы других сервисов, асинхронное программирование и т. Д.
7. Создайте слой дао. Обратите внимание, что я добавил условную проверку для обновления / удаления, потому что мы хотим обновить / удалить документ в DynamoDB только в том случае, если он был ранее помещен в базу данных.
@Component public class UserCrudDaoImpl implements UserCrudDao { private DynamoDBMapper dynamoDBMapper; @Autowired public UserCrudDaoImpl(DynamoDBMapper dynamoDBMapper) { this.dynamoDBMapper = dynamoDBMapper; } @Override public User createUser(User user) { dynamoDBMapper.save(user); return user; } @Override public User readUser(String userId) { return dynamoDBMapper.load(User.class, userId); } @Override public User updateUser(User user) { Map<String, ExpectedAttributeValue> expectedAttributeValueMap = new HashMap<>(); expectedAttributeValueMap.put("userId", new ExpectedAttributeValue(new AttributeValue().withS(user.getUserId()))); DynamoDBSaveExpression saveExpression = new DynamoDBSaveExpression().withExpected(expectedAttributeValueMap); dynamoDBMapper.save(user, saveExpression); return user; } @Override public void deleteUser(String userId) { Map<String, ExpectedAttributeValue> expectedAttributeValueMap = new HashMap<>(); expectedAttributeValueMap.put("userId", new ExpectedAttributeValue(new AttributeValue().withS(userId))); DynamoDBDeleteExpression deleteExpression = new DynamoDBDeleteExpression().withExpected(expectedAttributeValueMap); User user = User.builder() .userId(userId) .build(); dynamoDBMapper.delete(user, deleteExpression); } }
8. Создайте свою таблицу DynamoDB в консоли AWS. Нам нужно будет определить имя таблицы и первичный ключ (хеш-ключ). При желании мы также можем определить какой-нибудь ключ сортировки, если это необходимо. Щелкните «Создать».
9. Протестируйте свой CRUD API! Разместите объект пользователя на http: // localhost: 8080 / users
{ “userName”: “abc”, “email”: “[email protected]”, “phoneNumber”: “1234567890” }
Вы получите в ответ
{ "userId": "08f546e2-5e7e-40b5-af39-2bc03ec2c569", "userName": "abc", "email": "[email protected]", "phoneNumber": "1234567890" }
UserId автоматически генерируется DynamoDB, потому что мы добавили аннотацию @DynamoDBAutoGeneratedKey к первичному ключу, который мы определили в классе POJO.
Заключение:
С помощью этих простых шагов у вас теперь есть отличная отправная точка для хранения и извлечения данных пользователей, необходимых для вашего веб-приложения. Для получения дополнительной информации о том, как улучшить ваше приложение с помощью DynamoDB, вы можете посмотреть здесь: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Programming.html