mybatisプラグイン:sqlと実行時間の実現方法を印刷します。


Plugins
MyBatisの公式文書から一節を取る。
MyBatisは、ある時点でマッピングされたステートメントの呼び出しをブロックすることができます。デフォルトでは、MyBatisはプラグインを使ってブロッキング方法の呼び出しを許可します。
Exector(udate、query、flush Sttements、compmint、rollback、get Transation、close、isCloosed)
Parameeter Handler(get ParameterObject、set Parameters)
Result SetHandler(handleResult Sets、handle Output Parameters)
Sttement Handler(prepare、parameterize、batch、udate、query)
これらのクラスの方法の詳細は、各方法の署名を調べることによって発見され、そのソースコードはMyBatis発行パケット中に存在する。あなたがカバーしている方法の行動は、監視呼び出しよりも多く行われているということを理解してください。与えられた方法を変更したりカバーしたりすると、MyBatisのコアを壊すことがあります。これは低レベルのクラスと方法です。プラグインは慎重に使います。
プラグインの例:各SQL文とその実行時間を印刷します。
MyBatisのプラグインの使い方をコードで示します。デモするシーンは、各SQL文と実行時間をプリントします。これは非常に有用な需要であり、MyBatis自身のログはSQLを記録することができますが、以下のいくつかの問題があります。
MyBatisログから印刷されたSQLログは、パラメータがすべてプレースホルダで表示されます。」代わりに、本当に実行されているSQL文のパラメータが分かりませんでした。
MyBatisログから印刷されたSQLログは、大量の改行符があります。通常はSQL文は十数行の表示を通して、読書体験が非常に悪いです。
SQL実行時間を記録できませんでした。SQL実行時間があれば、実行時間が遅いSQLに正確に位置決めできます。
MyBatisプラグインを書くのはとても簡単です。Interceptorインターフェースを実現するだけでいいです。私はここでSql CostIntercepterと名づけました。

/**
 * Sql          
 */
@Intercepts({@Signature(type = StatementHandler.class, method = "query", args = {Statement.class, ResultHandler.class}),
 @Signature(type = StatementHandler.class, method = "update", args = {Statement.class}),
 @Signature(type = StatementHandler.class, method = "batch", args = { Statement.class })})
public class SqlCostInterceptor implements Interceptor {
 
 @Override
 public Object intercept(Invocation invocation) throws Throwable {
  Object target = invocation.getTarget();
   
  long startTime = System.currentTimeMillis();
  StatementHandler statementHandler = (StatementHandler)target;
  try {
   return invocation.proceed();
  } finally {
   long endTime = System.currentTimeMillis();
   long sqlCost = endTime - startTime;
    
   BoundSql boundSql = statementHandler.getBoundSql();
   String sql = boundSql.getSql();
   Object parameterObject = boundSql.getParameterObject();
   List<ParameterMapping> parameterMappingList = boundSql.getParameterMappings();
    
   //    Sql  ,     ,    
   sql = formatSql(sql, parameterObject, parameterMappingList);
    
   System.out.println("SQL:[" + sql + "]    [" + sqlCost + "ms]");
  }
 }
 
 @Override
 public Object plugin(Object target) {
  return Plugin.wrap(target, this);
 }
 
 @Override
 public void setProperties(Properties properties) {
   
 }
  
 @SuppressWarnings("unchecked")
 private String formatSql(String sql, Object parameterObject, List<ParameterMapping> parameterMappingList) {
  //   sql      
  if (sql == null || sql.length() == 0) {
   return "";
  }
   
  //   sql
  sql = beautifySql(sql);
   
  //        ,   Sql        
  if (parameterObject == null || parameterMappingList == null || parameterMappingList.size() == 0) {
   return sql;
  }
   
  //              sql,        
  String sqlWithoutReplacePlaceholder = sql;
   
  try {
   if (parameterMappingList != null) {
    Class<?> parameterObjectClass = parameterObject.getClass();
 
    //      StrictMap Value   Collection,  key="list"   ,         <foreach>     List          
    //   select * from xxx where id in <foreach collection="list">...</foreach>
    if (isStrictMap(parameterObjectClass)) {
     StrictMap<Collection<?>> strictMap = (StrictMap<Collection<?>>)parameterObject;
      
     if (isList(strictMap.get("list").getClass())) {
      sql = handleListParameter(sql, strictMap.get("list"));
     }
    } else if (isMap(parameterObjectClass)) {
     //      Map     ,  map.get(key)          
     //          <insert>、<delete>、<update>、<select>   parameterType map   
     Map<?, ?> paramMap = (Map<?, ?>) parameterObject;
     sql = handleMapParameter(sql, paramMap, parameterMappingList);
    } else {
     //     ,                           String
     sql = handleCommonParameter(sql, parameterMappingList, parameterObjectClass, parameterObject);
    }
   }
  } catch (Exception e) {
   //             ,                   sql,      sql   BoundSql  sql   
   return sqlWithoutReplacePlaceholder;
  }
   
  return sql;
 }
  
 /**
  *   Sql
  */
 private String beautifySql(String sql) {
  sql = sql.replace("
", "").replace("\t", "").replace(" ", " ").replace("( ", "(").replace(" )", ")").replace(" ,", ","); return sql; } /** * List */ private String handleListParameter(String sql, Collection<?> col) { if (col != null && col.size() != 0) { for (Object obj : col) { String value = null; Class<?> objClass = obj.getClass(); // 、 、String // , , if (isPrimitiveOrPrimitiveWrapper(objClass)) { value = obj.toString(); } else if (objClass.isAssignableFrom(String.class)) { value = "\"" + obj.toString() + "\""; } sql = sql.replaceFirst("\\?", value); } } return sql; } /** * Map */ private String handleMapParameter(String sql, Map<?, ?> paramMap, List<ParameterMapping> parameterMappingList) { for (ParameterMapping parameterMapping : parameterMappingList) { Object propertyName = parameterMapping.getProperty(); Object propertyValue = paramMap.get(propertyName); if (propertyValue != null) { if (propertyValue.getClass().isAssignableFrom(String.class)) { propertyValue = "\"" + propertyValue + "\""; } sql = sql.replaceFirst("\\?", propertyValue.toString()); } } return sql; } /** * */ private String handleCommonParameter(String sql, List<ParameterMapping> parameterMappingList, Class<?> parameterObjectClass, Object parameterObject) throws Exception { for (ParameterMapping parameterMapping : parameterMappingList) { String propertyValue = null; // , toString , paramterMapping property if (isPrimitiveOrPrimitiveWrapper(parameterObjectClass)) { propertyValue = parameterObject.toString(); } else { String propertyName = parameterMapping.getProperty(); Field field = parameterObjectClass.getDeclaredField(propertyName); // Field , accessible true field.setAccessible(true); propertyValue = String.valueOf(field.get(parameterObject)); if (parameterMapping.getJavaType().isAssignableFrom(String.class)) { propertyValue = "\"" + propertyValue + "\""; } } sql = sql.replaceFirst("\\?", propertyValue); } return sql; } /** * */ private boolean isPrimitiveOrPrimitiveWrapper(Class<?> parameterObjectClass) { return parameterObjectClass.isPrimitive() || (parameterObjectClass.isAssignableFrom(Byte.class) || parameterObjectClass.isAssignableFrom(Short.class) || parameterObjectClass.isAssignableFrom(Integer.class) || parameterObjectClass.isAssignableFrom(Long.class) || parameterObjectClass.isAssignableFrom(Double.class) || parameterObjectClass.isAssignableFrom(Float.class) || parameterObjectClass.isAssignableFrom(Character.class) || parameterObjectClass.isAssignableFrom(Boolean.class)); } /** * DefaultSqlSession StrictMap */ private boolean isStrictMap(Class<?> parameterObjectClass) { return parameterObjectClass.isAssignableFrom(StrictMap.class); } /** * List */ private boolean isList(Class<?> clazz) { Class<?>[] interfaceClasses = clazz.getInterfaces(); for (Class<?> interfaceClass : interfaceClasses) { if (interfaceClass.isAssignableFrom(List.class)) { return true; } } return false; } /** * Map */ private boolean isMap(Class<?> parameterObjectClass) { Class<?>[] interfaceClasses = parameterObjectClass.getInterfaces(); for (Class<?> interfaceClass : interfaceClasses) { if (interfaceClass.isAssignableFrom(Map.class)) { return true; } } return false; } }
このコードを分析してみます。(これは改良されたバージョンです。主にselect*from xxx where id inという書き方のプレースホルダを本当のパラメータに置き換えるサポートを追加しました。
まず注解@Interceptsと@Signatureです。この二つの注釈は必要です。Pluginのwrap方法はこの二つの注釈の中のパラメータを取りますから。Interceptsでは複数の@Signatureが定義され、一つの@Signature表現は以下の条件に適合する方法でブロックされます。
インターフェースはtypeで定義されるタイプでなければなりません。
メソッド名はmethodと一致していなければなりません。
メソッドのイメージのクラスタイプは、args定義のクラスタイプと順序が一致していなければなりません。
次の問題は、4つのインターフェースがありますが、どうしてSttement Handlerを使ってブロックできますか?名前によってはParameeter HandlerとResultSetHandler、前者の処理パラメータ、後者の処理結果は不可能です。残りはExectorとSttement Handlerです。Sttement Handlerをブロックする理由は、Exectorを使用しない理由である。
Exectorのudateとquery方法は、MyBatisの1つまたは2つのキャッシュを使用することができますので、統計の結果は、本当のSQL実行時間ではありません。
Sttement Handlerのudateとquery方法は、どうしてもPreparedSttementのexecuteメソッド実行時間を統計します。一定の誤差がありますが、(誤差は主に処理結果を計算する時間から来ます。)、違いは大きくありません。
次にset Propties方法について説明します。いくつかの構成属性をのサブタグに配置してもいいです。すべての構成属性は、イメージPropertiesにあります。set Properties方法は、構成の属性を取得して必要な処理を行うことができます。
続いて、plugin方法を説明します。ここはターゲットインターフェースのためのプロキシです。必要でもないし、自分でプロキシを生成する方法を書く必要もないです。MyBatisのPlugin類はすでにwrap方法を提供してくれました。この方法の実現を見てください。

public static Object wrap(Object target, Interceptor interceptor) {
 Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
 Class<?> type = target.getClass();
 Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
 if (interfaces.length > 0) {
  return Proxy.newProxyInstance(
   type.getClassLoader(),
   interfaces,
   new Plugin(target, interceptor, signatureMap));
 }
 return target;
}
ここのtargetはきっとインターフェースであるため、JDK自身が提供するProxy類を安心して使用することができます。ここでは、このインターフェースが方法を満たして署名すれば、代理を生成することに相当します。
最後にintercept方法です。ここはスクリーンセーバーの核心コードです。方法のロジックは説明しません。自分で見てもいいです。唯一注意すべき点は、どうしても最後にinvocation.proceedに戻ります。スクリーンの重層的な呼び出しを保証します。
xmlファイルの設定で効果のデモンストレーションです。
プラグインを書き終わったら、config.xmlファイルの中で一回だけ配置すればいいです。とても簡単です。

<plugins>
  <plugin interceptor="org.xrq.mybatis.plugin.SqlCostInterceptor" />
 </plugins>
ここでは各サブラベルはプラグインを表しています。interceptorはブロックの完全な経路を表しています。一人一人の違いです。
このような配置があれば、Sql CostInterceptrを使用できます。Sql CostInterceptorは通用しますが、一人一人のCRUDは違っています。ここのCRUD実行の結果を印刷してみます。

SQL:[insert into mail(id, create_time, modify_time, web_id, mail, use_for) values(null, now(), now(), "1", "[email protected]", "    ");]    [1ms]
SQL:[insert into mail(id, create_time, modify_time, web_id, mail, use_for) values(null, now(), now(), "2", "[email protected]", "    ");]    [1ms]
SQL:[insert into mail(id, create_time, modify_time, web_id, mail, use_for) values(null, now(), now(), "3", "[email protected]", "      ");]    [0ms]
完全なSQl文とSQL文の実行時間が印刷されているのを見ました。
ただ、このプラグインは簡単なDemoにすぎません。全部のシーンをカバーすることはできません。だから、このコードの一部分を使って本物のSQLと実行時間の友達をプリントしたいなら、この基礎の上で修正する必要があります。コードを変えなくても、このプラグインはSQLを美化する役割を果たします。改行を削除しても大丈夫です。
MyBatisプラグインの実現原理については、「MyBatisソース分析」シリーズの文章の中で詳しく解読します。文章の住所は「MyBatisソース分析」です。
後記
MyBatisプラグインのメカニズムは非常に有用で、多くの問題を解決することができます。ここのSQL文の印刷とSQL文の実行時間を記録するだけでなく、改ページ、分表はプラグインによって実現できます。プラグインを上手に使うコツは最初から挙げます。ここでもう一度書きます。
Exector(udate、query、flush Sttements、compmint、rollback、get Transation、close、isCloosed)
Parameeter Handler(get ParameterObject、set Parameters)
Result SetHandler(handleResult Sets、handle Output Parameters)
Sttement Handler(prepare、parameterize、batch、udate、query)
この4つのインターフェースと関連した方法を理解してこそ、良いブロックを作成し、期待に合う機能を開発することができます。
以上のmybatisプラグイン:Sqlと実行時間の実現方法は小編集で皆さんに提供した内容の全部です。参考にしていただければと思います。よろしくお願いします。