sql >> Databáze >  >> NoSQL >> MongoDB

Importujte data do MongoDB ze souboru JSON pomocí Java

1. Úvod

V tomto tutoriálu se naučíme, jak číst data JSON ze souborů a importovat je do MongoDB pomocí Spring Boot. To může být užitečné z mnoha důvodů:obnovení dat, hromadné vkládání nových dat nebo vkládání výchozích hodnot. MongoDB používá JSON interně ke strukturování svých dokumentů, takže přirozeně to budeme používat k ukládání importovatelných souborů. Jelikož se jedná o prostý text, má tato strategie také výhodu v tom, že je snadno komprimovatelná.

Navíc se naučíme, jak v případě potřeby ověřit naše vstupní soubory proti našim vlastním typům. Nakonec odhalíme rozhraní API, abychom je mohli používat za běhu naší webové aplikace.

2. Závislosti

Pojďme přidat tyto závislosti Spring Boot do našeho pom.xml :

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

Budeme také potřebovat spuštěnou instanci MongoDB, která vyžaduje správně nakonfigurovaný application.properties soubor.

3. Import řetězců JSON

Nejjednodušší způsob, jak importovat JSON do MongoDB, je převést jej na „org.bson.Document ” objekt jako první. Tato třída představuje obecný dokument MongoDB žádného specifického typu. Proto se nemusíme starat o vytváření úložišť pro všechny druhy objektů, které můžeme importovat.

Naše strategie bere JSON (ze souboru, zdroje nebo řetězce), převádí jej na dokument s a uloží je pomocí MongoTemplate . Dávkové operace obecně fungují lépe, protože počet zpátečních cest je snížen ve srovnání s vkládáním každého objektu samostatně.

A co je nejdůležitější, budeme uvažovat, že náš vstup má pouze jeden objekt JSON na zalomení řádku. Tímto způsobem můžeme snadno oddělovat naše objekty. Tyto funkce zapouzdříme do dvou tříd, které vytvoříme:ImportUtils a ImportJsonService . Začněme naší třídou služeb:

@Service
public class ImportJsonService {

    @Autowired
    private MongoTemplate mongo;
}

Dále přidáme metodu, která analyzuje řádky JSON do dokumentů:

private List<Document> generateMongoDocs(List<String> lines) {
    List<Document> docs = new ArrayList<>();
    for (String json : lines) {
        docs.add(Document.parse(json));
    }
    return docs;
}

Poté přidáme metodu, která vloží seznam Dokumentů objekty do požadované kolekce . Je také možné, že dávková operace částečně selže. V takovém případě můžeme zaškrtnutím příčiny vrátit počet vložených dokumentů z výjimky :

private int insertInto(String collection, List<Document> mongoDocs) {
    try {
        Collection<Document> inserts = mongo.insert(mongoDocs, collection);
        return inserts.size();
    } catch (DataIntegrityViolationException e) {
        if (e.getCause() instanceof MongoBulkWriteException) {
            return ((MongoBulkWriteException) e.getCause())
              .getWriteResult()
              .getInsertedCount();
        }
        return 0;
    }
}

Nakonec tyto metody zkombinujme. Ten převezme vstup a vrátí řetězec ukazující, kolik řádků bylo přečteno vs. počet úspěšně vložených:

public String importTo(String collection, List<String> jsonLines) {
    List<Document> mongoDocs = generateMongoDocs(jsonLines);
    int inserts = insertInto(collection, mongoDocs);
    return inserts + "/" + jsonLines.size();
}

4. Případy použití

Nyní, když jsme připraveni zpracovat vstup, můžeme vytvořit nějaké případy použití. Pojďme vytvořit ImportUtils třídy, aby nám s tím pomohla. Tato třída bude zodpovědná za převod vstupu na řádky JSON. Bude obsahovat pouze statické metody. Začněme tím, který slouží ke čtení jednoduchého řetězce :

public static List<String> lines(String json) {
    String[] split = json.split("[\\r\\n]+");
    return Arrays.asList(split);
}

Vzhledem k tomu, že jako oddělovač používáme zalomení řádků, regulární výraz funguje skvěle pro rozdělení řetězců na více řádků. Tento regulární výraz zpracovává zakončení řádků Unixu i Windows. Dále metoda pro převod souboru na seznam řetězců:

public static List<String> lines(File file) {
    return Files.readAllLines(file.toPath());
}

Podobně skončíme s metodou převodu zdroje classpath na seznam:

public static List<String> linesFromResource(String resource) {
    Resource input = new ClassPathResource(resource);
    Path path = input.getFile().toPath();
    return Files.readAllLines(path);
}

4.1. Importovat soubor během spouštění pomocí rozhraní CLI

V našem prvním případě použití implementujeme funkcionalitu pro import souboru pomocí argumentů aplikace. Využijeme výhod ApplicationRunner Spring Boot rozhraní, abyste to udělali při spouštění. Můžeme například číst parametry příkazového řádku pro definování souboru, který se má importovat:

@SpringBootApplication
public class SpringBootJsonConvertFileApplication implements ApplicationRunner {
    private static final String RESOURCE_PREFIX = "classpath:";

    @Autowired
    private ImportJsonService importService;

    public static void main(String ... args) {
        SpringApplication.run(SpringBootPersistenceApplication.class, args);
    }

    @Override
    public void run(ApplicationArguments args) {
        if (args.containsOption("import")) {
            String collection = args.getOptionValues("collection")
              .get(0);

            List<String> sources = args.getOptionValues("import");
            for (String source : sources) {
                List<String> jsonLines = new ArrayList<>();
                if (source.startsWith(RESOURCE_PREFIX)) {
                    String resource = source.substring(RESOURCE_PREFIX.length());
                    jsonLines = ImportUtils.linesFromResource(resource);
                } else {
                    jsonLines = ImportUtils.lines(new File(source));
                }
                
                String result = importService.importTo(collection, jsonLines);
                log.info(source + " - result: " + result);
            }
        }
    }
}

Pomocí getOptionValues() můžeme zpracovat jeden nebo více souborů. Tyto soubory mohou být buď z naší cesty ke třídě, nebo z našeho systému souborů. Rozlišujeme je pomocí RESOURCE_PREFIX . Každý argument začínající „classpath: “ se načte z naší složky zdrojů místo ze systému souborů. Poté budou všechny importovány do požadované kolekce .

Začněme používat naši aplikaci vytvořením souboru pod src/main/resources/data.json.log :

{"name":"Book A", "genre": "Comedy"}
{"name":"Book B", "genre": "Thriller"}
{"name":"Book C", "genre": "Drama"}

Po sestavení můžeme k jeho spuštění použít následující příklad (pro čitelnost přidány konce řádků). V našem příkladu budou importovány dva soubory, jeden z cesty ke třídě a jeden ze systému souborů:

java -cp target/spring-boot-persistence-mongodb/WEB-INF/lib/*:target/spring-boot-persistence-mongodb/WEB-INF/classes \
  -Djdk.tls.client.protocols=TLSv1.2 \
  com.baeldung.SpringBootPersistenceApplication \
  --import=classpath:data.json.log \
  --import=/tmp/data.json \
  --collection=books

4.2. Soubor JSON z nahrání HTTP POST

Pokud navíc vytvoříme REST Controller, budeme mít koncový bod pro nahrávání a import souborů JSON. K tomu budeme potřebovat MultipartFile parametr:

@RestController
@RequestMapping("/import-json")
public class ImportJsonController {
    @Autowired
    private ImportJsonService service;

    @PostMapping("/file/{collection}")
    public String postJsonFile(@RequestPart("parts") MultipartFile jsonStringsFile, @PathVariable String collection)  {
        List<String> jsonLines = ImportUtils.lines(jsonStringsFile);
        return service.importTo(collection, jsonLines);
    }
}

Nyní můžeme importovat soubory pomocí POST, jako je tento, kde „/tmp/data.json ” odkazuje na existující soubor:

curl -X POST http://localhost:8082/import-json/file/books -F "[email protected]/tmp/books.json"

4.3. Mapování JSON na konkrétní typ Java

Používali jsme pouze JSON, nevázaný na žádný typ, což je jedna z výhod práce s MongoDB. Nyní chceme náš vstup ověřit. V tomto případě přidejte ObjectMapper provedením této změny naší služby:

private <T> List<Document> generateMongoDocs(List<String> lines, Class<T> type) {
    ObjectMapper mapper = new ObjectMapper();

    List<Document> docs = new ArrayList<>();
    for (String json : lines) {
        if (type != null) {
            mapper.readValue(json, type);
        }
        docs.add(Document.parse(json));
    }
    return docs;
}

Tímto způsobem, pokud typ je zadán parametr, náš mapovač se pokusí analyzovat náš řetězec JSON jako tento typ. A ve výchozí konfiguraci vyvolá výjimku, pokud jsou přítomny nějaké neznámé vlastnosti. Zde je naše jednoduchá definice beanu pro práci s úložištěm MongoDB:

@Document("books")
public class Book {
    @Id
    private String id;
    private String name;
    private String genre;
    // getters and setters
}

A nyní, abychom mohli používat vylepšenou verzi našeho generátoru dokumentů, změňme také tuto metodu:

public String importTo(Class<?> type, List<String> jsonLines) {
    List<Document> mongoDocs = generateMongoDocs(jsonLines, type);
    String collection = type.getAnnotation(org.springframework.data.mongodb.core.mapping.Document.class)
      .value();
    int inserts = insertInto(collection, mongoDocs);
    return inserts + "/" + jsonLines.size();
}

Nyní místo předávání názvu kolekce předáváme Třídu . Předpokládáme, že obsahuje Dokument anotace, jak jsme použili v naší Knize , takže může získat název kolekce. Protože však anotace i Dokument třídy mají stejný název, musíme specifikovat celý balíček.


  1. mongoDB odlišné a kde ve stejném dotazu?

  2. seřadit podle délky řetězce v Mongodb/pymongo

  3. Jak používat šifrování k ochraně dat MongoDB

  4. Měly by dva moduly používat stejné připojení redis? (Pracuji s Flaskem)