MyBatisストリーミングクエリの3つの実装方法
4444 ワード
ガイド:ストリーミングクエリとは、クエリが成功した後、集合に戻るのではなく、1つのサブコーダに戻り、その都度、サブコーダからクエリ結果を抽出することを意味します。ストリーミングクエリの利点は、メモリの使用を低減することです。
もしストリーミングクエリがないなら、私達はデータベースから1000万本の記録を取りたいですが、十分なメモリがない場合は、ページ別に調べなければなりません。ページ別の検索効率は表の設計によって決まります。もしデザインが良くないなら、効率的なページ別のクエリを実行することができません。したがって、ストリーミングクエリはデータベースアクセスフレームに必須の機能です。
ストリーミングクエリのプロセスでは、データベース接続はオープン状態を維持していますので、ストリーミングクエリを実行した後、データベースアクセスフレームはデータベース接続をオフにする責任がありません。データを取った後、自分でシャットダウンする必要があります。
MyBatisフロー式クエリインターフェース
MyBatisはorg.apphe.ibatis.cursor.Cursorというインターフェースクラスを提供しています。このインターフェースはjava.io.loseableとjava.lang.Iterableインターフェースを継承しています。 Cursorはオフできます。実際にCursorをオフにすると、データベース接続も一緒にオフになります。 Cursorは遍歴可能です。 この他に、Cursorは、3つの方法を提供する。 isOpen():データを取る前にCursorオブジェクトがオン状態であるかどうかを判断するためのもの。開けた時にのみCursorはデータを取ります。 isConsmed():クエリーの結果が全部取れたかどうかを判断するために使用されます。 get CurrenntIndex():いくつかのデータが取得されたかを返す。 Currsorは、ディケンサインターフェースを実現しているので、実際の使用では、Curerからデータを取るのは非常に簡単です。
しかし、Cursorを構築する過程は簡単ではない。
私たちは実際の例を挙げます。次はMapper類です。
その後、SpringMVC Controller方法を書いて、Mapperを呼び出します。
上のコードは問題ないようですが、scanFoo 0を実行するとエラーが発生します。
java.lang.Illagal StateException:A Currsor is already closed.
これは前に言ったように、データを取る過程でデータベース接続を維持する必要がありますが、Mapper方法は通常実行後に接続がオフになりますので、Cusorも一緒にオフになりました。
だから、この問題を解決する考えは複雑ではなく、データベース接続を維持して開けばいいです。私たちは少なくとも三つの案があります。
案一:Sql Session Factory
Sql Session Factoryでデータベース接続を手動で開くことができます。Controller方法を以下のように修正します。
案二:Transaction Template
Springでは、Transaction Templateでデータベーストランザクションを実行できます。この過程でデータベース接続は同じように開かれています。コードは以下の通りです
案三:@Transactionalコメント
この本質は方案と同じで、コードは以下の通りです。
これまでMyBatis流动调査の実现方法についての记事をここに绍介します。MyBatis流动调査の内容については、以前の文章を検索してください。または、下の関连する文章を引き続きご覧ください。これからもよろしくお愿いします。
もしストリーミングクエリがないなら、私達はデータベースから1000万本の記録を取りたいですが、十分なメモリがない場合は、ページ別に調べなければなりません。ページ別の検索効率は表の設計によって決まります。もしデザインが良くないなら、効率的なページ別のクエリを実行することができません。したがって、ストリーミングクエリはデータベースアクセスフレームに必須の機能です。
ストリーミングクエリのプロセスでは、データベース接続はオープン状態を維持していますので、ストリーミングクエリを実行した後、データベースアクセスフレームはデータベース接続をオフにする責任がありません。データを取った後、自分でシャットダウンする必要があります。
MyBatisフロー式クエリインターフェース
MyBatisはorg.apphe.ibatis.cursor.Cursorというインターフェースクラスを提供しています。このインターフェースはjava.io.loseableとjava.lang.Iterableインターフェースを継承しています。
try(Cursor cursor = mapper.querySomeData()) {
cursor.forEach(rowObject -> {
// ...
});
}
try-resource方式を使用するとCursorが自動的に閉じることができます。しかし、Cursorを構築する過程は簡単ではない。
私たちは実際の例を挙げます。次はMapper類です。
@Mapper
public interface FooMapper {
@Select("select * from foo limit #{limit}")
Cursor<Foo> scan(@Param("limit") int limit);
}
方法scan()は非常に簡単な検索です。この当事者を定義するとき、戻り値をCursorタイプに指定します。MyBatisはこのクエリ方法がストリーミングクエリであることが分かります。その後、SpringMVC Controller方法を書いて、Mapperを呼び出します。
@GetMapping("foo/scan/0/{limit}")
public void scanFoo0(@PathVariable("limit") int limit) throws Exception {
try (Cursor<Foo> cursor = fooMapper.scan(limit)) { // 1
cursor.forEach(foo -> {}); // 2
}
}
fooMapperが@Autowiredで入ってきたと仮定します。注釈1はCursorオブジェクトを取得し、最後に閉じることができることを保証する。2箇所は、cursorからデータを取ります。上のコードは問題ないようですが、scanFoo 0を実行するとエラーが発生します。
java.lang.Illagal StateException:A Currsor is already closed.
これは前に言ったように、データを取る過程でデータベース接続を維持する必要がありますが、Mapper方法は通常実行後に接続がオフになりますので、Cusorも一緒にオフになりました。
だから、この問題を解決する考えは複雑ではなく、データベース接続を維持して開けばいいです。私たちは少なくとも三つの案があります。
案一:Sql Session Factory
Sql Session Factoryでデータベース接続を手動で開くことができます。Controller方法を以下のように修正します。
@GetMapping("foo/scan/1/{limit}")
public void scanFoo1(@PathVariable("limit") int limit) throws Exception {
try (
SqlSession sqlSession = sqlSessionFactory.openSession(); // 1
Cursor<Foo> cursor =
sqlSession.getMapper(FooMapper.class).scan(limit) // 2
) {
cursor.forEach(foo -> { });
}
}
上のコードの中で、1箇所はSql Sessionを開いています。(実際にはデータベース接続を表しています。)最後に閉じることができるように保証します。2箇所はSql Sessionを使ってMapperオブジェクトを獲得します。このようにして得られたCursorオブジェクトはオープン状態であることが保証されます。案二:Transaction Template
Springでは、Transaction Templateでデータベーストランザクションを実行できます。この過程でデータベース接続は同じように開かれています。コードは以下の通りです
@GetMapping("foo/scan/2/{limit}")
public void scanFoo2(@PathVariable("limit") int limit) throws Exception {
TransactionTemplate transactionTemplate =
new TransactionTemplate(transactionManager); // 1
transactionTemplate.execute(status -> { // 2
try (Cursor<Foo> cursor = fooMapper.scan(limit)) {
cursor.forEach(foo -> { });
} catch (IOException e) {
e.printStackTrace();
}
return null;
});
}
上記のコードの中で、1つのTransaction Templateオブジェクトを作成しました。2つはデータベース事務を実行します。データベース事務の中身はMapper対象のストリーミングクエリを呼び出します。ここのMapperオブジェクトはSql Sessionで作成する必要がありません。案三:@Transactionalコメント
この本質は方案と同じで、コードは以下の通りです。
@GetMapping("foo/scan/3/{limit}")
@Transactional
public void scanFoo3(@PathVariable("limit") int limit) throws Exception {
try (Cursor<Foo> cursor = fooMapper.scan(limit)) {
cursor.forEach(foo -> { });
}
}
ただ元の方法に@Transationコメントを付けただけです。この案は一番簡潔に見えるが、Springフレームの中で使用されている穴に注意してください。外部呼び出し時のみ有効です。この方法を現在のクラスで呼び出しても、エラーが発生します。これまでMyBatis流动调査の実现方法についての记事をここに绍介します。MyBatis流动调査の内容については、以前の文章を検索してください。または、下の関连する文章を引き続きご覧ください。これからもよろしくお愿いします。