Mybatisのパラメータ使用

14130 ワード

Mybatisを使い始めたばかりの時、霧のような感じがしました.データベースの実際の操作はすでにパッケージ化されていますので、パラメータが入るだけでいいですが、これらのパラメータはSQLとどのように関連していますか?パラメータの名前を使って関連しますか?もし複数のパラメータがあるなら?配列またはリストを使うなら?
各種パラメータの使用例を以下に示します.
//DAOの実現類:
public class UserDaoImpl extends SqlSessionDaoSupport implements UserDao {
	//     SqlSessionDaoSupport   ,    sqlSessionTemplate setter  ,
	//   applicationContext.xml   DAO ,        sqlSessionTemplate
	
	@Override
	public User getUserByNameStr1(String name) {
		return getSqlSession().selectOne("findByNameStr1", name);
	}

	@Override
	public User getUserByNameStr2(String name) {
		return getSqlSession().selectOne("findByNameStr2", name);
	}

	@Override
	public User getUserByNameStr3(String name) {
		return getSqlSession().selectOne("findByNameStr3", name);
	}

	@Override
	public User getUserByNameStr4(String keyWord) {
		return getSqlSession().selectOne("findByNameStr4", keyWord);
	}

	@Override
	public User getUserByBean(User user) {
		return getSqlSession().selectOne("findByBean", user);
	}

	@Override
	public User getUserByBean2(User user) {
		return getSqlSession().selectOne("findByBean2", user);
	}

	@Override
	public User getUserByMap(Map<String, String> map) {
		return getSqlSession().selectOne("findByMap", map);
	}

	@Override
	public List<User> getUserByMap2(Map<String, String> map) {
		return getSqlSession().selectList("findByMap2", map);
	}

	@Override
	public List<User> getUserByMap3(Map<String, String> map) {
		return getSqlSession().selectList("findByMap3", map);
	}

	@Override
	public List<User> getUserByMap4(Map<String, String> map) {
		return getSqlSession().selectList("findByMap4", map);
	}

	@Override
	public List<User> getUserByArray(String[] names) {
		return getSqlSession().selectList("findByArray", names);
	}

	@Override
	public List<User> getUserByArray2(String[] keyWords) {
		return getSqlSession().selectList("findByArray2", keyWords);
	}

	@Override
	public List<User> getUserByList(List<String> names) {
		return getSqlSession().selectList("findByList", names);
	}

	@Override
	public List<User> getUserByList2(List<User> users) {
		return getSqlSession().selectList("findByList2", users);
	}
	
	@Override
	public List<User> getUserByList3(List<String> keyWords) {
		return getSqlSession().selectList("findByList3", keyWords);
	}
}
//Mapper.xml
<?xml version="1.0" encoding="UTF-8" ?>  
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="">
	<resultMap id="userMap" type="User">
		<id property="id" column="id" />
		<result property="name" column="name" />
		<result property="password" column="password" />
	</resultMap>
	
	<!--
		          ,               ;
		       TypeHandler ,     #{}        ,
		   name,    A 0,   abc,            
		    ,     #{} (     ,      ,      ) 
	 -->
	<select id="findByNameStr1" resultMap="userMap">
		select * from users where name=#{name}
	</select>	
	<select id="findByNameStr2" resultMap="userMap">
		select * from users where name=#{A}
	</select>	
	<select id="findByNameStr3" resultMap="userMap">
		select * from users where name=#{0}
	</select>
	<!--
		          ,               ;
		       TypeHandler,        SQL  bind  ,
		         _parameter,  bind   _parameter    ,
		            ( abc),   #{}           。
		 bind   ,    OGNL   ,           bean,  
		       bean   , :         User,      
		 _parameter.getName() 。
	 -->
	<select id="findByNameStr4" resultMap="userMap">
		<bind name="abc" value="'%' + _parameter + '%'" />
		select * from users where name like #{abc}
	</select>
	
	
	<!-- 
		     Bean,     #{}   Bean      ,       
		      getter。       getter,    , findByBean2 
		    A   getter,    :ReflectionException: There is no getter for property named 'A' in 'class com.mybatis.demo6.User'
	 -->
	<select id="findByBean" resultMap="userMap">
		select * from users where name=#{name}
	</select>	
	<select id="findByBean2" resultMap="userMap">
		select * from users where name=#{A}
	</select>
	
	
	<!-- 
		     Map,     #{}  Map  key 。
		Map       key,  findByMap;
		     key        ,  findByMap2;
		     key        ,  findByMap3;
		       key   like,  findByMap4;
	 -->
	<select id="findByMap" resultMap="userMap">
		select * from users where name=#{keyName}
	</select>	
	<select id="findByMap2" resultMap="userMap">
		select * from users where name=#{keyName} or password=#{keyPwd}
	</select>	
	<select id="findByMap3" resultMap="userMap">
		select * from users where name=#{keyName1} or name=#{keyName2}
	</select>	
	<!--   like  %   ,     bind  SQL,           -->
	<select id="findByMap4" resultMap="userMap">
		select * from users where name like CONCAT('%',#{keyWord},'%') or password like CONCAT('%',#{keyWord},'%')
	</select>
	
	
	<!-- 
		        ,         ,           SQL(          TypeHandler);
		foreach   SQL   , foreach    SQL          ,     separator      
		    ; collection    array(    ),index       ,item       SQL  #{},
		 item     #{}      。    ,          ,     2,     SQL   :
		select * from users where name = #{__frch_keyName_0} or name = #{__frch_keyName_1}
		       foreach  #{keyName}    #{__frch_keyName_0} #{__frch_keyName_1},     
		     ;             map , key #__frch_keyName_0、__frch_keyName_1,    
		     ,           map     key     。
	 -->
	<select id="findByArray" resultMap="userMap">
		select * from users where 
		<foreach item="keyName" index="index" collection="array" open="(" separator="or" close=")">
			name = #{keyName}
		</foreach>
	</select>	
	<!-- 
		   bind  SQL ,       map :
			   map:key=_parameter,value=   map;
			   map:key=array,value=  。
		  bind      OGNL       ,  bind                  (   );
		                             ,       。
	 -->
	<select id="findByArray2" resultMap="userMap">
		<bind name="keyName" value="_parameter['array'][0]" />
		<bind name="keyPwd" value=" _parameter['array'][1]" />
		select * from users where name=#{keyName} or password=#{keyPwd}
	</select>
	
	
	<!-- 
		list     ,               list,           :
		 foreach  collection      list,       ;
		   bind   ,   map key  list,value  list。
	 -->
	<select id="findByList" resultMap="userMap">
		select * from users where 
		<foreach item="keyName" index="index" collection="list" open="(" separator="or" close=")">
			name = #{keyName}
		</foreach>
	</select>	
	<select id="findByList2" resultMap="userMap">
		select * from users where 
		<foreach item="user" index="index" collection="list" open="(" separator="or" close=")">
			name = #{user.name}
		</foreach>
	</select>	
	<select id="findByList3" resultMap="userMap">
		<bind name="keyName" value="_parameter['list'][0]" />
		<bind name="keyPwd" value=" _parameter['list'][1]" />
		select * from users where name=#{keyName} or password=#{keyPwd}
	</select>
</mapper>  
上記の例では主に以下のパラメータの使用状況を示しています.
  • 単一文字列.
  • Benオブジェクト;
  • Mapオブジェクト;
  • 配列
  • List
  • これらの使用方法は上のMapper.xmlで詳しく説明しましたが、なぜこのようにしますか?
    上のDAO実装クラスでは、Sql Sessionを直接使用して操作します.まずSql Sessionインターフェースの定義を見てみます.
    public interface SqlSession extends Closeable {
      <T> T selectOne(String statement);
      <T> T selectOne(String statement, Object parameter);
      <E> List<E> selectList(String statement);
      <E> List<E> selectList(String statement, Object parameter);
      <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds);
      //   。。。
    }
    は、上のインターフェース定義から、selectOneであれ、selectListであれ、本当にSQL文に使用されるパラメータは一つしかない(最初のパラメータはSQL文を選択するためのIDであり、第二のパラメータはSQL文のためのパラメータである)、つまり、一つのパラメータだけを受け入れてSQL文を組み立てることができます.これは、どのような形のパラメータであっても、一つのオブジェクト(インターフェースパラメータの種類はObject)にしなければならないという意味です.Mapperインターフェースを使用する場合、Mapperインターフェースは複数のパラメータを使用することができますが、実際には1つのMapに移行します.Keyは0、1、2、3…など、パラメータリストの位置でKeyを表します.valueはパラメータの値です.(実際には、param 0、param 1…というkey、valueは同じパラメータ値である)をmapに加え、最後にMapperのエージェントが呼び出したのは依然としてSql Sessionのインターフェースでデータベース処理を行う.
    Sql Sessionはどのように一つのパラメータを使って多様なパラメータを満たすのですか?まずorg.aparthe.ibatis.scripting.defaults.Default Parameter Handlerを見にきます.
    public class DefaultParameterHandler implements ParameterHandler {
      //   ……
      public void setParameters(PreparedStatement ps) throws SQLException {
        ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        if (parameterMappings != null) {
          MetaObject metaObject = parameterObject == null ? null : configuration.newMetaObject(parameterObject);
          for (int i = 0; i < parameterMappings.size(); i++) {
            ParameterMapping parameterMapping = parameterMappings.get(i);
            if (parameterMapping.getMode() != ParameterMode.OUT) {
              Object value;
              String propertyName = parameterMapping.getProperty();
             //   1
             if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
                value = boundSql.getAdditionalParameter(propertyName);
              } else if (parameterObject == null) { //   2
                value = null;
              } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) { //   3
                value = parameterObject;
              } else { //   4
                value = metaObject == null ? null : metaObject.getValue(propertyName);
              }
              TypeHandler typeHandler = parameterMapping.getTypeHandler();
              if (typeHandler == null) {
                throw new ExecutorException("There was no TypeHandler found for parameter " + propertyName + " of statement " + mappedStatement.getId());
              }
              JdbcType jdbcType = parameterMapping.getJdbcType();
              if (value == null && jdbcType == null) jdbcType = configuration.getJdbcTypeForNull();
              typeHandler.setParameter(ps, i + 1, value, jdbcType);
            }
          }
        }
      }
    }
    上のコードはパラメータによって異なるType Handlerを使用してSQLを設定する値を決定します.その中のparameterMappingsはSQL文の各プレースホルダ情報を封入した対象です.parameterObjectは現在のパラメータのパッケージオブジェクトであり、パラメータのすべての値情報(もちろん、文字列、Bean、Map、Listなど)が含まれています.typeHandlerはPreparedSttementを設定するための値です.中間の4つのif/else分岐は、現在の設定値をPreparedSttementに決定します.
    ブランチの状況:
  • ダイナミックSQLが使用されると、一般に分岐1に入る.ダイナミックSQLを使用すると、ForEachSql Nodeの実現により、動的SQLのパラメータ値が実際にMapに変換されていることが分かります.したがって、propertyNameを通じて対応するパラメータ値が得られます.
  • 分岐2に入るとパラメータがないことを表します.
  • は分岐3に入り、これらのタイプは登録済み(デフォルトまたはカスタムを含む)Type Handlerであり、JAVAによくあるタイプ(int、long、byte、bootlean、String)などはデフォルトのType Handlerがあると説明しています.Mapper.xmlでType Handlerをカスタマイズすることもできます.登録したType Handlerがある場合、直接Type Handlerから値を取り出すことができます.
  • は、分岐4のために、一般的にMapおよびBeanのタイプに用いられる.org.apphe.ibatis.reflection.MetaObjectを見るとわかるように、BeanパラメータはBenWrapperにパッケージされ、MapパラメータはMapWrapperにパッケージされ、これらのWrapperは共通のインターフェースがあり、異なる(Benのgetterで属性値を取得し、Mapは属性値をgetで取得する)ため、統一インターフェースに取得することができます.
  • 配列およびlistに関する処理はより複雑である:
    1、まずorg.apache.ibatis.session.defaults.Default Sql Sessionでmapに置いてください.
      private Object wrapCollection(final Object object) {
        if (object instanceof List) {
          StrictMap<Object> map = new StrictMap<Object>();
          map.put("list", object);
          return map;
        } else if (object != null && object.getClass().isArray()) {
          StrictMap<Object> map = new StrictMap<Object>();
          map.put("array", object);
          return map;
        }
        return object;
      }
    2、org.apache.ibatis.scripting.xmltags.DynamicContectの中でもう一つのMapの中に詰めます.
      public static final String PARAMETER_OBJECT_KEY = "_parameter";
    
      public DynamicContext(Configuration configuration, Object parameterObject) {
        if (parameterObject != null && !(parameterObject instanceof Map)) {
          MetaObject metaObject = configuration.newMetaObject(parameterObject);
          bindings = new ContextMap(metaObject);
        } else {
          bindings = new ContextMap(null);
        }
        bindings.put(PARAMETER_OBJECT_KEY, parameterObject);
        bindings.put(DATABASE_ID_KEY, configuration.getDatabaseId());
      }
    ですので、実際には配列とリストのパラメータ値がデュアルmapに入れられています.
    3、Sql Nodeの実現の中でONLで取り出して簡単なMapにカプセル化する:
    Object value = OgnlCache.getValue(expression, parameterObject);
    ですので、SQL文でもONLの文法に基づいてこれらの値を取り出すことができます.