Mybatisのパラメータ使用
14130 ワード
Mybatisを使い始めたばかりの時、霧のような感じがしました.データベースの実際の操作はすでにパッケージ化されていますので、パラメータが入るだけでいいですが、これらのパラメータはSQLとどのように関連していますか?パラメータの名前を使って関連しますか?もし複数のパラメータがあるなら?配列またはリストを使うなら?
各種パラメータの使用例を以下に示します.
//DAOの実現類:単一文字列. Benオブジェクト; Mapオブジェクト; 配列 List これらの使用方法は上のMapper.xmlで詳しく説明しましたが、なぜこのようにしますか?
上のDAO実装クラスでは、Sql Sessionを直接使用して操作します.まずSql Sessionインターフェースの定義を見てみます.
Sql Sessionはどのように一つのパラメータを使って多様なパラメータを満たすのですか?まずorg.aparthe.ibatis.scripting.defaults.Default Parameter Handlerを見にきます.
ブランチの状況:ダイナミック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に置いてください.
3、Sql Nodeの実現の中でONLで取り出して簡単なMapにカプセル化する:
各種パラメータの使用例を以下に示します.
//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>
上記の例では主に以下のパラメータの使用状況を示しています.上の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に決定します.ブランチの状況:
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の文法に基づいてこれらの値を取り出すことができます.