Spring環境下のMybatisの配置と作業メカニズム


MyBatisは、SQL、ストレージプロセス、および高度マッピングをカスタマイズするための一流のサポートフレームです。MyBatisは簡単なXML形式または注釈で構成でき、ベースデータ要素、Mapインターフェース、POJO(通常javaオブジェクト)をデータベースにマッピングすることができます。すべてのMyBatisアプリケーションはSql Session Factoryの実例を中心にしています。Sql Session Factoryの例はSql Session FactoryBuiderによって得られ、Sql Session FactoryBuiderはXMLからファイルを構成することができ、またはカスタムで作成したコンフィグレーションclassによってSql Session Factoryの例を作成することができます。Spring以外の環境でXMLファイルの構成は以下の通りです。

  
     
     
     
 	  
          
          
	  
     
     
  
  
    
  

は、上のMybatisに事務マネージャおよびデータソース(データベース接続池)が配置されている。Spring環境ではデータソースや事務マネージャはファイルに配置する必要がなく、mybatis-springパッケージのorg.mybatis.spring.Sql Session FactoryBenによって第三者データソースを注入します。mybatisのSpringでのappication Contect.xmlの構成は以下の通りです。


	
	
	

	


	
	
	
	
	

	
	
	
	
	
	
	



	
は上記の構成から分かるように、Sql Session FactoryBeanに第三者dataSource、すなわちdbcpデータソースが注入されている。また、mybatisプロファイルとMapperマッピングファイルの経路も構成において与えられる。ここでSpring環境下のmybatis配置ファイルを見てみます。

	
	
		
		
	
	
		
	
は上記の構成を観察して、データソース、事務マネージャなどのオプションを再構成する必要がなくなったことが分かります。ちなみに上の配置を説明します。ファイルは外部属性ファイルを提供しています。運行中のmybatisの行動を変えるために重要です。私はmybatisの二級キャッシュを開設しました。私たちはMybatisが二段階キャッシュに分かれていることを知っています。一級キャッシュはSessionに基づいて、二級キャッシュの役割領域はMapperの範囲です。ただしSpring環境下でSql SessionはSpring容器で管理されているため、mybatisの一級キャッシュは有効ではない。クラスのフルネームに短いエイリアスを作成し、Mapperファイルを使用しやすくします。
配置ができました。具体的にはmybatisはどのように仕事をしていますか?まず、ビジネス層でデータベースを読む時に使うmybatisのインターフェースを見てみましょう。
public class BaseDao  extends SqlSessionDaoSupport {

	public int  save(String key, Object object){
		return getSqlSession().insert(key, object);
	}
	
	public int update(String key, Object object){
		return getSqlSession().update(key, object);
	}
	
	public int  update(String key){
		return getSqlSession().update(key);
	}
	
	public int  delete(String key, Serializable id) {
		return getSqlSession().delete(key, id);
	}.................
ここではSql Session DaoSupportを継承する方法を使用します。このクラスを見に来てください
public abstract class SqlSessionDaoSupport extends DaoSupport {

	  private SqlSession sqlSession;

	  private boolean externalSqlSession;

	  public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
	    if (!this.externalSqlSession) {
	      this.sqlSession = new SqlSessionTemplate(sqlSessionFactory); 	    }
	  }

	  public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
	    this.sqlSession = sqlSessionTemplate;
	    this.externalSqlSession = true;
	  }

	  /**
	   * Users should use this method to get a SqlSession to call its statement methods
	   * This is SqlSession is managed by spring. Users should not commit/rollback/close it
	   * because it will be automatically done.
	   *
	   * @return Spring managed thread safe SqlSession
	   */
	  public SqlSession getSqlSession() {
	    return this.sqlSession;
	  }
	}
私たちはBaseDaoインターフェースを使用してget Sql Sessionを呼び出すと、そのベースSql Session DaoSupportに対応する方法を呼び出す。このSql SessionはそのサブクラスSql Session Templateです。 の一例です
this.sql Session=new Sql Session Template(sql Session Factory); 
上の配置ファイルにはこの文があります。    ,ここから前の文Sql Session Templateの実用化が入ってきたパラメータsql Session Factoryが見られます。 org.mybatis.spring.Sql Session FactoryBen。 
まずSql Session Templateの正体を見てみます。
public class SqlSessionTemplate implements SqlSession {

	  private final SqlSessionFactory sqlSessionFactory;

	  private final ExecutorType executorType;

	  private final SqlSession sqlSessionProxy;

	  private final PersistenceExceptionTranslator exceptionTranslator;

	  public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
		 PersistenceExceptionTranslator exceptionTranslator) {

			notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
			notNull(executorType, "Property 'executorType' is required");

			 this.sqlSessionFactory = sqlSessionFactory;
			 this.executorType = executorType;
			 this.exceptionTranslator = exceptionTranslator;
			 this.sqlSessionProxy = (SqlSession) newProxyInstance(
			  SqlSessionFactory.class.getClassLoader(), new Class[] { SqlSession.class },new SqlSessionInterceptor());			
		} .......
の最も重要なのは最後の文で、javaの動的エージェントを使って生成されたものです。
Sql Session Proxyは具体的なデータベースCRUDを実行します。JDKの動的エージェントはインターフェースに基づいています。このプロキシはSql Sessionのインターフェースを実現し、Sql Session Intercepterインターフェースを使用して、mybatis方法をSpringトランザクションマネージャによって生成されたSql Sessionに導く。動的エージェントでは、プロキシが対応するクラス方法を呼び出すと、InvocationHandler中のinvoke方法が呼び出されることを知っています。Sql Session IntercepterはInvocationHandlerのサブクラスです。このブロックの中で一番重要なのはinvoke方法です。
prvate class Sql Session Interceptor implemens InvocationHandler{    public Object invoke(Object proxy,Method,Object[]args)throwable{      Sql Session sql Session=get Sql Session(          Sql Session Template.this.sql Session Factory、          Sql Session Template.this.exect rType、          Sql Session Template.this.exception Translator;      }
スクリーンセーバーにおけるget Session方法の呼び出しorg.mybatis.spring.Sql Session Utilsにおける静的get Session方法。この方法を再観察します。
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {

    //     holder   sqlSession
    SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);

    if (holder != null && holder.isSynchronizedWithTransaction()) {
      if (holder.getExecutorType() != executorType) {
        throw new TransientDataAccessResourceException("Cannot change the ExecutorType when there is an existing transaction");
      }
      holder.requested();

      return holder.getSqlSession();
    }
   SqlSession session = sessionFactory.openSession(executorType);
    ................
    return session;
  }
この文に気づきました。
 
Sql Session session=session Factory.
openSession
(exectorType)
 伝達されたsession Factoryを呼び出してsql Sessionを開きます。Spring注入のsql Session Factoryは
org.mybatis.spring.Sql Session FactoryBen。 Sql Session FactoryBenというクラスはSql Session Factoryを生成するためのものです。これも一般的にSpring環境下で共有のSql Session Factoryを生成する方法です。実際には依存注入時にmybatisに基づくDAOインターフェース(この例ではBaseDao)に注入するのは、その内部で生成されたSlqSession Faction Factory Factoryです。
public class SqlSessionFactoryBean implements FactoryBean, InitializingBean, ApplicationListener {

			  private static final Log logger = LogFactory.getLog(SqlSessionFactoryBean.class);

			  private Resource configLocation;

			  private Resource[] mapperLocations;

			  private DataSource dataSource;

			  private TransactionFactory transactionFactory;

			  private Properties configurationProperties;

			  private SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();

			  private SqlSessionFactory sqlSessionFactory;

			  private String environment = SqlSessionFactoryBean.class.getSimpleName(); // EnvironmentAware requires spring 3.1

; .............}
は、Sql Session FactoryBeanにdataSource、sql Session FactoryBuider、sql Session Factory、およびプロファイルConfigrationなどの例が含まれていることが分かります。
this.sql Session Factory=buildSql Session Factory() 
buildSql Session Factoryはまたthis.sql Session FactoryBuild.buildを呼び出します。
またこのbuildの方法を見てください。
public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }
連絡先からBaseDao注入のSql Session Factoryがここであることが分かりました。
Default Sql Session Factroyは、プロファイルConfigrationの例をパラメータとして伝えてきたので、上記のようにopenSessionを呼び出すには、2つの方法があります。
public class DefaultSqlSessionFactory implements SqlSessionFactory {

  private final Configuration configuration;

  public SqlSession openSession() {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
  }
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
      Transaction tx = null;
      final Environment environment = configuration.getEnvironment();
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      final Executor executor = configuration.newExecutor(tx, execType);  //      SQL      
      return new DefaultSqlSession(configuration, executor, autoCommit);  //           SqlSession     
   
  }
public SqlSession openSession(Connection connection) {
    return openSessionFromConnection(configuration.getDefaultExecutorType(), connection);
  }
は上から分かりました。私達が実際に使っているSql SessionはそのサブタイプのDefault Sql Sessionです。
public class DefaultSqlSession implements SqlSession {

  private Configuration configuration;
  private Executor executor;

  private boolean autoCommit;
  private boolean dirty;
  
  public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
    this.configuration = configuration;
    this.executor = executor;
    this.dirty = false;
    this.autoCommit = autoCommit;
  }...............................
Default Sql SessionはSql Sessionのすべてのインターフェースを実現しました。ソースをよく見ると、実際にSQL文を実行するのはExectorであることが分かります。
Exectorの生成は、org.apache.ibatis.session.CoonfigrationのnewExector法により生成される。
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
	    executorType = executorType == null ? defaultExecutorType : executorType;
	    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
	    Executor executor;
	    if (ExecutorType.BATCH == executorType) {
	      executor = new BatchExecutor(this, transaction);
	    } else if (ExecutorType.REUSE == executorType) {
	      executor = new ReuseExecutor(this, transaction);
	    } else {
	      executor = new SimpleExecutor(this, transaction);
	    }
	    if (cacheEnabled) {
	      executor = new CachingExecutor(executor);
	    }
	    executor = (Executor) interceptorChain.pluginAll(executor);
	    return executor;
	  }
で、cacheを開けないと、作成されたExectorは3の基本タイプの一つにすぎません。BatExectorはバッチSql操作を専門に実行します。ReuseExectorはstatementでsql操作を実行します。SimpleExectorは単にsqlを実行するだけです。cacheを開くと(デフォルトでは開けられますが、それを閉じる理由はありません。)CachingExectorは、前に作成したExectorを唯一のパラメータとして作成します。CachingExectorはデータベースを検索する前にキャッシュを探しておきます。見つけられなかったらdelegateを呼び出します。Exectorオブジェクトは、Exectorタイプに対するプラグインが定義されている場合、最終的に生成されるExectorオブジェクトは、各プラグインによって挿入されたエージェントオブジェクトである。(mybatisの改ページはメモリベースの論理改ページで、データ量が多い時はメモリを使いすぎてしまいます。一般的に提唱されている改ページ方式は物理改ページです。これは自分でブロックで実現する必要があります。)
public class SimpleExecutor extends BaseExecutor {

		  public SimpleExecutor(Configuration configuration, Transaction transaction) {
		    super(configuration, transaction);
		  }

		  public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
		    Statement stmt = null;
		    try {
		      Configuration configuration = ms.getConfiguration();
		      StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
		      stmt = prepareStatement(handler, ms.getStatementLog());
		      return handler.update(stmt);
		    } finally {
		      closeStatement(stmt);
		    }
		  }
では、Exectorの具体的な作業はStation Handlerによって行われることがわかります。Sttement HandlerもConfigrationで生成されます。
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, 
						RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {

	 StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);

	 statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);

	 return statementHandler;
 }
作成されたSttement Handlerは毎回RoutingSttement Handlerであることが分かります。これはただの配布者です。彼の属性はdelegateで、どのような具体的なSttementHandlerを指定しますか?オプションのSttement Handlerがあります。
SimpleStation Handler、
PreparedSttement Handlerと
Callable Statitement Handlerの3つの種類があります。どのようなmapperプロファイルの各statementに指定されていますか?デフォルトはPreparedStatitement Handlerです。SttementHandlerがブロックされているかにも注目してください。Exectorと同じように、スクリーンでブロックされた後の対応はプロキシオブジェクトが多くあります。mybatisデータベースが実装されていません。の実現はこの場所でブロックを使って実現されます。
 public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {

		    switch (ms.getStatementType()) {
		      case STATEMENT:
			delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
			break;
		      case PREPARED:
			delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
			break;
		      case CALLABLE:
			delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
			break;
		      default:
			throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
		    }

		  }
Sttement Handler作成後、statementのオープンやパラメータ設定、PreparedSttementに対してはパラメータの設定操作などを行う必要があります。具体的なExectorでは、コードは以下の通りです。
 private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
	    Statement stmt;
	    Connection connection = getConnection(statementLog);
	    stmt = handler.prepare(connection);
	    handler.parameterize(stmt);
	    return stmt;
	  }
では、handlerのパラメータ化方法は、このhandler、すなわちPreparedSttement Handlerである。 
public void parameterize(Statement statement) throws SQLException {
	    parameterHandler.setParameters((PreparedStatement) statement);
	  }
では、parameterHandlerは、PreparedSttement HandlerのベースクラスのBaseStation Handlerである。
public abstract class BaseStatementHandler implements StatementHandler {

		  protected final Configuration configuration;
		  protected final ObjectFactory objectFactory;
		  protected final TypeHandlerRegistry typeHandlerRegistry;
		  protected final ResultSetHandler resultSetHandler;
		  protected final ParameterHandler parameterHandler;

		  protected final Executor executor;
		  protected final MappedStatement mappedStatement;
		  protected final RowBounds rowBounds;

		  protected BoundSql boundSql;
のうち、this.parameterHandler=configrations.newParameeter Handler(mapped Station,parameterObject,boundSql)は、Configrationで生成される。
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
		    ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
		    parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
		    return parameterHandler;
		  }
では、mapped Statitement.getLangがlangage Driverに戻る。
public class XMLLANggage Driver implemens Language Driver{
 public Parameter Handler createParameter Handler(Mapped Sttement mapped Sttement、Object parameterObject、BoundSql boundSql){
   return new Default Parameter Handler;
 }
生成します
Default Parameeter Handler
public class DefaultParameterHandler implements ParameterHandler {

		  private final TypeHandlerRegistry typeHandlerRegistry;

		  private final MappedStatement mappedStatement;
		  private final Object parameterObject;
		  private BoundSql boundSql;
		  private Configuration configuration;

		  public DefaultParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
		    this.mappedStatement = mappedStatement;
		    this.configuration = mappedStatement.getConfiguration();
		    this.typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();
		    this.parameterObject = parameterObject;
		    this.boundSql = boundSql;
		  }

		  public Object getParameterObject() {
		    return parameterObject;
		  }

		  public void setParameters(PreparedStatement ps) throws SQLException {
		    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
		    List parameterMappings = boundSql.getParameterMappings();
		    if (parameterMappings != null) {
		      for (int i = 0; i < parameterMappings.size(); i++) {
			ParameterMapping parameterMapping = parameterMappings.get(i);
			if (parameterMapping.getMode() != ParameterMode.OUT) {
			  Object value;
			  String propertyName = parameterMapping.getProperty();
			  if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
			    value = boundSql.getAdditionalParameter(propertyName);
			  } else if (parameterObject == null) {
			    value = null;
			  } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
			    value = parameterObject;
			  } else {
			    MetaObject metaObject = configuration.newMetaObject(parameterObject);
			    value = metaObject.getValue(propertyName);
			  }
			  TypeHandler typeHandler = parameterMapping.getTypeHandler();
			  JdbcType jdbcType = parameterMapping.getJdbcType();
			  if (value == null && jdbcType == null) jdbcType = configuration.getJdbcTypeForNull();
			  typeHandler.setParameter(ps, i + 1, value, jdbcType);
			}
		      }
		    }
		  }

		}
この中で一番重要なのは最後のコードです。その役割は適切なType Handlerでパラメータの設定を完成することです。何が適切なType Handlerですか?また、どのように決断されたのですか?BaseStation Handlerの構造方法にはこうあります。
this.boundSql=mapped Sttement.get BoundSql(parameterObject)
sqlの解析を触発しました。sqlを解析する過程でType Handlerも決断されました。決断の原則はパラメータの種類とパラメータに対応するJDBCタイプによってどのType Handlerを使うかを決めます。例えば、パラメータタイプがStringならStering Type Handlerを使います。パラメータタイプが整数ならInteger Typendlerなどを使います。
パラメータ設定が完了したら、データベース操作(udateまたはquery)を実行します。queryの最後にクエリー結果の処理があります。
Mybatisのいくつかの重要な種類を結合して、具体的なSql文の生成過程を分析してみます。Mapped Sttement、Sql SourceなどのMybatisの実現メカニズムを理解すれば、明らかになるはずです。ここに書いておきます。もしミスがあったら、指摘してください。