SpringでのJinqの概要
1. 前書き
Jinqは、Javaでデータベースをクエリするための直感的で便利なアプローチを提供します。 このチュートリアルでは、how to configure a Spring project to use Jinqとその機能のいくつかを簡単な例で説明します。
2. Mavenの依存関係
pom.xmlファイルにthe Jinq dependencyを追加する必要があります。
org.jinq
jinq-jpa
1.8.22
Springの場合、pom.xmlファイルにthe Spring ORM dependencyを追加します。
org.springframework
spring-orm
5.0.3.RELEASE
最後に、テストのために、H2インメモリデータベースを使用するので、pom.xmlファイルにthis dependencyも追加しましょう。
com.h2database
h2
1.4.196
3. Jinqを理解する
Jinqは、the Java Stream API.に内部的に基づいている流暢なAPIを公開することにより、より簡単で読みやすいデータベースクエリを作成するのに役立ちます。
モデルで車をフィルタリングする例を見てみましょう。
jinqDataProvider.streamAll(entityManager, Car.class)
.where(c -> c.getModel().equals(model))
.toList();
Jinq translates the above code snippet into a SQL query in an efficient wayであるため、この例の最後のクエリは次のようになります。
select c.* from car c where c.model=?
クエリの記述にプレーンテキストを使用せず、代わりにタイプセーフAPIを使用しているため、このアプローチではエラーが発生しにくくなります。
さらに、Jinqは、一般的で読みやすい式を使用することで、開発を高速化することを目指しています。
それでも、次に説明するように、使用できるタイプと操作の数にはいくつかの制限があります。
3.1. 制限事項
Jinq supports only the basic types in JPA and a concrete list of SQL functions.すべてのオブジェクトとメソッドを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 needs an EntityManager instance to get the persistence context.このチュートリアルでは、Hibernateによって提供されるEntityManagerでJinqを機能させるためのSpringの簡単なアプローチを紹介します。
4.1. リポジトリインターフェース
Spring uses the concept of repositories to manage entities.特定のモデルのCarを取得するメソッドがあるCarRepositoryインターフェースを見てみましょう。
public interface CarRepository {
Optional findByModel(String model);
}
4.2. 抽象ベースリポジトリ
次に、we’ll need a base repositoryを使用して、すべてのJinq機能を提供します。
public abstract class BaseJinqRepositoryImpl {
@Autowired
private JinqJPAStreamProvider jinqDataProvider;
@PersistenceContext
private EntityManager entityManager;
protected abstract Class entityType();
public JPAJinqStream stream() {
return streamOf(entityType());
}
protected JPAJinqStream streamOf(Class clazz) {
return jinqDataProvider.streamAll(entityManager, clazz);
}
}
4.3. リポジトリの実装
これで、Jinqに必要なのは、EntityManagerインスタンスとエンティティタイプクラスだけです。
定義したばかりのJinqベースリポジトリを使用したCarリポジトリの実装を見てみましょう。
@Repository
public class CarRepositoryImpl
extends BaseJinqRepositoryImpl implements CarRepository {
@Override
public Optional findByModel(String model) {
return stream()
.where(c -> c.getModel().equals(model))
.findFirst();
}
@Override
protected Class entityType() {
return Car.class;
}
}
4.4. JinqJPAStreamProviderの配線
JinqJPAStreamProviderインスタンスを配線するために、add the Jinq provider configuration:
@Configuration
public class JinqProviderConfiguration {
@Bean
@Autowired
JinqJPAStreamProvider jinqProvider(EntityManagerFactory emf) {
return new JinqJPAStreamProvider(emf);
}
}
4.5. Springアプリケーションの構成
最後のステップはconfigure our Spring application using Hibernate and our Jinq configuration.です。参照として、application.propertiesファイルを参照してください。このファイルでは、メモリ内のH2インスタンスをデータベースとして使用しています。
spring.datasource.url=jdbc:h2:~/jinq
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.hibernate.ddl-auto=create-drop
5. クエリガイド
Jinq provides many intuitive options to customize the final SQL query with select, where, joins and more.これらには、上記ですでに紹介したのと同じ制限があることに注意してください。
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 is able to resolve one-to-one and many-to-one relationships。
たとえば、製造元エンティティをCarに追加すると、次のようになります。
@Entity(name = "CAR")
public class Car {
//...
@OneToOne
@JoinColumn(name = "name")
public Manufacturer getManufacturer() {
return manufacturer;
}
}
そして、Carsのリストを持つManufacturerエンティティ:
@Entity(name = "MANUFACTURER")
public class Manufacturer {
// ...
@OneToMany(mappedBy = "model")
public List getCars() {
return cars;
}
}
これで、特定のモデルのManufacturerを取得できるようになりました。
Optional manufacturer = stream()
.where(c -> c.getModel().equals(model))
.select(c -> c.getManufacturer())
.findFirst();
予想どおり、このシナリオのJinq will use an inner join SQL clause:
select m.name, m.city from car c inner join manufacturer m on c.name=m.name where c.model=?
多対多の関係など、エンティティに対してより複雑な関係を実装するために、join句をより細かく制御する必要がある場合は、joinメソッドを使用できます。
List> list = streamOf(Manufacturer.class)
.join(m -> JinqStream.from(m.getCars()))
.toList()
最後に、joinメソッドの代わりにleftOuterJoinメソッドを使用して、左外部結合SQL句を使用できます。
5.4. 集計
これまでに紹介したすべての例では、toListメソッドまたはfindFirstメソッドのいずれかを使用して、Jinqでクエリの最終結果を返しています。
これらのメソッドに加えて、we also have access to other methods to aggregate results。
たとえば、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,、possibility to combine different aggregationsなどの集計メソッドも提供します。
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の利点とその主な機能のいくつかについても簡単に説明しました。
いつものように、ソースはover on GitHubで見つけることができます。