Jakarta NoSQLを試す(動かない)

なんかJakarta NoSQLというのが出ていて素敵そうだったので試してみた。
サポートしているデータベースがこんな感じで、このあたりを統一的に扱えると便利そう。
http://www.jnosql.org/docs/supported_dbs.html

NoSQLといっても色々あって単一APIでは難しいんでは、というのはそのとおりで、Key Value、Column、Document、Graphの4分類で扱う感じっぽい。

このGetting Startedに従ってやってみる。
http://www.jnosql.org/docs/document.html

結果としては、動かなかった。
けど、準備としては必要なことをやってるはずなので、メモとして残しておいて、今後正式リリースされてちゃんと動きそうになったらまた試してみる。

MongoDBを起動する

今回はMongoDBを試すけど、Dockerで動かします。

> docker run --name testmongo -d -p 27017:27017 mongo

試しに普通にJavaから接続する

まずは普通にJavaコードで。
dependencyにこういうの追加します。

<dependency>
    <groupId>org.mongodb</groupId>
    <artifactId>mongodb-driver-sync</artifactId>
    <version>4.8.1</version>
</dependency>

データつっこむやつ。

import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import org.bson.Document;

public class TryInsert {

    public static void main(String[] args) {
        
        var uri = "mongodb://localhost";
        try (MongoClient client = MongoClients.create(uri)) {
            var db = client.getDatabase("mydb");
            var coll = db.getCollection("mycol");
            var doc = Document.parse("""
                           {
                             id: 123,
                             name: "kishida",
                             phone: ["234", "123"],
                           }
                           """);
            coll.insertOne(doc);
        }
    }
}

データ読み込むやつ

import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;

public class TryRead {
    public static void main(String[] args){
        var uri = "mongodb://localhost";
        try (MongoClient client = MongoClients.create(uri)) {
            var db = client.getDatabase("mydb");
            var coll = db.getCollection("mycol");
            var ite = coll.find();
            for (var d : ite) {
                System.out.println(d);
            }
        }
    }
}

成功。よかった。

Document{{_id=63b68bfe6414fd518da18292, id=123, name=kishida, phone=[234, 123]}}

Jakarta NoSQLのための依存ライブラリ

ということでJNoSQLを試す。 まずはMongoDBはdocumentデータベースなので、次のようなmappingを追加

<dependency>
    <groupId>org.eclipse.jnosql.mapping</groupId>
    <artifactId>mapping-document</artifactId>
    <version>1.0.0-b4</version>
</dependency>

そしてMongoDBのためのドライバを追加

<dependency>
    <groupId>org.eclipse.jnosql.communication</groupId>
    <artifactId>mongodb-driver</artifactId>
    <version>1.0.0-b4</version>
</dependency>

CDIが必要なのでWeld SEを入れる

<dependency>
    <groupId>org.jboss.weld.se</groupId>
    <artifactId>weld-se-shaded</artifactId>
    <version>5.1.0.Final</version>
</dependency>
<dependency>
    <groupId>org.jboss.weld.se</groupId>
    <artifactId>weld-se</artifactId>
    <version>2.4.8.Final</version>
</dependency>

MicroProfile Configが必要なので入れる

<dependency>
    <groupId>org.eclipse.microprofile.config</groupId>
    <artifactId>microprofile-config-api</artifactId>
    <version>3.0.2</version>
</dependency>

あと、標準アノテーションも入れておく

<dependency>
    <groupId>javax.annotation</groupId>
    <artifactId>javax.annotation-api</artifactId>
    <version>1.3.2</version>
</dependency>

試してみる

CDIを使うので、resources/META-INF/beans.xmlに空のファイルを作っておく。

マッピング用のクラス。JPAっぽいけど違うアノテーションを使う。共通にしてほしい。

import jakarta.nosql.mapping.Column;
import jakarta.nosql.mapping.Entity;
import jakarta.nosql.mapping.Id;
import java.util.List;

@Entity("Person")
public class Person {
    @Id("id")
    long id;
    
    @Column
    String name;
    
    @Column("phone")
    List<String> phones;
}

こういうDocumentCollectionManagerをとってくるためのProducerを用意。getting startedではクラス名にミスがありそう。

import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.Produces;
import jakarta.nosql.Settings;
import jakarta.nosql.document.DocumentCollectionManager;
import jakarta.nosql.document.DocumentCollectionManagerFactory;
import jakarta.nosql.document.DocumentConfiguration;
import java.util.Map;
import org.eclipse.jnosql.communication.mongodb.document.MongoDBDocumentConfiguration;

@ApplicationScoped
public class DocumentCollectionManagerProducer {
    private static final String COLLECTION = "mycol";
    private DocumentConfiguration config;
    private DocumentCollectionManagerFactory manFact;
    
    @PostConstruct
    public void init() {
        config = new MongoDBDocumentConfiguration();
        Map<String, Object> settings = Map.of("mongodb-server-host-1", "localhost:27017");
        manFact = config.get(Settings.of(settings));
    }
    
    @Produces
    public DocumentCollectionManager getManager() {
        return manFact.get(COLLECTION);
    }
}

こんな感じらしい。

import jakarta.enterprise.inject.se.SeContainer;
import jakarta.enterprise.inject.se.SeContainerInitializer;
import jakarta.nosql.document.DocumentQuery;
import jakarta.nosql.mapping.document.DocumentTemplate;
import java.util.List;
import java.util.Optional;
import static jakarta.nosql.document.DocumentQuery.*;

public class App {
    public static void main(String[] args) {
        try (SeContainer cont = SeContainerInitializer.newInstance().initialize()) {
            Person p = new Person();
            p.name = "yamamoto";
            p.phones = List.of("321", "543");
            p.id = 918;
            
            var temp = cont.select(DocumentTemplate.class).get();
            temp.insert(p);
            
            DocumentQuery q = select().from("Person").where("id").eq(918).build();
            Optional<Person> result = temp.singleResult(q);
            System.out.println(result);
        }
    }
}

では動かしてみよう。

なんかDocumentTemplateがふたつあるといって怒られた・・・

Exception in thread "main" org.jboss.weld.exceptions.AmbiguousResolutionException: WELD-001335: Ambiguous dependencies for type DocumentTemplate with qualifiers 
 Possible dependencies: 
  - Managed Bean [class org.eclipse.jnosql.mapping.document.DefaultDocumentTemplate] with qualifiers [@Any @Default],
  - Managed Bean [class org.eclipse.jnosql.mapping.document.DefaultDocumentTemplateProducer$ProducerDocumentTemplate] with qualifiers [@Any @Default]

それでは、とリポジトリ使うやつを試す。 こんな感じにインタフェース作ると実装を勝手につくってくれる。

import jakarta.inject.Named;
import jakarta.nosql.mapping.Repository;
import java.util.List;
import java.util.stream.Stream;

public interface PersonRepository extends Repository<Person, Long> {
    List<Person> findByName(String name);
    Stream<Person> findByPhones(String phone);
}

そして動かす。

import jakarta.enterprise.inject.se.SeContainer;
import jakarta.enterprise.inject.se.SeContainerInitializer;
import java.util.List;
import org.eclipse.jnosql.mapping.DatabaseQualifier;

public class App {
    public static void main(String[] args) {
        try (SeContainer cont = SeContainerInitializer.newInstance().initialize()) {
            Person p = new Person();
            p.name = "yamamoto";
            p.phones = List.of("321", "543");
            p.id = 918;

            PersonRepository repo = cont.select(PersonRepository.class)
                    .select(DatabaseQualifier.ofDocument("mycol")).get();
            repo.save(p);
        }
    }
}

@DatabaseはQualifierではないと怒られた。

Exception in thread "main" org.jboss.weld.exceptions.IllegalArgumentException: WELD-000814: Annotation @jakarta.nosql.mapping.Database(value=DOCUMENT, provider="mycol") is not a qualifier

なんか設定でどうにかなる問題じゃなさそうなので、今日はあきらめ。
ちゃんと使えるなら使いやすそうなAPIなので、正式化は楽しみ。
しかしJava / Jakarta EEは難しい・・・