前言
dynamodb是AWS的一款非關系型數據庫。適用於無需頻繁增刪改查且關聯性不強的大數據,比如某個用戶的歷史訂單信息等。
歷史訂單可能不常查詢但數據量很大,且只要有用戶ID就可以查詢出來,類似這種的可以使用非關系型數據庫。
這個例子未必恰當,僅想表達不是任何情況都要使用非關系型數據庫,每種數據庫都有其存在的意義。(曾經因為這個被坑的很慘)
進入正題:
可進入AWS官網查看Java Dynamodb的文檔,但我覺得文檔寫的過大過多,不太好找,所以自己記錄下。
在AWS控制台創建表,創建過程中要制定分區鍵和排序鍵,若未創建排序鍵無法候補,只能刪表重建。
Dynamodb無法分庫,因此表名要清楚明了。
重要::雖然我很菜,寫的也不夠好,但我不接受任何批評,本文僅供有需要的人參考及自己記錄用。
實體常用注解:映射實體和表的關系,放在getter方法上
@DynamoDBHashKey 注釋分區鍵
@DynamoDBRangeKey 注釋排序鍵
@DynamoDBAttribute 注釋普通屬性
@DynamoDBIndexHashKey 注釋二級索引分區鍵
@DynamoDBIndexRangeKey 注釋二級索引排序鍵
@DynamoDBIgnore 忽略某個對象或屬性
// 分區鍵 @DynamoDBHashKey(attributeName="user_id") public String getUserId() { return userId; } // 排序鍵 @DynamoDBRangeKey(attributeName="event_id") public String getEventId() { return eventId; } // 二級索引 @DynamoDBIndexHashKey(globalSecondaryIndexName= "user_name-index", attributeName= "user_name") public String getUserName() { return userName; } // 屬性 @DynamoDBAttribute(attributeName="user_gender") public String getUserGender() { return userGender; } // 忽略 @DynamoDBIgnore public Student getStudent() { return student; } ...
代碼實現
注意::
① withKeyConditionExpression 針對分區鍵、排序鍵的查詢條件中,不支持使用contains模糊查詢
② withFilterExpression 針對其他字段的過濾查詢條件,結合limit使用,會先查詢,后分頁,導致數據變少
因此Dynamodb無法在模糊查詢的同時進行分頁。
Service層
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBQueryExpression; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBScanExpression; import com.amazonaws.services.dynamodbv2.datamodeling.QueryResultPage; import com.amazonaws.services.dynamodbv2.datamodeling.ScanResultPage; import com.amazonaws.services.dynamodbv2.model.AttributeValue;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.HashMap; import java.util.List; import java.util.Map; @Service public class TestService { @Autowired public BaseDao baseDao; /** * Student表: 分區鍵student_id、二級索引student_name等 * Teacher表: 分區鍵teacher_id、排序鍵student_id、二級索引teacher_name等 * */ /** * 插入數據 */ public void saveDemoData() { Student demoInfo = new Student(); demoInfo.setStudentId("sId1"); demoInfo.setStudentName("sName1"); baseDao.saveItem(demoInfo); } /** * 更新數據 */ public void updateDemoData() { Student demoInfo = new Student(); demoInfo.setStudentId("sId1"); demoInfo.setStudentName("sName1"); baseDao.updateItem(demoInfo); } /** * 刪除數據 */ public void deleteDemoData() { Student demoInfo = new Student(); demoInfo.setStudentId("sId1"); demoInfo.setStudentName("sName1"); baseDao.deleteItem(demoInfo); } /** * Query * 根據分區鍵查詢 */ public Student getQueryResult() { Student demoInfo = (Student) baseDao.getQueryResult(Student.class, "sId1"); System.out.println(demoInfo); return demoInfo; } /** * Query * 根據二級索引查詢 */ public void getQueryIndexResult() { // 構建查詢數據 Map<String, AttributeValue> vals = new HashMap<>(); vals.put(":v_student_name", new AttributeValue().withS("sName1")); DynamoDBQueryExpression<Student> exp = new DynamoDBQueryExpression<Student>() .withKeyConditionExpression("student_name = :v_student_name") // 查詢條件 .withIndexName("student_name-index") // 二級索引名稱 .withExpressionAttributeValues(vals) // 查詢條件賦值 .withConsistentRead(false); // 最終一致性,需設置成false QueryResultPage<Student> result = (QueryResultPage<Student>) baseDao.getQueryPageExpResult(Student.class, exp); System.out.println(result); // 總條數 System.out.println("Result size ===>" + result.getResults().size()); // 是否存在下一頁 System.out.println("Last evaluated key ===>" + result.getLastEvaluatedKey()); } /** * Query 查詢 * 根據分區鍵、排序鍵查詢數據 */ public void getQueryExpResult() { Map<String, AttributeValue> vals = new HashMap<String, AttributeValue>(); vals.put(":v_teacher_id", new AttributeValue().withS("tId1")); vals.put(":v_student_id",new AttributeValue().withS("sId1")); DynamoDBQueryExpression<Teacher> queryExpression = new DynamoDBQueryExpression<Teacher>() .withKeyConditionExpression("teacher_id = :v_teacher_id and student_id > :v_student_id") .withExpressionAttributeValues(vals); List<Teacher> list = baseDao.getQueryExpResult(Teacher.class, queryExpression); System.out.println(list); } /** * Query 分頁查詢 * 根據二級索引查詢 */ public void getQueryPageExpResult() { // 構建查詢數據 Map<String, AttributeValue> vals = new HashMap<>(); vals.put(":v_teacher_name", new AttributeValue().withS("tName1")); DynamoDBQueryExpression<Teacher> exp = new DynamoDBQueryExpression<Teacher>() .withKeyConditionExpression("teacher_name = :v_teacher_name") // 查詢條件 .withIndexName("teacher_name-index") // 二級索引名稱 .withExpressionAttributeValues(vals) // 查詢條件賦值 .withScanIndexForward(true) .withConsistentRead(false) .withLimit(3); // 分頁條數 // 下一頁賦值 Map<String, AttributeValue> startKey = new HashMap<>(); startKey.put("teacher_id", new AttributeValue().withS("tId1")); startKey.put("student_id", new AttributeValue().withS("sId2")); exp.setExclusiveStartKey(startKey); QueryResultPage<Teacher> result = (QueryResultPage<Teacher>) baseDao.getQueryPageExpResult(Teacher.class, exp); // 返回結果 List<Teacher> list = result.getResults(); System.out.println("Result size ===>" + result.getResults().size()); System.out.println("Last evaluated key ===>" + result.getLastEvaluatedKey()); } /** * Scan 查詢 */ public void getScanList() { Map<String, AttributeValue> vals = new HashMap<>(); vals.put(":v_student_address", new AttributeValue().withS("地址1")); // 構建查詢數據 DynamoDBScanExpression exp = new DynamoDBScanExpression() .withFilterExpression("contains(student_address,:v_student_address)") .withExpressionAttributeValues(vals) .withLimit(3); List<Student> b = (List<Student>) baseDao.getScanResult(Student.class, exp); } /** * Scan分頁查詢 */ public void getScanPageList() { Map<String, AttributeValue> vals = new HashMap<>(); vals.put(":v_student_address", new AttributeValue().withS("地址1")); DynamoDBScanExpression exp = new DynamoDBScanExpression() .withFilterExpression("contains(student_address,:v_student_address)") .withExpressionAttributeValues(vals) .withLimit(3); // 下一頁賦值 Map<String, AttributeValue> startKey = new HashMap<>(); startKey.put("student_id", new AttributeValue().withS("sId1")); exp.setExclusiveStartKey(startKey); ScanResultPage<Student> result = baseDao.getScanPageResult(Student.class, exp); System.out.println("Result size ===>" + result.getResults().size()); System.out.println("Last evaluated key ===>" + result.getLastEvaluatedKey()); } }
Dao層
import com.amazonaws.regions.Regions; import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder; import com.amazonaws.services.dynamodbv2.datamodeling.*; import org.springframework.stereotype.Repository; import java.util.List; @Repository public class BaseDao { static Regions region = Regions.CN_NORTHWEST_1; static AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard() .withRegion(region).build(); static DynamoDBMapper dynamoDBMapper = new DynamoDBMapper(client); /** * 插入數據 */ public void saveItem(Object clazz) { try { dynamoDBMapper.save(clazz); } catch (Exception e) { System.err.println("插入失敗:" + e.getMessage()); } } /** * 更新數據 */ public void updateItem(Object clazz) { try { dynamoDBMapper.save(clazz); } catch (Exception e) { System.err.println("更新失敗:" + e.getMessage()); } } /** * 刪除數據 */ public void deleteItem(Object clazz) { try { dynamoDBMapper.delete(clazz); } catch (Exception e) { System.err.println("刪除失敗:" + e.getMessage()); } } /** * Query * 根據分區鍵查詢 */ public Object getQueryResult(Class<?> clazz, String pk) { return dynamoDBMapper.load(clazz, pk); } /** * Query * 根據分區鍵、排序鍵查詢 */ public <T> List<T> getQueryExpResult(Class<?> clazz, DynamoDBQueryExpression queryExp) { return dynamoDBMapper.query(clazz, queryExp); } /** * Query 分頁查詢 */ public <T> QueryResultPage getQueryPageExpResult(Class<T> clazz, DynamoDBQueryExpression queryExp) { return dynamoDBMapper.queryPage(clazz, queryExp); } /** * Scan 查詢 */ public List<?> getScanResult(Class<?> clazz, DynamoDBScanExpression scanExp) { return dynamoDBMapper.scan(clazz, scanExp); } /** * Scan 查詢 分頁查詢 */ public <T> ScanResultPage getScanPageResult(Class<T> clazz, DynamoDBScanExpression scanExp) { return dynamoDBMapper.scanPage(clazz, scanExp); } }