Spring Data和MongoDB的首次实验非常棒。现在我有了以下结构(简化):
public class Letter {
@Id
private String id;
private List<Section> sections;
}
public class Section {
private String id;
private String content;
}
加载和保存整个Letter对象/文档就像魅力一样。 (我使用ObjectId为Section.id字段生成唯一ID。)
Letter letter1 = mongoTemplate.findById(id, Letter.class)
mongoTemplate.insert(letter2);
mongoTemplate.save(letter3);
由于文档很大(200K),有时应用程序只需要子部分:是否有可能查询子文档(部分),修改并保存它? 我想实现像
这样的方法Section s = findLetterSection(letterId, sectionId);
s.setText("blubb");
replaceLetterSection(letterId, sectionId, s);
当然还有以下方法:
addLetterSection(letterId, s); // add after last section
insertLetterSection(letterId, sectionId, s); // insert before given section
deleteLetterSection(letterId, sectionId); // delete given section
我看到最后三种方法有些“奇怪”,即从面向对象的角度来看,加载整个文档,修改集合并再次保存它可能是更好的方法;但第一个用例(“导航”到子文档/子对象并在此对象的范围内工作)似乎很自然。
我认为MongoDB可以更新子文档,但SpringData可以用于对象映射吗?谢谢你的任何指示。
答案 0 :(得分:15)
我想出了以下方法来切片和加载一个子对象。看起来好吗?我知道并发修改的问题。
Query query1 = Query.query(Criteria.where("_id").is(instance));
query1.fields().include("sections._id");
LetterInstance letter1 = mongoTemplate.findOne(query1, LetterInstance.class);
LetterSection emptySection = letter1.findSectionById(sectionId);
int index = letter1.getSections().indexOf(emptySection);
Query query2 = Query.query(Criteria.where("_id").is(instance));
query2.fields().include("sections").slice("sections", index, 1);
LetterInstance letter2 = mongoTemplate.findOne(query2, LetterInstance.class);
LetterSection section = letter2.getSections().get(0);
这是加载所有部分的替代解决方案,但省略其他(大)字段。
Query query = Query.query(Criteria.where("_id").is(instance));
query.fields().include("sections");
LetterInstance letter = mongoTemplate.findOne(query, LetterInstance.class);
LetterSection section = letter.findSectionById(sectionId);
这是我用来存储单个集合元素的代码:
MongoConverter converter = mongoTemplate.getConverter();
DBObject newSectionRec = (DBObject)converter.convertToMongoType(newSection);
Query query = Query.query(Criteria.where("_id").is(instance).and("sections._id").is(new ObjectId(newSection.getSectionId())));
Update update = new Update().set("sections.$", newSectionRec);
mongoTemplate.updateFirst(query, update, LetterInstance.class);
很高兴看到Spring Data如何与MongoDB的“部分结果”一起使用。
任何评论都非常感谢!
答案 1 :(得分:1)
我认为Matthias Wuttke的答案很棒,对于那些寻找他的答案的通用版本的人,请参阅下面的代码:
SQL> CREATE TABLE test(
id number generated always as identity primary key,
item varchar2(100),
quantity int
);
/
SQL> INSERT INTO test(item,quantity) VALUES ('KA1',5);
SQL> INSERT INTO test(item,quantity) VALUES ('KA2',2);
SQL> CREATE OR REPLACE PROCEDURE XXI_MULTI_PR_REMOVE( I_ITEM varchar2 ) IS
BEGIN
DELETE TEST WHERE ITEM = I_ITEM;
END;
/
SQL> CREATE OR REPLACE TRIGGER MERG_DUP
BEFORE INSERT ON TEST
FOR EACH ROW
DECLARE
v_qty NUMBER;
BEGIN
BEGIN
SELECT SUM(NVL(QUANTITY,0)) INTO v_qty FROM TEST WHERE ITEM = :NEW.ITEM;
EXCEPTION WHEN OTHERS THEN v_qty := NULL;
END;
IF ( v_qty IS NOT NULL ) THEN
XXI_MULTI_PR_REMOVE(:NEW.ITEM);
:NEW.QUANTITY:=:NEW.QUANTITY+v_qty;
END IF;
END MERG_DUP;
/
SQL> INSERT INTO test(item,quantity) VALUES ('KA3',6);
SQL> COMMIT;
SQL> SELECT * FROM test;
例如可以使用
调用@Service
public class MongoUtils {
@Autowired
private MongoTemplate mongo;
public <D, N extends Domain> N findNestedDocument(Class<D> docClass, String collectionName, UUID outerId, UUID innerId,
Function<D, List<N>> collectionGetter) {
// get index of subdocument in array
Query query = new Query(Criteria.where("_id").is(outerId).and(collectionName + "._id").is(innerId));
query.fields().include(collectionName + "._id");
D obj = mongo.findOne(query, docClass);
if (obj == null) {
return null;
}
List<UUID> itemIds = collectionGetter.apply(obj).stream().map(N::getId).collect(Collectors.toList());
int index = itemIds.indexOf(innerId);
if (index == -1) {
return null;
}
// retrieve subdocument at index using slice operator
Query query2 = new Query(Criteria.where("_id").is(outerId).and(collectionName + "._id").is(innerId));
query2.fields().include(collectionName).slice(collectionName, index, 1);
D obj2 = mongo.findOne(query2, docClass);
if (obj2 == null) {
return null;
}
return collectionGetter.apply(obj2).get(0);
}
public void removeNestedDocument(UUID outerId, UUID innerId, String collectionName, Class<?> outerClass) {
Update update = new Update();
update.pull(collectionName, new Query(Criteria.where("_id").is(innerId)));
mongo.updateFirst(new Query(Criteria.where("_id").is(outerId)), update, outerClass);
}
}
mongoUtils.findNestedDocument(Shop.class, "items", shopId, itemId, Shop::getItems);
mongoUtils.removeNestedDocument(shopId, itemId, "items", Shop.class);
界面如下所示:
Domain
注意:如果嵌套文档的构造函数包含具有原始数据类型的元素,则嵌套文档的默认(空)构造函数(可能受到保护)非常重要,以便类可以实例化null arguments。