前言
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 com.systron.common.dao.BaseDao; import com.systron.models.Student; import com.systron.models.Teacher; 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); } }