春のジンク入門
1. 序章
Jinq は、Javaでデータベースを照会するための直感的で便利なアプローチを提供します。 このチュートリアルでは、 Jinqを使用するようにSpringプロジェクトを構成する方法と、簡単な例で示されているその機能のいくつかについて説明します。
2. Mavenの依存関係
pom.xmlファイルにJinq依存関係を追加する必要があります。
<dependency>
<groupId>org.jinq</groupId>
<artifactId>jinq-jpa</artifactId>
<version>1.8.22</version>
</dependency>
Springの場合、pom.xmlファイルにSpringORM依存関係を追加します。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>5.3.3</version>
</dependency>
最後に、テストにはH2インメモリデータベースを使用するので、この依存関係と spring-boot-starter-data-jpaも追加します。 pom.xmlファイル:
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.200</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>2.6.1</version>
</dependency>
3. Jinqを理解する
Jinqは、 Java Stream APIに内部的に基づいている流暢なAPIを公開することにより、より簡単で読みやすいデータベースクエリを作成するのに役立ちます。
モデルごとに車をフィルタリングする例を見てみましょう。
jinqDataProvider.streamAll(entityManager, Car.class)
.where(c -> c.getModel().equals(model))
.toList();
Jinqは、上記のコードスニペットを効率的な方法でSQLクエリに変換します。したがって、この例の最終的なクエリは次のようになります。
select c.* from car c where c.model=?
クエリの記述にプレーンテキストを使用せず、代わりにタイプセーフAPIを使用しているため、このアプローチではエラーが発生しにくくなります。
さらに、Jinqは、一般的で読みやすい表現を使用することにより、より迅速な開発を可能にすることを目指しています。
それでも、次に説明するように、使用できるタイプと操作の数にはいくつかの制限があります。
3.1. 制限事項
Jinqは、JPAの基本型とSQL関数の具体的なリストのみをサポートします。すべてのオブジェクトとメソッドをJPAデータ型とSQL関数にマッピングすることにより、ラムダ操作をネイティブSQLクエリに変換することで機能します。
したがって、ツールがすべてのカスタムタイプまたはタイプのすべてのメソッドを変換することを期待することはできません。
3.2. サポートされているデータ型
サポートされているデータ型とサポートされているメソッドを見てみましょう。
- String – equals()、 compareTo()メソッドのみ
- プリミティブデータ型–算術演算
- Enums およびカスタムクラス– ==および!=操作のみをサポート
- java.util.Collection – contains()
- Date API – equals()、 before()、 after()メソッドのみ
注:Javaオブジェクトからデータベースオブジェクトへの変換をカスタマイズする場合は、AttributeConverterの具体的な実装をJinqに登録する必要があります。
4. JinqとSpringの統合
Jinqは、永続コンテキストを取得するためにEntityManagerインスタンスを必要とします。このチュートリアルでは、が提供する EntityManager でJinqを機能させるために、Springを使用した簡単なアプローチを紹介します。 Hibernate。
4.1. リポジトリインターフェース
Springは、リポジトリの概念を使用してエンティティを管理します。特定のモデルのCarを取得するメソッドがあるCarRepositoryインターフェイスを見てみましょう。
public interface CarRepository {
Optional<Car> findByModel(String model);
}
4.2. 抽象ベースリポジトリ
次に、すべてのJinq機能を提供するためのベースリポジトリが必要です。
public abstract class BaseJinqRepositoryImpl<T> {
@Autowired
private JinqJPAStreamProvider jinqDataProvider;
@PersistenceContext
private EntityManager entityManager;
protected abstract Class<T> entityType();
public JPAJinqStream<T> stream() {
return streamOf(entityType());
}
protected <U> JPAJinqStream<U> streamOf(Class<U> clazz) {
return jinqDataProvider.streamAll(entityManager, clazz);
}
}
4.3. リポジトリの実装
これで、Jinqに必要なのは、EntityManagerインスタンスとエンティティタイプクラスだけです。
定義したJinqベースリポジトリを使用したCarリポジトリの実装を見てみましょう。
@Repository
public class CarRepositoryImpl
extends BaseJinqRepositoryImpl<Car> implements CarRepository {
@Override
public Optional<Car> findByModel(String model) {
return stream()
.where(c -> c.getModel().equals(model))
.findFirst();
}
@Override
protected Class<Car> entityType() {
return Car.class;
}
}
4.4. JinqJPAStreamProviderの配線
JinqJPAStreamProvider インスタンスを配線するために、 Jinqプロバイダー構成を追加します:
@Configuration
public class JinqProviderConfiguration {
@Bean
@Autowired
JinqJPAStreamProvider jinqProvider(EntityManagerFactory emf) {
return new JinqJPAStreamProvider(emf);
}
}
4.5. Springアプリケーションの構成
最後のステップは
spring.datasource.url=jdbc:h2:~/jinq
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.hibernate.ddl-auto=create-drop
5. クエリガイド
Jinqは、select、where、joinsなどを使用して最終的なSQLクエリをカスタマイズするための多くの直感的なオプションを提供します。これらには、上記ですでに紹介したのと同じ制限があることに注意してください。
5.1. どこ
where 句を使用すると、データコレクションに複数のフィルターを適用できます。
次の例では、モデルと説明で車をフィルタリングします。
stream()
.where(c -> c.getModel().equals(model)
&& c.getDescription().contains(desc))
.toList();
そして、これはJinqが変換するSQLです。
select c.model, c.description from car c where c.model=? and locate(?, c.description)>0
5.2. 選択する
データベースから少数の列/フィールドのみを取得する場合は、select句を使用する必要があります。
複数の値をマップするために、Jinqは最大8つの値を持つ多数のTupleクラスを提供します。
stream()
.select(c -> new Tuple3<>(c.getModel(), c.getYear(), c.getEngine()))
.toList()
そして翻訳されたSQL:
select c.model, c.year, c.engine from car c
5.3. 参加する
Jinqは、エンティティが適切にリンクされている場合、1対1および多対1の関係を解決できます。
たとえば、Carに製造元エンティティを追加すると次のようになります。
@Entity(name = "CAR")
public class Car {
//...
@OneToOne
@JoinColumn(name = "name")
public Manufacturer getManufacturer() {
return manufacturer;
}
}
そして、 ManufacturerエンティティとCarのリスト:
@Entity(name = "MANUFACTURER")
public class Manufacturer {
// ...
@OneToMany(mappedBy = "model")
public List<Car> getCars() {
return cars;
}
}
これで、特定のモデルのManufacturerを取得できるようになりました。
Optional<Manufacturer> manufacturer = stream()
.where(c -> c.getModel().equals(model))
.select(c -> c.getManufacturer())
.findFirst();
予想どおり、このシナリオではJinqは内部結合SQL句を使用します。
select m.name, m.city from car c inner join manufacturer m on c.name=m.name where c.model=?
多対多の関係など、エンティティに対してより複雑な関係を実装するために join 句をより詳細に制御する必要がある場合は、joinメソッドを使用できます。 :
List<Pair<Manufacturer, Car>> list = streamOf(Manufacturer.class)
.join(m -> JinqStream.from(m.getCars()))
.toList()
最後に、joinメソッドの代わりにleftOuterJoinメソッドを使用して、左外部結合SQL句を使用できます。
5.4. 集合体
これまでに紹介したすべての例では、toListまたはfindFirstメソッドのいずれかを使用して、Jinqでクエリの最終結果を返しています。
これらのメソッドに加えて、結果を集約するための他のメソッドにもアクセスできます。
たとえば、 count メソッドを使用して、データベース内の具体的なモデルの車の総数を取得してみましょう。
long total = stream()
.where(c -> c.getModel().equals(model))
.count()
そして、最終的なSQLは、期待どおりに countSQLメソッドを使用しています。
select count(c.model) from car c where c.model=?
Jinqは、 sum 、 average 、 min 、 max、、さまざまな集計を組み合わせる可能性などの集計方法も提供します。 ]。
5.5. ページ付け
データをバッチで読み取りたい場合は、limitおよびskipメソッドを使用できます。
最初の10台の車をスキップして、20個のアイテムのみを取得する例を見てみましょう。
stream()
.skip(10)
.limit(20)
.toList()
そして、生成されたSQLは次のとおりです。
select c.* from car c limit ? offset ?
6. 結論
そこに行きます。 この記事では、Hibernateを使用して(最低限)JinqでSpringアプリケーションをセットアップするためのアプローチを見てきました。
また、Jinqの利点とその主な機能のいくつかについても簡単に説明しました。
いつものように、ソースはGitHubでにあります。