我在数据库中存在一个集合“ documentDev”,其分片键为“ dNumber” 样本文档:
{
"_id" : "12831221wadaee23",
"dNumber" : "115",
"processed": false
}
如果我尝试使用-
之类的命令通过任何查询工具来更新此文档,db.documentDev.update({
"_id" : ObjectId("12831221wadaee23"),
"dNumber":"115"
},{
$set:{"processed": true}},
{ multi: false, upsert: false}
)}`
它将正确更新文档。 但是如果我确实使用spring boot的mongorepository命令,例如 DocumentRepo.save(Object) 会引发异常
- 由com.mongodb.MongoCommandException引起:命令失败,错误61:服务器上的查询“命令中的查询必须以单个分片键为目标” by3prdddc01-docdb-3.documents.azure.com:10255。完整的响应为{“ _t”:“ OKMongoResponse”,“ ok”:0,“代码”:61,“ errmsg”:“命令中的查询必须以单个分片键为目标”,“ $ err”:“命令中的查询必须以单个分片键为目标”}
这是我的DocumentObject:
@Document(collection = "documentDev")
public class DocumentDev
{
@Id
private String id;
private String dNumber;
private String fileName;
private boolean processed;
}
这是我的存储库类-
@Repository
public interface DocumentRepo extends MongoRepository<DocumentDev,
String> { }
和我试图更新的价值
- 值:doc: { “ _id”:“ 12831221wadaee23”, “ dNumber”:“ 115”, “已处理”:是 }
我要执行的功能:
@Autowired
DocumentRepo docRepo;
docRepo.save(doc); // Fails to execute
注意:我在dNumber字段上启用了分片。而且我能够在NoSQL Tool上使用本机查询成功进行更新。 我还能够对非分片集合执行存储库保存操作。
更新:我可以通过使用MongoTemplate创建本机查询来更新文档-我的查询如下所示-
public DocumentDev updateProcessedFlag(DocumentDev request) {
Query query = new Query();
query.addCriteria(Criteria.where("_id").is(request.getId()));
query.addCriteria(Criteria.where("dNumber").is(request.getDNumber()));
Update update = new Update();
update.set("processed", request.isProcessed());
mongoTemplate.updateFirst(query, update, request.getClass());
return request;
}
但这不是通用的解决方案,因为任何其他字段可能都已更新,而我的文档也可能具有其他字段。
答案 0 :(得分:0)
我遇到了同样的问题,并通过以下hack解决了该问题:
@Configuration
public class ReactiveMongoConfig {
@Bean
public ReactiveMongoTemplate reactiveMongoTemplate(ReactiveMongoDatabaseFactory reactiveMongoDatabaseFactory,
MongoConverter converter,
MyService service) {
return new ReactiveMongoTemplate(reactiveMongoDatabaseFactory, converter) {
@Override
protected Mono<UpdateResult> doUpdate(String collectionName, Query query, UpdateDefinition update, Class<?> entityClass, boolean upsert, boolean multi) {
query.addCriteria(new Criteria("shardKey").is(service.getShardKey()));
return super.doUpdate(collectionName, query, update, entityClass, upsert, multi);
}
};
}
}
最好有一个@ShardKey注释将文档字段标记为分片,并将其添加到自动查询中。
答案 1 :(得分:0)
我通过创建自定义存储库来对此进行入侵:
public interface CosmosCustomRepository<T> {
void customSave(T entity);
void customSave(T entity, String collectionName);
}
此存储库的实现
public class CosmosCustomRepositoryImpl<T> implements CosmosCustomRepository<T> {
@Autowired
private MongoTemplate mongoTemplate;
@Override
public void customSave(T entity) {
mongoTemplate.upsert(createQuery(entity), createUpdate(entity), entity.getClass());
}
@Override
public void customSave(T entity, String collectionName) {
mongoTemplate.upsert(createQuery(entity), createUpdate(entity), collectionName);
}
private Update createUpdate(T entity) {
Update update = new Update();
for (Field field : entity.getClass().getDeclaredFields()) {
try {
field.setAccessible(true);
if (field.get(entity) != null) {
update.set(field.getName(), field.get(entity));
}
} catch (IllegalArgumentException | IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return update;
}
private Query createQuery(T entity) {
Criteria criteria = new Criteria();
for (Field field : entity.getClass().getDeclaredFields()) {
try {
field.setAccessible(true);
if (field.get(entity) != null) {
if (field.getName().equals("id")) {
return new Query(Criteria.where("id").is(field.get(entity)));
}
criteria.and(field.getName()).is(field.get(entity));
}
} catch (IllegalArgumentException | IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return new Query(criteria);
}
}
您的DocumentRepo
将扩展这个新的自定义存储库。
@Repository
public interface DocumentRepo extends MongoRepository<DocumentDev, String>, CosmosCustomRepository<DocumentDev> { }
要保存新文档,只需使用新的customSave
@Autowired
DocumentRepo docRepo;
docRepo.customSave(doc);
答案 2 :(得分:0)
在使用自定义存储库方法之后,我遇到了一个错误,因为spring希望自定义实现{EntityName} CustomRepositoryImpl中可以使用Cosmos实体,因此我重命名了实现。我还添加了以下代码:
将生成的ObjectId添加到新文档的实体中
public class DocumentRepositoryImpl<T> implements CosmosRepositoryCustom<T> {
@Autowired
protected MongoTemplate mongoTemplate;
@Override
public T customSave(T entity) {
WriteResult writeResult = mongoTemplate.upsert(createQuery(entity), createUpdate(entity), entity.getClass());
setIdForEntity(entity,writeResult);
return entity;
}
@Override
public T customSave(T entity, String collectionName) {
WriteResult writeResult = mongoTemplate.upsert(createQuery(entity), createUpdate(entity), collectionName);
setIdForEntity(entity,writeResult);
return entity;
}
@Override
public void customSave(List<T> entities) {
if(CollectionUtils.isNotEmpty(entities)){
entities.forEach(entity -> customSave(entity));
}
}
public <T> Update createUpdate(T entity){
Update update = new Update();
for (Field field : getAllFields(entity)) {
try {
field.setAccessible(true);
if (field.get(entity) != null) {
update.set(field.getName(), field.get(entity));
}
} catch (IllegalArgumentException | IllegalAccessException e) {
LOGGER.error("Error creating update for entity",e);
}
}
return update;
}
public <T> Query createQuery(T entity) {
Criteria criteria = new Criteria();
for (Field field : getAllFields(entity)) {
try {
field.setAccessible(true);
if (field.get(entity) != null) {
if (field.getName().equals("id")) {
Query query = new Query(Criteria.where("id").is(field.get(entity)));
query.addCriteria(new Criteria(SHARD_KEY_NAME).is(SHARD_KEY_VALUE));
return query;
}
criteria.and(field.getName()).is(field.get(entity));
}
} catch (IllegalArgumentException | IllegalAccessException e) {
LOGGER.error("Error creating query for entity",e);
}
}
return new Query(criteria);
}
private <T> List<Field> getAllFields(T entity) {
List<Field> fields = new ArrayList<>();
fields.addAll(Arrays.asList(entity.getClass().getDeclaredFields()));
Class<?> c = entity.getClass().getSuperclass();
if(!c.equals(Object.class)){
fields.addAll(Arrays.asList(c.getDeclaredFields()));
}
return fields;
}
public <T> void setIdForEntity(T entity,WriteResult writeResult){
if(null != writeResult && null != writeResult.getUpsertedId()){
Object upsertId = writeResult.getUpsertedId();
entity.setId(upsertId.toString());
}
}
}
我正在使用spring-boot-starter-mongodb:1.5.1和spring-data-mongodb:1.9.11