Вступление
- Amazon DynamoDB — это полностью управляемая служба баз данных NoSQL, которая обеспечивает быструю и предсказуемую производительность с плавной масштабируемостью.
- Amazon DynamoDB автоматически распределяет данные и трафик для таблицы по достаточному количеству серверов, чтобы обрабатывать объем запросов, указанный клиентом, и объем хранимых данных, сохраняя при этом стабильную и быструю производительность.
- Все элементы данных хранятся на твердотельных дисках (SSD) и автоматически реплицируются в нескольких зонах доступности в регионе, чтобы обеспечить встроенную высокую доступность и долговечность данных.
- Вы можете запустить новую таблицу базы данных Amazon DynamoDB, увеличить или уменьшить объем запросов для этой таблицы без простоев или снижения производительности, а также получить представление об использовании ресурсов и показателях производительности через Консоль управления AWS.
- С помощью Amazon DynamoDB вы можете снять административную нагрузку с эксплуатации и масштабирования распределенных баз данных на AWS, поэтому вам не придется беспокоиться о выделении оборудования, настройке и настройке, репликации, исправлении программного обеспечения или масштабировании кластера.
- SDK: http://aws.amazon.com/sdkforjava/
Amazon DynamoDB Аннотации
- @DynamoDBTable
Определяет целевую таблицу в Amazon DynamoDB. Например, следующий фрагмент кода Java определяет класс Developer и отображает его в таблицу People в Amazon DynamoDB.
1
2
|
@DynamoDBTable (tableName= "People" ) public class Developer { ...} |
- @DynamoDBIgnore
Указывает экземпляру DynamoDBMapper, что соответствующее свойство следует игнорировать. При сохранении данных в таблицу DynamoDBMapper не сохраняет это свойство в таблице.
- @DynamoDBAttribute
Сопоставляет свойство с атрибутом таблицы. По умолчанию каждое свойство класса отображается на атрибут элемента с тем же именем. Однако, если имена не совпадают, с помощью этого тега вы можете сопоставить свойство с атрибутом. В следующем фрагменте кода Java DynamoDBAttribute сопоставляет свойство BookAuthors с именем атрибута Authors в таблице.
1
2
3
|
@DynamoDBAttribute (attributeName = "Authors" ) public List<String> getBookAuthors() { return BookAuthors; } public void setBookAuthors(List<String> BookAuthors) { this .BookAuthors = BookAuthors; } |
DynamoDBMapper использует Authors в качестве имени атрибута при сохранении объекта в таблице.
- @DynamoDBHashKey
Сопоставляет свойство класса с атрибутом hash таблицы. Свойство должно быть одного из поддерживаемых типов String или Numeric и не может быть типом коллекции.
Предположим, у вас есть таблица ProductCatalog, в которой Id является первичным ключом. Следующий фрагмент кода Java определяет класс CatalogItem и отображает свойство itsId в первичный ключ таблицы ProductCatalog с помощью тега @DynamoDBHashKey.
01
02
03
04
05
06
07
08
09
10
11
12
|
@DynamoDBTable (tableName= "ProductCatalog" ) public class CatalogItem { private String Id; @DynamoDBHashKey (attributeName= "Id" ) public String getId() { return Id; } public void setId(String Id) { this .Id = Id; } // Additional properties go here. } |
- @DynamoDBRangeKey
Сопоставляет свойство класса с атрибутом ключа диапазона таблицы. Если первичный ключ состоит из атрибутов ключа хеша и диапазона, вы можете использовать этот тег для сопоставления поля класса с атрибутом диапазона. Например, предположим, что у вас есть таблица ответов, в которой хранятся ответы на темы форума. Каждый поток может иметь много ответов. Таким образом, первичным ключом этой таблицы является ThreadId и ReplyDateTime. ThreadId является атрибутом хэша, а ReplyDateTime является атрибутом диапазона. Следующий фрагмент кода Java определяет класс Reply и сопоставляет его с таблицей Reply. Он использует @DynamoDBHashKey и @DynamoDBRangeKeytags для определения свойств класса, которые сопоставляются с первичным ключом.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
@DynamoDBTable (tableName= "Reply" ) public class Reply { private String id; private String replyDateTime; @DynamoDBHashKey (attributeName= "Id" ) public String getId() { return id; } public void setId(String id) { this .id = id; } @DynamoDBRangeKey (attributeName= "ReplyDateTime" ) public String getReplyDateTime() { return replyDateTime; } public void setReplyDateTime(String replyDateTime) { this .replyDateTime = replyDateTime; } // Additional properties go here. } |
- @DynamoDBAutoGeneratedKey
Помечает свойство ключа хеша или ключа диапазона как автоматически сгенерированное. Модель сохраняемости объектов будет генерировать случайный UUID при сохранении этих атрибутов. Только свойства String могут быть помечены как автоматически сгенерированные ключи.
Следующий фрагмент демонстрирует использование автоматически сгенерированных ключей.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
@DynamoDBTable (tableName= "AutoGeneratedKeysExample" ) public class AutoGeneratedKeys { private String id; private String payload; @DynamoDBHashKey (attributeName = "Id" ) @DynamoDBAutoGeneratedKey public String getId() { return id; } public void setId(String id) { this .id = id; } @DynamoDBAttribute (attributeName= "payload" ) public String getPayload() { return this .payload }; public String setPayload(String payload) { this .payload = payload }; public static void saveItem() { AutoGeneratedKeys obj = new AutoGeneratedKeys(); obj.setPayload( "abc123" ); // id field is null at this point DynamoDBMapper mapper = new DynamoDBMapper(dynamoDBClient); mapper.save(obj); System.out.println( "Object was saved with id " + obj.getId()); } } |
- @DynamoDBVersionAttribute
Определяет свойство класса для хранения оптимистичного номера версии блокировки. DynamoDBMapper назначает номер версии этому свойству при сохранении нового элемента и увеличивает его при каждом обновлении элемента. Поддерживаются только числовые скалярные типы.
DynamoDBMapper Class
Класс DynamoDBMapper является точкой входа в Amazon DynamoDB. Он обеспечивает подключение к Amazon DynamoDB и позволяет получать доступ к данным в различных таблицах, выполнять различные операции CRUD с элементами, а также выполнять запросы и сканирование по таблицам. Этот класс предоставляет следующие ключевые операции для работы с Amazon DynamoDB.
- спасти
- Сохраняет указанный объект в таблицу. Объект, который вы хотите сохранить, является единственным обязательным параметром для этого метода. Вы можете предоставить необязательные параметры конфигурации, используя объект DynamoDBMapperConfig.
- Если элемент с таким же первичным ключом не существует, этот метод создает новый элемент в таблице. Если элемент с таким же первичным ключом существует, он обновляет существующий элемент. Строковый хэш и ключи диапазона, аннотированные @DynamoDBAutoGeneratedKey, получают случайный универсальный уникальный идентификатор (UUID), если он оставлен неинициализированным. Поля версии, помеченные @DynamoDBVersionAttribute, будут увеличены на единицу. Кроме того, если поле версии обновляется или генерируется ключ, переданный объект обновляется в результате операции.
1
|
mapper.save(obj, new DynamoDBMapperConfig(DynamoDBMapperConfig.SaveBehavior.CLOBBER)); |
- нагрузка
- Получает элемент из таблицы. Вы должны предоставить первичный ключ элемента, который вы хотите получить.
1
2
|
CatalogItem item = mapper.load(CatalogItem. class , item.getId(), new DynamoDBMapperConfig(DynamoDBMapperConfig.ConsistentReads.CONSISTENT)); |
- удалять
- Удаляет элемент из таблицы. Вы должны передать экземпляр объекта сопоставленного класса.
- запрос
- Включает запрос к таблице. Вы можете запросить таблицу, только если ее первичный ключ состоит из атрибута хеша и диапазона. Этот метод требует, чтобы вы предоставили значение атрибута хеша и фильтр запроса, который применяется к атрибуту диапазона. Выражение фильтра включает условие и значение.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
|
String forumName = "Amazon DynamoDB" ; String forumSubject = "DynamoDB Thread 1" ; String hashKey = forumName + "#" + forumSubject; long twoWeeksAgoMilli = ( new Date()).getTime() - (14L*24L*60L*60L*1000L); Date twoWeeksAgo = new Date(); twoWeeksAgo.setTime(twoWeeksAgoMilli); SimpleDateFormat df = new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" ); String twoWeeksAgoStr = df.format(twoWeeksAgo); Condition rangeKeyCondition = new Condition() .withComparisonOperator(ComparisonOperator.GT.toString()) .withAttributeValueList( new AttributeValue().withS(twoWeeksAgoStr.toString())); Reply replyKey = new Reply(); replyKey.setId(hashKey); DynamoDBQueryExpression<Reply> queryExpression = new DynamoDBQueryExpression<Reply>() .withHashKeyValues(replyKey) .withRangeKeyCondition( "ReplyDateTime" , rangeKeyCondition); List<Reply> latestReplies = mapper.query(Reply. class , queryExpression); |
Запрос возвращает коллекцию объектов Reply.
- сканирование
- Сканирует всю таблицу. Вы можете указать необязательные элементы фильтров условий на основе одного или нескольких экземпляров Условий и указать выражение фильтра для любых атрибутов элементов.
01
02
03
04
05
06
07
08
09
10
11
12
|
DynamoDBScanExpression scanExpression = new DynamoDBScanExpression(); Map<String, Condition> scanFilter = new HashMap<String, Condition>(); Condition scanCondition = new Condition() .withComparisonOperator(ComparisonOperator.EQ.toString()) .withAttributeValueList( new AttributeValue().withN( "0" )); scanFilter.put( "Answered" , scanCondition); scanExpression.setScanFilter(scanFilter); List<Thread> unansweredThreads = mapper.scan(Thread. class , scanExpression); |
- Метод сканирования возвращает коллекцию с отложенной загрузкой. Первоначально он возвращает только одну страницу результатов, а затем при необходимости выполняет сервисный вызов для следующей страницы. Чтобы получить все подходящие элементы, вам нужно всего лишь перебрать коллекцию unansptedThreads.
- batchDelete
- Удаляет объекты из одной или нескольких таблиц, используя один или несколько вызовов метода AmazonDynamoDB.batchWriteItem. Этот метод не предоставляет гарантий транзакций.
1
2
3
|
Book book1 = mapper.load(Book. class , 901 ); Book book2 = mapper.load(Book. class , 902 ); mapper.batchDelete(Arrays.asList(book1, book2)); |
- batchSave
- Сохраняет объекты в одну или несколько таблиц, используя один или несколько вызовов метода AmazonDynamoDB.batchWriteItem. Этот метод не предоставляет гарантий транзакций.
01
02
03
04
05
06
07
08
09
10
11
|
Book book1 = new Book(); book1.id = 901 ; book1.productCategory = "Book" ; book1.title = "Book 901 Title" ; Book book2 = new Book(); book2.id = 902 ; book2.productCategory = "Book" ; book2.title = "Book 902 Title" ; mapper.batchSave(Arrays.asList(book1, book2)); |
- batchWrite
- Сохраняет объекты и удаляет объекты из одной или нескольких таблиц, используя один или несколько вызовов метода AmazonDynamoDB.batchWriteItem. Этот метод не предоставляет гарантии транзакций и не поддерживает управление версиями (условное размещение или удаление).
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
// Create a Forum item to save Forum forumItem = new Forum(); forumItem.name = "Test BatchWrite Forum" ; // Create a Thread item to save Thread threadItem = new Thread(); threadItem.forumName = "AmazonDynamoDB" ; threadItem.subject = "My sample question" ; // Load a ProductCatalog item to delete Book book3 = mapper.load(Book. class , 903 ); List<Object> objectsToWrite = Arrays.asList(forumItem, threadItem); List<Book> objectsToDelete = Arrays.asList(book3); mapper.batchWrite(objectsToWrite, objectsToDelete); |
- подсчитывать
- Оценивает указанное выражение сканирования и возвращает количество совпадающих элементов. Данные элемента не возвращаются.
- marshallIntoObject
- Служебный метод для преобразования результата из низкоуровневого API в объект домена.
Поддерживаемые типы данных
Amazon DynamoDB поддерживает следующие примитивные типы данных и примитивные классы-оболочки.
- строка
- Логическое, логическое
- Байт, байт
- Дата (как строка с точностью до миллисекунды ISO8601, смещенная в UTC)
- Календарь (как строка с точностью до миллисекунды ISO8601, смещенная в UTC)
- Долго долго
- Integer, int
- Двойной, двойной
- Плавать, плавать
- BigDecimal
- BigInteger
Amazon DynamoDB поддерживает типы коллекций Java Set . Если ваше свойство сопоставленной коллекции не является Set, то генерируется исключение.
В следующей таблице показано, как предыдущие типы Java отображаются на типы Amazon DynamoDB.
Тип Java | Тип Amazon DynamoDB |
Все типы номеров | N (тип номера) |
Струны | S (тип строки) |
логический | N (тип числа), 0 или 1. |
ByteBuffer | B (двоичный тип) |
Дата | S (тип строки). Значения даты хранятся в виде строк в формате ISO-8601. |
Установить типы коллекций | Тип SS (набор строк), тип NS (набор номеров) или тип BS (набор двоичных данных). |
Пример Java: операции CRUD
CatalogItem.java
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
import java.util.Set; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBAttribute; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable; @DynamoDBTable (tableName= "ProductCatalog" ) public class CatalogItem { private Integer id; private String title; private String ISBN; private Set bookAuthors; @DynamoDBHashKey (attributeName= "Id" ) public Integer getId() { return id; } public void setId(Integer id) { this .id = id; } @DynamoDBAttribute (attributeName= "Title" ) public String getTitle() { return title; } public void setTitle(String title) { this .title = title; } @DynamoDBAttribute (attributeName= "ISBN" ) public String getISBN() { return ISBN; } public void setISBN(String ISBN) { this .ISBN = ISBN;} @DynamoDBAttribute (attributeName = "Authors" ) public Set getBookAuthors() { return bookAuthors; } public void setBookAuthors(Set bookAuthors) { this .bookAuthors = bookAuthors; } @Override public String toString() { return "Book [ISBN=" + ISBN + ", bookAuthors=" + bookAuthors + ", id=" + id + ", title=" + title + "]" ; } } |
ObjectPersistenceCRUDExample.java
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
|
import java.io.IOException; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Random; import com.amazonaws.AmazonClientException; import com.amazonaws.AmazonServiceException; import com.amazonaws.auth.AWSCredentials; import com.amazonaws.auth.ClasspathPropertiesFileCredentialsProvider; import com.amazonaws.regions.Region; import com.amazonaws.regions.Regions; import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperConfig; import com.amazonaws.services.dynamodbv2.model.AttributeDefinition; import com.amazonaws.services.dynamodbv2.model.AttributeValue; import com.amazonaws.services.dynamodbv2.model.ComparisonOperator; import com.amazonaws.services.dynamodbv2.model.Condition; import com.amazonaws.services.dynamodbv2.model.CreateTableRequest; import com.amazonaws.services.dynamodbv2.model.DescribeTableRequest; import com.amazonaws.services.dynamodbv2.model.KeySchemaElement; import com.amazonaws.services.dynamodbv2.model.KeyType; import com.amazonaws.services.dynamodbv2.model.ProvisionedThroughput; import com.amazonaws.services.dynamodbv2.model.ScalarAttributeType; import com.amazonaws.services.dynamodbv2.model.ScanRequest; import com.amazonaws.services.dynamodbv2.model.ScanResult; import com.amazonaws.services.dynamodbv2.model.TableDescription; import com.amazonaws.services.dynamodbv2.model.TableStatus; public class ObjectPersistenceCRUDExample { static AmazonDynamoDBClient client; private DynamoDBMapper mapper; private static int PRODUCT_ID; public static void main(String[] args) throws IOException { ObjectPersistenceCRUDExample demo = new ObjectPersistenceCRUDExample(); demo.init(); demo.createTable( "ProductCatalog" ); for ( int i = 0 ; i < 100 ; i++) { System.out.println(i); PRODUCT_ID = PRODUCT_ID + i; demo.insert(); } demo.getAllRows(); CatalogItem itemRetrieved = demo.load(PRODUCT_ID); demo.update(itemRetrieved); CatalogItem updatedItem = demo.load(PRODUCT_ID); demo.delete(updatedItem); demo.load(updatedItem.getId()); System.out.println( "Example complete!" ); } private void init() { PRODUCT_ID = new Random().nextInt( 1000 ); AWSCredentials credentials = new ClasspathPropertiesFileCredentialsProvider() .getCredentials(); client = new AmazonDynamoDBClient(credentials); Region usWest2 = Region.getRegion(Regions.US_WEST_2); client.setRegion(usWest2); mapper = new DynamoDBMapper(client); } private void createTable(String tableName) { try { CreateTableRequest createTableRequest = new CreateTableRequest() .withTableName(tableName); createTableRequest.withKeySchema( new KeySchemaElement() .withAttributeName( "Id" ).withKeyType(KeyType.HASH)); createTableRequest .withAttributeDefinitions( new AttributeDefinition() .withAttributeName( "Id" ).withAttributeType( ScalarAttributeType.N)); createTableRequest .withProvisionedThroughput( new ProvisionedThroughput() .withReadCapacityUnits(10L).withWriteCapacityUnits( 10L)); TableDescription createdTableDescription = client.createTable( createTableRequest).getTableDescription(); System.out.println( "Created Table: " + createdTableDescription); // Wait for it to become active waitForTableToBecomeAvailable(tableName); } catch (AmazonServiceException e) { e.printStackTrace(); } catch (AmazonClientException e) { e.printStackTrace(); } } private void waitForTableToBecomeAvailable(String tableName) { System.out.println( "Waiting for " + tableName + " to become ACTIVE..." ); long startTime = System.currentTimeMillis(); long endTime = startTime + ( 10 * 60 * 1000 ); while (System.currentTimeMillis() < endTime) { try { Thread.sleep( 1000 * 20 ); } catch (Exception e) { } try { DescribeTableRequest request = new DescribeTableRequest() .withTableName(tableName); TableDescription tableDescription = client.describeTable( request).getTable(); String tableStatus = tableDescription.getTableStatus(); System.out.println( " - current state: " + tableStatus); if (tableStatus.equals(TableStatus.ACTIVE.toString())) return ; } catch (AmazonServiceException ase) { if (ase.getErrorCode().equalsIgnoreCase( "ResourceNotFoundException" ) == false ) throw ase; } } throw new RuntimeException( "Table " + tableName + " never went active" ); } private void insert() { CatalogItem item = new CatalogItem(); item.setId(PRODUCT_ID); item.setTitle( "Book PRODUCT_ID" ); item.setISBN( "611-1111111111" ); item.setBookAuthors( new HashSet(Arrays.asList( "Author1" , "Author2" ))); // Save the item (book). mapper.save(item); } private void update(CatalogItem itemRetrieved) { itemRetrieved.setISBN( "622-2222222222" ); itemRetrieved.setBookAuthors( new HashSet(Arrays.asList( "Author1" , "Author3" ))); mapper.save(itemRetrieved); System.out.println( "Item updated:" ); System.out.println(itemRetrieved); } private void delete(CatalogItem updatedItem) { // Delete the item. mapper.delete(updatedItem); } private CatalogItem load( int id) { // Retrieve the updated item. DynamoDBMapperConfig config = new DynamoDBMapperConfig( DynamoDBMapperConfig.ConsistentReads.CONSISTENT); CatalogItem updatedItem = mapper.load(CatalogItem. class , id, config); if (updatedItem == null ) { System.out.println( "Done - Sample item is deleted." ); } else { System.out.println( "Retrieved item:" ); System.out.println(updatedItem); } return updatedItem; } private void getAllRows() { ScanRequest scanRequest = new ScanRequest() .withTableName( "ProductCatalog" ); scanRequest.setLimit( 10 ); HashMap scanFilter = new HashMap(); Condition condition = new Condition().withComparisonOperator( ComparisonOperator.EQ.toString()).withAttributeValueList( new AttributeValue().withS( "611-1111111111" )); scanFilter.put( "ISBN" , condition); Condition condition2 = new Condition().withComparisonOperator( ComparisonOperator.LE.toString()).withAttributeValueList( new AttributeValue().withN( "1000" )); scanFilter.put( "Id" , condition2); scanRequest.withScanFilter(scanFilter); try { System.out.println( "Scan Request: " + scanRequest); ScanResult scanResponse = client.scan(scanRequest); for (Map item : scanResponse.getItems()) { System.out.println(item.get( "Id" ).getN() + " , " + item.get( "ISBN" ).getS() + " , " + item.get( "Authors" ).getSS() + " , " + item.get( "Title" ).getS()); } System.out.println( "Scan Response: " + scanResponse); System.out.println( "Count: " + scanResponse.getCount()); System.out.println( "Scanned Count: " + scanResponse.getScannedCount()); System.out.println( "Items: " + scanResponse.getItems()); } catch (AmazonServiceException e) { e.printStackTrace(); } catch (AmazonClientException e) { e.printStackTrace(); } } } |