【问题标题】:Managing Mongodb connections in Java as Object Oriented在 Java 中以面向对象的方式管理 Mongodb 连接
【发布时间】:2020-02-12 02:13:52
【问题描述】:

如何使用多个类正确管理 mongo 连接?

例如,我有 4 个类管理 4 个集合。

Collection1.class Collection2.class 等等。

我所做的是在每个类中创建一个连接和关闭方法,这会减慢应用程序中某些事务的连接速度

除了创建每个类的对象并单独连接每个类之外,将应用程序连接到数据库一次并开始使用所有类实例的最佳方法是什么?

【问题讨论】:

  • 一个澄清问题:当您说“正确”时,您的意思是专门针对性能吗?您在问题标题中提到了 OO,但您的最后两句话似乎暗示您主要关心的是性能。请澄清,因为这些可能会导致完全不同的答案。
  • @sprinter 好吧,每个类实例都需要一个连接,所以我最终会得到 class1.connect class2.connect 等......所以我想创建一个持有连接的类以及数据库集合并继承自它但做不到......我想你明白我的意思吗?
  • 嗯,我明白了。将尝试回答。
  • 我发布了一个答案,其中包含一些额外的细节和一些 Java 代码。希望它对您的应用程序有用。

标签: java mongodb mongodb-java


【解决方案1】:

在应用程序中,具有所需连接数的单个MongoClient 对象,使用连接池,将在这种情况下工作。连接池默认值为100,可根据需要修改(或配置)。

mongo 客户端对象可以在应用程序启动时创建,只有在应用程序关闭时才会关闭。这节省了与每个集合访问类中的 mongo 客户端对象创建连接相关的资源。

可以在整个应用程序中使用相同的 mongo 客户端对象。 singleton 类(维护 mongo 客户端对象的一个​​实例)可以被应用程序中需要连接到 MongoDB 数据库服务器的任何其他对象访问。


What is connection pooling?

在软件工程中,连接池是数据库的缓存 保持连接,以便在连接时可以重用 未来对数据库的请求是必需的。连接池是 用于提高在数据库上执行命令的性能。 为每个用户打开和维护一个数据库连接, 尤其是对动态数据库驱动网站的请求 应用,成本高,浪费资源。在连接池中, 创建连接后,将其放入池中并使用 再次,这样就不必建立新的连接。我摔倒 正在使用连接,建立并添加新连接 到游泳池。连接池还减少了 用户必须等待建立与数据库的连接。


示例代码:

/*
 * Manages the MongoClient object and its settings like host, port, connection pool, etc.
 */
public class DBAccess {

  private static MongoClient mongoClient;
  private static DBAccess dbAccess;

  // MongoClient with default settings
  // NOTE: the code will have only one of the constructors
  //private DBAccess() {
  //    final String connectionString = "mongodb://localhost:27017";
  //    this.mongoClient = MongoClients.create(connectionString);
  //}

  // MongoClient with custom settings.
  // Private constructor, so that the class can be instantiated outside this class.
  // NOTE: the code will have only one of the constructors
  private DBAccess() {

      MongoClientSettings settings =
          MongoClientSettings.builder()
              .applyToConnectionPoolSettings(builder ->
                   builder.maxSize(40).minSize(10))
             .applyToClusterSettings(builder ->
                   builder.hosts(Arrays.asList(new ServerAddress("localhost", 27017))))
            .build();

      mongoClient = MongoClients.create(settings);
  }

  public static MongoClient getConnection() {

      if (dbAccess == null) {
           dbAccess = new DBAccess();   
      }

      return mongoClient;
  }

  public static void closeDatabase() {
      mongoClient.close();
  }
}

/*
 * Class manages a collection.
 */
public class CollectionOneAccess {

  public static String COLLECTION_ONE = "collection_one";
  private MongoCollection<Document> collection;

  public CollectionOneAccess(MongoDatabase db) {    
      collection = db.getCollection(COLLECTION_ONE);
  }

  public void printOneDocument() {
      Document myDoc = collection.find().first();
      System.out.println(myDoc.toJson());
  }

  // other CRUD operations ...

}


// Usage of DBAcess and CollectionOneAccess classes:

private static final String APP_DATABASE = "abc_db";

public static void main(String [] args) {
    MongoDatabase database = DBAccess.getConnection().getDatabase(APP_DATABASE);
    CollectionOneAccess one = new CollectionOneAccess(database);
    one.printOneDocument();
    // ...
}

Mongo 客户端

MongoClient 对象用于连接到 MongoDB 服务器,使用 getDatebase() 方法访问数据库并使用集合。

com.mongodb.client.MongoClient接口:

MongoDB 集群的客户端表示。实例可以 表示独立的 MongoDB 实例、副本集或 分片集群。这个类的实例负责 维护集群的最新状态,并可能缓存 与此相关的资源,包括后台线程 监控和连接池。

来自MongoDB Java documentation

MongoClient 实例代表一个数据库连接池;即使有多个线程,您也只需要一个 MongoClient 类的实例。

重要提示:通常您只为一个 MongoClient 实例创建一个 给定 MongoDB 部署(例如,独立、副本集或分片 集群)并在您的应用程序中使用它。但是,如果您确实创建 多个实例:

  • 所有资源使用限制(例如最大连接数等)均适用于每个 MongoClient 实例。
  • 要处理实例,请调用 MongoClient.close() 来清理资源。

以下代码使用默认设置创建 MongoDB 客户端连接对象,如主机(“localhost”)和端口(@98​​7654329@)、连接池等,并连接到 MongoDB 实例并访问 @ 987654330@数据库。

MongoClient mongoClient = MongoClients.create();
MongoDatabase database = mongoClient.getDatabase("testDB");

Mongo 客户端设置:

您可以使用MongoClientSettings 明确指定其他设置来控制MongoClient 的行为。

MongoClient mongoClient = MongoClients.create(MongoClientSettings settings)

ConnectionPoolSettings 对象指定与 MongoDB 服务器的连接池相关的所有设置。应用程序在创建客户端对象时创建此连接池。 ConnectionPoolSettings.BuilderConnectionPoolSettings 的构建器,具有指定连接池属性的方法。例如,maxSize​(int maxSize):允许的最大连接数(默认为100)。其他方法包括,minSizemaxConnectionIdleTime等。

使用连接池设置实例化 MongoClient 的代码:

MongoClientSettings settings = MongoClientSettings.builder()
                                   .applyToConnectionPoolSettings(builder -> 
                                       builder.maxSize(20))
                                   .build();
MongoClient mongoClient = MongoClients.create(settings);
// ...
// Verify the connection pool settings 
System.out.println("Pool size: " + 
    settings.getConnectionPoolSettings().getMaxSize());

【讨论】:

    【解决方案2】:

    您是正确的,每个类(代表 MongoDB 集合)不应该管理自己与数据库的连接。相反,您应该将数据库连接传递给类 - 通常在构造函数中。像这样的:

    class Animal {
        private String species;
        private String name;
        private int age;
    
        public Animal(DBObject dbObject) { ... }
    }
    
    class AnimalCollection {
        private final DBCollection collection;        
    
        public AnimalCollection(Database database) {
            collection = database.getCollection("animals");
        }
    
        public List<Animal> getAll() {
            List<Animal> animals 
            try (DBCursor cursor = collection.find(query)) {
                while (cursor.hasNext()) {
                    animals.add(new Animal(cursor.next());
                }
            }
            return animals;
        }
    }
    

    您创建所有集合的代码应该获取 MongoClient,连接到数据库并在退出时管理关闭连接。这样您就可以管理一个连接。

    所以管理集合的类可能如下所示:

    class CollectionManager implements AutoCloseable {
        private final Database database;
        private final AnimalCollection animals;
    
        public CollectionManager(MongoClient client) {
            database = client.getDB("Zoo");
            animals = new AnimalCollection(database);
        }
    
        @Override
        public void close() {
            database.close();
        }
    }
    

    让这个类扩展AutoCloseable 的原因是当您退出try-with-resources 块时会自动调用close。这将使您的代码更易于阅读且更安全。

    这种方法还有一个很大的优势。您可以通过在构造函数中传递一个模拟的 Database 来对您的类进行单元测试,并测试响应各种 DB 输出的行为,而无需包含任何数据的实际 DB。

    【讨论】:

    • 非常感谢,这正是我正在寻找的......所以基本上如果你可以确认使用单例设计模式进行连接,这样我就不需要每次都传递连接类对象...有了这个,我可以从集合类中创建尽可能多的对象,同时只维护一个连接类对象...正确吗??
    • No not a singleton - 这通常意味着所有集合类都可以访问的单个全局变量。我建议您将数据库对象传递给每个集合类。由于各种原因,这是更好的设计。我将在答案的末尾添加一些细节。
    • 好吧,太好了...但是我的意思是创建一个包含 2 个方法 connect 和 closedb 的类,并在应用程序中的任何位置使用单例传递它...将它应用到您的动物集合中将是 lil this : animalcollection(new connectionobject) 这将设法返回先前创建的连接实例 ...
    • 如果“单例”是指单个共享对象,那么可以。通常,该设计模式还意味着对对象的全局访问,而不需要将其传递给客户端。这通常是(尽管并非总是)糟糕的设计。
    • 好吧,这不是我对单例的理解......我认为这是一种确保在整个应用程序中只创建一个类的实例的设计......但是你为什么认为它是糟糕的设计?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-26
    • 1970-01-01
    • 2017-06-04
    • 2010-11-15
    • 1970-01-01
    • 2011-11-05
    相关资源
    最近更新 更多