HibernateでのSQLクエリの選択的ロギング

先週の初めに、ラジオ局のオーディオコンテンツを管理する実際のプロジェクトに取り組み始めました。 VAADINフレームワーク( https://vaadin.com/home )を使用する通常の管理パネルは、プレイリストやDJの生活を簡素化するその他の機能を設定できるWebインターフェースをユーザーに提供します。

顧客はアプリケーションを継承し、その機能を拡張し、システムの運用中に特定されたバグを修正したいと考えていました。 私にとっては、それは完全に普通で合理的な願望です。


私のタスクの1つは、データサンプリングのバグを修正することでした。 判明したように、アプリケーションは空の値でデータベースレコードに書き込み、フェッチするとき、データは空ではないと想定されていました。 アプリケーションはHibernateのJPA実装を使用してデータベースを操作します。 原則として、Hibernateはかなり一般的なフレームワークであるため、驚くべきことではありません。 私は彼と数年間働いてきましたが、私はプロではありません。 さらに、データベースモデルとJavaオブジェクトモデルは簡単ではありませんでした。循環依存関係、継承、多対多の関係などを使用します。 一般的に、すぐにモデルを見て、エラーの原因をどこで探すべきか理解できませんでした。

バリデーターがスキップされる場所を判断するためのSQLロギングに関する質問がありました。
hibernate.show_sql = trueパラメーターの使用はあまり好きではありませんでした。 私は2つの問題を見ました:

1)System.outのHibernateは、疑問符付きで実際のパラメーターなしのSQLクエリのみを書き込みます。
2)Hibernateは絶対にすべてのクエリをデータベースに書き込みます(判明したように、1ページでアプリケーションが約50のクエリを実行しました)。

表示されたクエリを見ると、自分のタスクに関連するテーブルへのクエリのみを表示する必要があることに気付きました。

Googleに助けを求めると、実際のパラメーターを表示する方法に関するアドバイスが見つかりました。Log4jを使用する場合、パラメーターのロガーを追加する必要があります( 記事へのリンク )。

#Hibernateのログオプション(INFOはスタートアップメッセージのみを表示します)
log4j.logger.org.hibernate = INFO
#JDBCバインドパラメーターランタイム引数のログ
log4j.logger.org.hibernate.type = trace

これらのジェスチャーの後、問題1を解決しました。クエリパラメーターがログに書き込まれましたが、ログが非常に大きくなり、数分間必要なクエリを探す必要がありました。 私はすぐにこれが機能しないことに気付き、クエリロギングをオンまたはオフにするアプリケーションが実行されているときにアイデアが生まれました。
現在のプロジェクトでは、EJB 3コンポーネントを使用してデータベースを操作します。インターセプターについて読んだ後、SQLクエリロギングをオンザフライで有効または無効にするためにアイデアを作成しました。

コードは次のとおりです。

// Interceptor public class ShowHibernateSQLInterceptor { //  @AroundInvoke public Object showSQL(InvocationContext ictx) throws Exception { //  SQL  Logger sqlLogger = Logger.getLogger("org.hibernate.SQL"); sqlLogger.setLevel(Level.DEBUG); //    Logger descLogger = Logger.getLogger("org.hibernate.type.descriptor.sql.BasicBinder"); descLogger.setLevel(Level.TRACE); //    Object res = ictx.proceed(); //  SQL sqlLogger = Logger.getLogger("org.hibernate.SQL"); sqlLogger.setLevel(Level.INFO); //   descLogger = Logger.getLogger("org.hibernate.type.descriptor.sql.BasicBinder"); descLogger.setLevel(Level.INFO); return res; } } 


サービスから選択したメソッドのロギングを使用する例:
 @Override @Interceptors(ShowHibernateSQLInterceptor.class) public List<AudioItem> listAllAudioItems() { return em.createQuery("select a from AudioItem a").getResultList(); } 


メソッドが呼び出されると、データベースからのAudioItemエンティティの選択に関連するパラメーターを持つすべてのSQLクエリがログに書き込まれます。
@Interceptors(ShowHibernateSQLInterceptor.class)が選択されたメソッドではなくサービスクラス全体に指定されている場合、このクラスのすべてのメソッドがログに記録されます)
なぜなら org.hibernate.SQL = DEBUGのため、クエリはHiberanteログに表示されます。
hibernate.show_sql = trueが消え、この機能を無効にしました。

結果:
個々のメソッドのみをログに記録するには、以下をお勧めします。
1) hibernate.show_sql = trueを無効にします
2)インターセプタークラスを記述する
3)選択したメソッドのインターセプターを追加します。

制限事項:
1)log4jのみでテスト済み-別のロガーで動作するかどうかはわかりませんが、Log4jをプロジェクトに追加するか、インターセプターを別のロガーフレームワーク用に調整することは可能だと思います
2)提案されたソリューションはEJB 3にのみ関連します。プロジェクトがSpringを使用する場合、同様の機能を持つアスペクト(Spring AOP)を記述する必要があります。
3)JBoss ASでテスト済み:このサーバーはLog4j用のロガーアダプターを使用します。 したがって、別のサーバーで動作するはずですが、微妙な違いがあるかもしれません...

この問題にはもっと洗練された解決策があるかもしれませんが、Googleで検索結果をすばやく確認しても見つけられなかったため、20分を費やすことにし、問題は解決し、その結果、タスクは時間通りに完了しました。

誰かがより良い解決策を知っているなら、私はそれを聞く準備ができています-Hibernateリクエストを分析するツールが必要な場合があるので、それはタスクを迅速に解決するのに役立ちます。

Source: https://habr.com/ru/post/J149001/


All Articles