MongoDB Java Driver 4.2.2 - java.lang.noSuchMethodError при связывании .iterator или .into с .find

Я пытаюсь создать собственное приложение и серверный API, чтобы лучше понять драйвер Java Mongo после прохождения курса M220J, но у меня возникает блокировщик.

Контекст

Я создаю приложение для управления закладками и перестраиваю серверную часть на Java, используя драйвер Mongo и Spring Boot. Ошибка появляется при использовании .iterator () или .into () после .find () для запроса моей базы данных Atlas. Ошибка НЕ ​​ЯВЛЯЕТСЯ при использовании .first () после .find ().

Мой pom.xml включает версию 4.2.2 mongodb-driver-sync. Я использую Java 15.0.1.

Коллекция папок использует CodecRegistry, во многом аналогично тому, как коллекция Users или Sessions создается в приложении mflix в курсе M220J Университета MongoDB, то есть запрос к folderCollection должен возвращать документы типа Folder. Я проверил, что данные имеют одинаковые поля в базе данных и классе Folder.java.

Код

В коде поле user_id в настоящее время хранится как String, а не ObjectId в документах папки (действует как внешний ключ). Это не вызывает ошибки. Уловка .into () была взята из В этой статье рассказывается о Mongo и Java Pojos.

Это моя текущая реализация, но это не работает.

  // Returns an array of folders, given a user id
  public List<Folder> getFolders(String id) {
    if (id == null || id.isEmpty()) return null;
    List<Folder> folders = foldersCollection.find(new Document("user_id", id)).into(new ArrayList<>());
    return folders;
  }

Этот код тоже не работает, который использует .iterator () после .find () и исключает идентификатор для получения всех папок. Это также не сработает, если я включу идентификатор в запрос.

  public List<Folder> getAllFolders(){
    List<Folder> folders = new ArrayList<>();
    foldersCollection.find().iterator().forEachRemaining(folders::add);
    return folders;
  }

Я проверил, что этот код работает с тестом на Java и в Postman:

  // Gets a folder from db given folder _id
  public Folder getFolder(String id) {
    if (id == null || id.isEmpty()) return null;
    return foldersCollection.find(new Document("_id", new ObjectId(id))).first();
  }

NoSuchMethodError:

Похоже, что он указывает на метод .find (), когда я использую .into (), но я подтвердил, что .find () работает в цепочке с .first (), поэтому я не думаю, что .find () является проблема. Такая же ошибка появляется при использовании .iterator ().

java.lang.NoSuchMethodError: 'com.mongodb.internal.operation.ExplainableReadOperation com.mongodb.internal.operation.SyncOperations.find(org.bson.conversions.Bson, java.lang.Class, com.mongodb.internal.client.model.FindOptions)'

    at com.mongodb.client.internal.FindIterableImpl.asReadOperation(FindIterableImpl.java:236)
    at com.mongodb.client.internal.FindIterableImpl.asReadOperation(FindIterableImpl.java:40)
    at com.mongodb.client.internal.MongoIterableImpl.execute(MongoIterableImpl.java:135)
    at com.mongodb.client.internal.MongoIterableImpl.iterator(MongoIterableImpl.java:92)
    at com.mongodb.client.internal.MongoIterableImpl.forEach(MongoIterableImpl.java:121)
    at com.mongodb.client.internal.MongoIterableImpl.into(MongoIterableImpl.java:130)
    at com.bookmarkd.api.daos.FolderDao.getFolders(FolderDao.java:46)
    at com.bookmarkd.FolderTest.GetFolders(FolderTest.java:50) <31 internal lines>
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1511) <9 internal lines>
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1511) <23 internal lines>

Наконец, вот мой класс Folder и изображение нескольких документов в базе данных Atlas:

public class Folder {

  @BsonId
  @JsonIgnore
  private ObjectId oid;

  @JsonProperty("_id")
  @BsonIgnore
  private String id;

  @JsonProperty("user_id")
  private String userId;

  private String name;
  private String icon;
  private boolean shareable;

  // Constructor, getters, and setters... 
}

Изображение нескольких папок с документами в папкеCollection

Приветствуется любая помощь в устранении этой проблемы!


Дополнительный код / ​​объяснение проблемы:

(Хотя это делает пост очень длинным, я подумал, что будет проще увидеть здесь весь код, чем просматривать его из репозитория Github.)

Возможно, я слишком усложняю ситуацию, но я пытаюсь изучить и использовать Spring Boot MVC. Есть FolderController, который обрабатывает запросы Http, который вызывает FolderService, который вызывает FolderDao, который применяет методы CRUD в кластере Atlas. Все эти файлы хранятся в соответствующих пакетах: контроллеры, службы, daos, модели и config.

FolderDao расширяет класс AbstractDao, который устанавливает для клиента и базы данных значения, полученные от конструктора @Autowired FolderDao.

Наконец, класс MongoDBConfiguration настраивает параметры и подключается к кластеру Atlas. Я настроил здесь PojoRegistry, как показано в этом примере. Я считаю, что этот класс действительно запускается сразу после запуска @SpringBootApplication в Application.java. Так это и было сделано в приложении mflix. Вы можете увидеть готовое приложение mflix здесь для справки.

Может ошибка исходит от Атласа? У меня бесплатный кластер M0 версии 4.4.4, но у меня не было проблем с прохождением курса M220J, где эта логика была реализована с помощью mflix.

Вот полный код для каждого класса в том порядке, в котором я его описал выше:

FolderController

package com.bookmarkd.api.controllers;

import com.bookmarkd.api.models.Folder;
import com.bookmarkd.api.services.FolderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@CrossOrigin(origins = "*")
@RequestMapping("/api/folders")
@RestController
public class FolderController {

  @Autowired
  FolderService folderService;

  public FolderController() { super(); }

  public void getFolders() {}

  // Get all folders, given the user id
  @GetMapping("")
  public ResponseEntity getFoldersByUserId(@RequestParam(value = "userid", required = false) String folderId) {
    List<Folder> folders = folderService.getFolders(folderId);
    if (folders == null) return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
    return ResponseEntity.status(HttpStatus.OK).body(folders);
  }

  // Get a single public folder, given the folder id
  @GetMapping("/{id}")
  public ResponseEntity getFolder(@PathVariable(value = "id") String folderId) {
    Folder folder = folderService.getFolder(folderId);
    if (folder == null) return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
    return ResponseEntity.status(HttpStatus.OK).body(folder);
  }

  public void addFolder() {}

  public void updateFolder() {}

  public void deleteFolder() {}
}

FolderService

package com.bookmarkd.api.services;

import com.bookmarkd.api.daos.FolderDao;
import com.bookmarkd.api.models.Folder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Service;

import java.util.List;

@Configuration
@Service
public class FolderService {

  @Autowired
  private FolderDao folderDao;

  public FolderService() {
    super();
  }

  public List<Folder> getFolders(String id) {
    return folderDao.getFolders(id);
  }

  public Folder getFolder(String id) {
    return folderDao.getFolder(id);
  }

  // Add folder, update folder, delete folder, to-be-implemented later...
}

Папка Дао

package com.bookmarkd.api.daos;

import com.bookmarkd.api.models.Folder;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoCollection;
import org.bson.Document;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

@Configuration
public class FolderDao extends AbstractDao {

  private final MongoCollection<Folder> foldersCollection;

  @Autowired
  public FolderDao(MongoClient mongoClient, @Value("${spring.mongodb.database}") String databaseName) {
    super(mongoClient, databaseName);

    foldersCollection = db.getCollection("folders", Folder.class);
  }

  // Returns an array of folders, given a user _id
  public List<Folder> getFolders(String id) {
    if (id == null || id.isEmpty()) return null;
    List<Folder> folders = foldersCollection.find(new Document("user_id", id)).into(new ArrayList<>());

//    List<Folder> folders = new ArrayList<>();
//    foldersCollection.find(new Document("user_id", id)); //.iterator().forEachRemaining(folders::add);

    return folders;
  }

  // Gets a folder from db given folder _id
  public Folder getFolder(String id) {
    if (id == null || id.isEmpty()) return null;
    return foldersCollection.find(new Document("_id", new ObjectId(id))).first();
  }
}

АннотацияDao

package com.bookmarkd.api.daos;

import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoDatabase;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;

@Configuration
public abstract class AbstractDao {

  protected String LBV_DATABASE;
  protected MongoDatabase db;
  protected MongoClient mongoClient;

  protected AbstractDao(MongoClient mongoClient, String databaseName) {

    this.mongoClient = mongoClient;
    LBV_DATABASE = databaseName;
    this.db = this.mongoClient.getDatabase(LBV_DATABASE);
  }
}

MongoDBConfiguration (здесь я регистрирую кодек Pojo, который работает для модели User и реализации folderCollection.find (). First ())

package com.bookmarkd.api.config;

import com.mongodb.ConnectionString;
import com.mongodb.MongoClientSettings;
import com.mongodb.WriteConcern;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import org.bson.codecs.configuration.CodecRegistry;
import org.bson.codecs.pojo.PojoCodecProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;

import static org.bson.codecs.configuration.CodecRegistries.fromProviders;
import static org.bson.codecs.configuration.CodecRegistries.fromRegistries;

// Used to configure connection to Atlas cluster
@Configuration
@Service
public class MongoDBConfiguration {

  @Bean
  @Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
  public MongoClient mongoClient(@Value("${spring.mongodb.uri}") String connectionString) {

    CodecRegistry pojoCodecRegistry = fromRegistries(
            MongoClientSettings.getDefaultCodecRegistry(),
            fromProviders(PojoCodecProvider.builder().automatic(true).build())
    );

    ConnectionString connString = new ConnectionString(connectionString);
    WriteConcern writeConcern = WriteConcern.MAJORITY; //.withWTimeout(2500, TimeUnit.MILLISECONDS);
    MongoClientSettings settings = MongoClientSettings.builder()
            .writeConcern(writeConcern)
            .applyConnectionString(connString)
            .codecRegistry(pojoCodecRegistry)
            .build();
    MongoClient mongoClient = MongoClients.create(settings);

    return mongoClient;
  }
}

Надеюсь, этот полный обзор всего кода даст вам представление о том, что происходит не так!

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
0
25
2

Ответы 2

Не совсем понятно, почему ваш код не работал (возможно, вы что-то упустили, а весь код, связанный с вашим приложением, отсутствует в сообщении).

Я пробовал этот простой и похожий пример, и он также использует класс POJO для сопоставления с документами коллекции MongoDB и использует кодеки. Я использую MongoDB v4.2.8, MongoDB Java Driver 4.2.0 и Java SE 8 (1.8.0_271).

В примере используется коллекция под названием movie со следующими образцами документов:

{ "_id" : ObjectId("607082bf20e9199b269d9268"), "name" : "Star Wars", "year" : 1977 }
{ "_id" : ObjectId("607082d020e9199b269d9269"), "name" : "Return of Jedi", "year" : 1983 }

В приведенном ниже коде я использовал оба метода find, которые вы успешно пробовали.

Класс POJO:

public class Movie {

    @BsonProperty("_id")
    private ObjectId id;
    private String name;
    private int year;

    // default constructor, get set methods, override toString()
    // ...
}

Метод main класса приложения:

CodecRegistry pojoCodecRegistry =
        fromRegistries(
                MongoClientSettings.getDefaultCodecRegistry(),
                fromProviders(PojoCodecProvider.builder().automatic(true).build()));

try(MongoClient client = MongoClients.create()) {

    MongoDatabase db = client.getDatabase("test");
    MongoCollection<Movie> movies =
            db.getCollection("movie", Movie.class)
                    .withCodecRegistry(pojoCodecRegistry);

    List<Movie> list1 = movies.find().into(new ArrayList<>());
    System.out.println(list1);

    List<Movie> list2 = new ArrayList<>();
    movies.find().forEach(list2::add);
    System.out.println(list2);
}

Спасибо за быстрый ответ prasad_ Я обновил свой пост с помощью кода FolderController, FolderService, FolderDao, AbstractDao и MongoDBConfiguration. Может быть, твои эльфийские глаза увидят то, что я упустил. На данный момент все еще возникает ошибка!

Ian 10.04.2021 00:30

Это также показывает, что версия mongo-db-driver отличается от версии, указанной в pom.xml. Может быть другой пакет, но он другой! репозиторий / org / mongodb / mongodb-driver-core / 4.1.2 / mongodb-dri‌ ver-core-4.1.2.jar! / ‌ com / mongodb / internal‌ / operation / SyncOpera‌ tions.class

Ian 10.04.2021 01:19

Оказывается, мне понадобились три зависимости, специфичные для Mongo, все одной версии.

Добавление org.mongo:bson:4.2.3 и org.mongo.mongodb-driver-code:4.2.3 к существующей зависимости org.mongodb-driver-sync:4.2.3 в pom.xml решает проблему (ранее у меня была версия 4.1.2 mongodb-driver-core и mongodb-driver-bson).

Ух ты, такой стресс, связанный только с проблемой зависимости! К сожалению, в документации было неясно, нужны ли все три, я думал, вам нужен только mongodb-driver-sync.

<?xml version = "1.0" encoding = "UTF-8"?>
<project xmlns = "http://maven.apache.org/POM/4.0.0" xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation = "http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.bookmarkd</groupId>
    <artifactId>LBV</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>LBV</name>
    <description>Bookmarkd Java REST API backend try 2</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.mongodb/mongodb-driver-sync -->
        <dependency>
            <groupId>org.mongodb</groupId>
            <artifactId>mongodb-driver-sync</artifactId>
            <version>4.2.3</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.mongodb/mongodb-driver-core -->
        <dependency>
            <groupId>org.mongodb</groupId>
            <artifactId>mongodb-driver-core</artifactId>
            <version>4.2.3</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.mongodb/bson -->
        <dependency>
            <groupId>org.mongodb</groupId>
            <artifactId>bson</artifactId>
            <version>4.2.3</version>
        </dependency>

    </dependencies>

<!--    <build>-->
<!--        <plugins>-->
<!--            <plugin>-->
<!--                <groupId>org.springframework.boot</groupId>-->
<!--                <artifactId>spring-boot-maven-plugin</artifactId>-->
<!--            </plugin>-->
<!--        </plugins>-->
<!--    </build>-->

</project>

Другие вопросы по теме