1。はじめに
このチュートリアルでは、ファイルからJSONデータを読み取り、SpringBootを使用してそれらをMongoDBにインポートする方法を学習します。 これは、データの復元、新しいデータの一括挿入、デフォルト値の挿入など、さまざまな理由で役立ちます。 MongoDBは内部でJSONを使用してドキュメントを構造化するため、当然のことながら、これを使用してインポート可能なファイルを保存します。プレーンテキストであるため、この戦略には簡単に圧縮できるという利点もあります。
さらに、必要に応じて、カスタムタイプに対して入力ファイルを検証する方法を学習します。 最後に、APIを公開して、実行時にWebアプリで使用できるようにします。
2。依存関係
これらのSpringBootの依存関係を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>
また、MongoDBの実行中のインスタンスも必要になります。これには、適切に構成された application.propertiesが必要です。 ファイル。
3。 JSON文字列のインポート
JSONをMongoDBにインポートする最も簡単な方法は、JSONを「 org.bson.Document」に変換することです。 」オブジェクトを最初に。 このクラスは、特定のタイプのない一般的なMongoDBドキュメントを表します。したがって、インポートする可能性のあるすべての種類のオブジェクトのリポジトリを作成することを心配する必要はありません。
私たちの戦略は、JSONを(ファイル、リソース、または文字列から)取得し、それをドキュメントに変換します s、 MongoTemplateを使用してそれらを保存します 。 各オブジェクトを個別に挿入する場合に比べてラウンドトリップの量が少なくなるため、バッチ操作のパフォーマンスは一般的に向上します。
最も重要なことは、入力が改行ごとに1つのJSONオブジェクトのみを持っていると見なすことです。そうすれば、オブジェクトを簡単に区切ることができます。これらの機能を、作成する2つのクラスにカプセル化します。 ImportUtils およびImportJsonService 。サービスクラスから始めましょう:
@Service
public class ImportJsonService {
@Autowired
private MongoTemplate mongo;
}
次に、JSONの行をドキュメントに解析するメソッドを追加しましょう:
private List<Document> generateMongoDocs(List<String> lines) {
List<Document> docs = new ArrayList<>();
for (String json : lines) {
docs.add(Document.parse(json));
}
return docs;
}
次に、ドキュメントのリストを挿入するメソッドを追加します オブジェクトを目的のコレクションに追加します 。また、バッチ操作が部分的に失敗する可能性があります。その場合、原因を確認することで、挿入されたドキュメントの数を返すことができます。 例外の :
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;
}
}
最後に、これらのメソッドを組み合わせてみましょう。これは入力を受け取り、読み取られた行数と正常に挿入された行数を示す文字列を返します。
public String importTo(String collection, List<String> jsonLines) {
List<Document> mongoDocs = generateMongoDocs(jsonLines);
int inserts = insertInto(collection, mongoDocs);
return inserts + "/" + jsonLines.size();
}
4。ユースケース
入力を処理する準備ができたので、いくつかのユースケースを作成できます。 ImportUtilsを作成しましょう それを手伝ってくれるクラス。 このクラスは、入力をJSONの行に変換する役割を果たします。 静的メソッドのみが含まれます。簡単な文字列を読むためのものから始めましょう :
public static List<String> lines(String json) {
String[] split = json.split("[\\r\\n]+");
return Arrays.asList(split);
}
区切り文字として改行を使用しているため、正規表現は文字列を複数の行に分割するのに最適です。この正規表現は、UnixとWindowsの両方の行末を処理します。次に、ファイルを文字列のリストに変換するメソッド:
public static List<String> lines(File file) {
return Files.readAllLines(file.toPath());
}
同様に、クラスパスリソースをリストに変換するメソッドで終了します。
public static List<String> linesFromResource(String resource) {
Resource input = new ClassPathResource(resource);
Path path = input.getFile().toPath();
return Files.readAllLines(path);
}
4.1。 CLIを使用した起動時にファイルをインポートする
最初のユースケースでは、アプリケーション引数を介してファイルをインポートするための機能を実装します。 Spring Boot ApplicationRunnerを利用します 起動時にこれを行うためのインターフェース。 たとえば、コマンドラインパラメータを読み取って、インポートするファイルを定義できます。
@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);
}
}
}
}
getOptionValues()の使用 1つ以上のファイルを処理できます。 これらのファイルは、クラスパスまたはファイルシステムのいずれかから取得できます。 RESOURCE_PREFIXを使用してそれらを区別します 。 「classpath:」で始まるすべての引数 」は、ファイルシステムからではなく、リソースフォルダから読み取られます。その後、それらはすべて目的のコレクションにインポートされます。 。
src / main / resources / data.json.log の下にファイルを作成して、アプリケーションの使用を開始しましょう。 :
{"name":"Book A", "genre": "Comedy"}
{"name":"Book B", "genre": "Thriller"}
{"name":"Book C", "genre": "Drama"}
ビルド後、次の例を使用して実行できます(読みやすくするために改行が追加されています)。この例では、2つのファイルがインポートされます。1つはクラスパスから、もう1つはファイルシステムからです。
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。 HTTPPOSTアップロードからのJSONファイル
さらに、RESTコントローラーを作成すると、JSONファイルをアップロードおよびインポートするためのエンドポイントがあります。そのためには、 MultipartFileが必要です。 パラメータ:
@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);
}
}
これで、次のようなPOSTを使用してファイルをインポートできます。ここで「/tmp/data.json」 」は既存のファイルを指します:
curl -X POST http://localhost:8082/import-json/file/books -F "[email protected]/tmp/books.json"
4.3。 JSONを特定のJavaタイプにマッピングする
私たちはJSONのみを使用しており、どのタイプにもバインドされていません。これは、MongoDBを使用する利点の1つです。 入力を検証します。 この場合、 ObjectMapperを追加しましょう。 私たちのサービスにこの変更を加えることによって:
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;
}
そうすれば、タイプ パラメータが指定されている場合、マッパー JSON文字列をそのタイプとして解析しようとします。 また、デフォルトの構成では、不明なプロパティが存在する場合は例外がスローされます。 MongoDBリポジトリを操作するための簡単なBean定義は次のとおりです。
@Document("books")
public class Book {
@Id
private String id;
private String name;
private String genre;
// getters and setters
}
そして今、私たちのドキュメントジェネレータの改良版を使用するために、このメソッドも変更しましょう:
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();
}
これで、コレクションの名前を渡す代わりに、クラスを渡します。 。 ドキュメントがあると想定しています 本で使用した注釈 、コレクション名を取得できるようにします。ただし、注釈とドキュメントの両方が クラスは同じ名前なので、パッケージ全体を指定する必要があります。