有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java最佳实践:Quarkus中的MongoDB Panache实体一对一关系处理?

遗憾的是Quarkus Guide on using MongoDB with Panache没有提到处理一对多实体关系的最佳做法。注意:我希望对依赖子文档进行建模,但将其作为一个独立的实体。MongoDB站点在:Model One-to-Many Relationships with Document References中演示了此模式,因此您必须在存储库中查找链接到父ID的所有实体

Quarkus(Panache)目前是否提供了使查找更方便的方法

PS:似乎有一个open enhancement request from May 2020来自动获取引用的实体


共 (2) 个答案

  1. # 1 楼答案

    正如@loicmathieu所告知的,在quarkus中没有直接的方法来执行populate(),因为您可以在mongoose中执行。 若您设置了mongoose.set('debug', true),那个么您可以首先在控制台中签入它

    • find已对集合执行查询
    • ^对引用集合执行{}查询

    json文档示例:

    {
        "_id" : ObjectId("612f6c5c081d0796761ec2e4"),
        "content" : "cannot restart Mongodb even when memory space is available",
        "created_by" : ObjectId("612cb8608d22afb0e404adf6"),
        "tags" : [ 
            ObjectId("612f6c5c081d0796761ec2e2")
        ],
        "createdAt" : ISODate("2021-09-01T12:04:44.361Z"),
        "updatedAt" : ISODate("2021-09-10T05:30:51.384Z"),
        "__v" : 2,
        "likes" : [],
        "replies" : []
    }
    

    标记文档

    {
        "_id" : ObjectId("612f6c5c081d0796761ec2e2"),
        "name" : "mongodb",
        "created_by" : ObjectId("612cb8608d22afb0e404adf6"),
        "createdAt" : ISODate("2021-09-01T12:04:44.358Z"),
        "updatedAt" : ISODate("2021-09-01T12:04:44.358Z"),
        "__v" : 0
    }
    

    最后是用户文档

    {
        "_id" : ObjectId("612cb8608d22afb0e404adf6"),
        "username" : "silentsudo",
        "password" : "sosimplebcrypted",
        "name" : "Ashish",
        "__v" : 0
    }
    

    要获取所有文档,我将执行以下查询:

    exports.getRecentTweets = async () => {
        return Tweet.find()
            .populate('created_by', '_id name username mediaId')
            .populate('tags', '_id name')
            .sort({'createdAt': -1});
    }
    

    Mongoose以以下方式在内部执行此操作

    Mongoose: tweets.find({}, { sort: { createdAt: -1 }, projection: {} })
    Mongoose: users.find({ _id: { '$in': [ new ObjectId("612cb8608d22afb0e404adf6"), new ObjectId("612f57ca9ea5c5cabbd89b38"), new ObjectId("612f57ae9ea5c5cabbd89b32"), new ObjectId("6134cb55328b793a1dacb9c2"), new ObjectId("612cb86a8d22afb0e404adfc") ] }}, { skip: undefined, limit: undefined, perDocumentLimit: undefined, projection: { _id: 1, name: 1, username: 1, mediaId: 1 }})
    Mongoose: tags.find({ _id: { '$in': [ new ObjectId("6144d9ef02f6da33f39fe134"), new ObjectId("612f6c5c081d0796761ec2e2"), new ObjectId("612f6e9f081d0796761ec343"), new ObjectId("612dc4623c401072c8098a84"), new ObjectId("612f6cb1081d0796761ec2f1"), new ObjectId("612f6c72081d0796761ec2eb"), new ObjectId("612f6e5b081d0796761ec337"), new ObjectId("612f6e42081d0796761ec330"), new ObjectId("612f6df9081d0796761ec320"), new ObjectId("612f6e0a081d0796761ec328"), new ObjectId("612f6de8081d0796761ec316"), new ObjectId("612f6de8081d0796761ec319"), new ObjectId("612f6dc9081d0796761ec30c"), new ObjectId("612f6dc9081d0796761ec30f"), new ObjectId("612f6db0081d0796761ec303"), new ObjectId("612f6db0081d0796761ec306"), new ObjectId("612f6cc7081d0796761ec2f7") ] }}, { skip: undefined, limit: undefined, perDocumentLimit: undefined, projection: { _id: 1, name: 1 }})
    
    

    我在试验基准测试。所以我决定使用quarkus,因为我以前从未使用它构建任何生产应用程序。所以我很快创建了这个模型,到达这个线程时才知道并没有直接的方法来实现基于java的驱动程序中的填充。但是我们可以使用$lookup和我们需要的聚合管道。第一种也是最简单的方法是使用mongodb compass工具构建查询,然后简单地用java语言导出结果。我的json查询生成类似于填充查询的输出,如下所示

    [
      {
        '$unwind': {
          'path': '$tags', 
          'preserveNullAndEmptyArrays': true
        }
      }, {
        '$lookup': {
          'from': 'tags', 
          'localField': 'tags', 
          'foreignField': '_id', 
          'as': 'tweet_tags'
        }
      }, {
        '$unwind': {
          'path': '$tweet_tags', 
          'preserveNullAndEmptyArrays': true
        }
      }, {
        '$group': {
          '_id': '$_id', 
          'content': {
            '$first': '$content'
          }, 
          'user': {
            '$first': '$created_by'
          }, 
          'tags': {
            '$push': '$tweet_tags'
          }
        }
      }, {
        '$lookup': {
          'from': 'users', 
          'localField': 'user', 
          'foreignField': '_id', 
          'as': 'createdByUser'
        }
      }, {
        '$unwind': {
          'path': '$createdByUser', 
          'preserveNullAndEmptyArrays': false
        }
      }, {
        '$set': {
          'createdByUser.password': null
        }
      }
    ]
    

    当转换成等价的java时

    public static List<Document> getTweetByUserQuery() {
                return Arrays.asList(new Document("$unwind",
                                new Document("path", "$tags")
                                        .append("preserveNullAndEmptyArrays", true)),
                        new Document("$lookup",
                                new Document("from", "tags")
                                        .append("localField", "tags")
                                        .append("foreignField", "_id")
                                        .append("as", "tweet_tags")),
                        new Document("$unwind",
                                new Document("path", "$tweet_tags")
                                        .append("preserveNullAndEmptyArrays", true)),
                        new Document("$group",
                                new Document("_id", "$_id")
                                        .append("content",
                                                new Document("$first", "$content"))
                                        .append("user",
                                                new Document("$first", "$created_by"))
                                        .append("tags",
                                                new Document("$push", "$tweet_tags"))),
                        new Document("$lookup",
                                new Document("from", "users")
                                        .append("localField", "user")
                                        .append("foreignField", "_id")
                                        .append("as", "createdByUser")),
                        new Document("$unwind",
                                new Document("path", "$createdByUser")
                                        .append("preserveNullAndEmptyArrays", false)),
                        new Document("$set",
                                new Document("createdByUser.password",
                                        new BsonNull())));
            }
    

    quarkus控制器代码如下:

    @Path("/hello")
    public class ExampleResource {
        @Inject
        private MongoClient mongoClient;
    
        @GET
        @Produces(MediaType.APPLICATION_JSON)
        public Map<Object, Object> hello() {
            MongoCollection<Tweet> tweetsCollection = mongoClient.getDatabase("simple-node-js")
                    .getCollection("tweets", Tweet.class);
    
    
            List<Document> groupByQueryUser = Tweet.GroupByIdDetails.getTweetByUserQuery();
    
            MongoCollection<Document> collections = mongoClient.getDatabase("simple-node-js")
                    .getCollection("tweets");
            AggregateIterable<Document> aggregate = collections.aggregate(groupByQueryUser);
            List<Document> data = new ArrayList<>();
            for (Document value : aggregate) {
                System.out.println(value);
                data.add(value);
            }
    
            return Map.of("size", data.size(), "data", data);
        }
    
    
    }
    

    对于响应,还有很多其他的事情需要做,比如删除不需要的字段,但是通过这种方式,我可以在node+MongooseAPI上获得相同的结果(除了我在这里跳过的其他一些事情)

  2. # 2 楼答案

    MongoDB是一个非关系数据库,这就是为什么它没有一对多关系支持的原因

    I would like to model the dependent sub-document,

    我没有注意到,子文档是受支持的,您只需创建一个单独的POJO并将其用作实体的属性

    不受支持的是,自动将引用的实体从一个集合加载到另一个集合,正如您指出的,有一个开放的it支持增强请求,您可以对此提供反馈(或通过+1投票支持)

    目前,对于引用的实体,您需要创建两个实体/存储库,并手动创建fecth