parameters in sql fragment substitute error - mybatis

I write a sql fragment, like this:
<sql id="day">
<choose>
<when test="${property} == 'day'">
substring(datachange_createtime, 1, 10)
</when>
<otherwise>
${property}
</otherwise>
</choose>
</sql>
and include this sql like this:
select * from my_table
<if test="groupBy != null">
group by
<foreach collection="groupBy" item="groupByAttr" separator=",">
<include refid="day">
<property name="property" value="groupByAttr"/>
</include>
</foreach>
</if>
But I got an error, mybatis didn't substitute property with a real parameter, but with groupByAttr. The result sql is like:
select * from my_table group by groupByAttr
In the otherwise clause, if I replace ${property} with #{${property}}, The result sql becomes:
select * from my_table group by ?
It is indeed correct, but for some reasons, I don't want a prepared sql.
I think the problem is the behavior of substitution in test and otherwise clauses is different.
Is the a bug, or just some mistakes I took? How to solve this problem?

Short answer:
You need to change the <include /> as follows.
<sql id="day">
<choose>
<when test="${property} == 'day'">
substring(datachange_createtime, 1, 10)
</when>
<otherwise>
\\${${property}}
</otherwise>
</choose>
</sql>
Long answer:
With your original code, the statement will look as follows after the <include /> substitution.
select * from my_table
<if test="groupBy != null">
group by
<foreach collection="groupBy" item="groupByAttr" separator=",">
<choose>
<when test="groupByAttr == 'day'">
substring(datachange_createtime, 1, 10)
</when>
<otherwise>
groupByAttr
</otherwise>
</choose>
</foreach>
</if>
As you can see, <when /> is OK, but <otherwise /> is not.
<otherwise /> should look like this:
<otherwise>
${groupByAttr}
</otherwise>
In the expression in the short answer \\${${property}}, the first two backslashes is the escape syntax which avoids the outer ${} from being treated as a variable.
Note that #{groupByAttr} does not work here because, in java.sql.PreparedStatement, a placeholder ? is not allowed for a table/column name.
Please see the wiki page for the details.

Related

Mybatis When test condition with Internal query

I am looking for a query where i can use either internal query returned value or direct SQL in when test= in Mybatis.
<foreach item="ID"
index="index"
collection="selectionIds"
separator=";">
UPDATE TABLE_1 SET
ACT_IND ='N', upd_by = 1234
WHERE SLCT_ID = #{ID}
AND rem= select REM from TABLE_1 where SLCT_ID=#{ID}
<choose>
<when test="rem == 3">
AND Bbsid=#{nsid}
</when>
<otherwise>
AND asid=#{asid}
</otherwise>
</choose>
</foreach>
From the above query, how can i get the rem value and use in when condition.
Thanks

Collection size in mybatis query

I have a MyBatis query that looks like this:
<if test="userIdList != null and userIdList > 0">
AND galleries.id IN (
SELECT gallery_id
FROM gallery_users
WHERE gallery_id IN (
<foreach collection="userIdList" item="item" separator="," open="(" close=")">
#{item}
</foreach>
)
GROUP BY gallery_id HAVING COUNT(gallery_id) = ???
)
</if>
That part I'm stuck on is getting the collection size which will be dynamic. So how can I get the collection size so that I can properly fill in the '???' value?
You can invoke the Collection#size() method using OGNL expression. i.e.
GROUP BY gallery_id HAVING COUNT(gallery_id) = ${userIdList.size}
Note that #{userIdList.size} won't work here because the expression in #{} is parsed by MyBatis' internal expression parser and not by OGNL.

Where to add a separator for mybatis collections(List)

Mapper.xml
<insert id="courseUploads" parameterType="com.technoshinelabs.eduskill.bean.Course" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
insert into
course_uploads
(course_id, training_provider_logo, assignment_material, course_image, trainer_image, created_date, updated_date)
values(
#{courseId},
<foreach item="Course" collection="trainingProviderPath" >
#{Course}
</foreach>,
<foreach item="Course" collection="assignmentMaterialPath">
#{Course}
</foreach>,
#{courseImagePath},
<foreach item="Course" collection="trainerImagePath">
#{Course}
</foreach>,
now(), now()
)
</insert>
How do I add a separator for the above list(item="Course"), this list will have multiple data, each data needs to be separated by some separator, well the data needs to be stored in the database, please do help me on this.
You can use the separator attribute.
e.g.)
<foreach item="Course" collection="foo" separator="||">
#{Course}
</foreach>
Specifically, what of SQL do you want to generate?

Dynamic SQL using mybatis not fetching the mail id

I am trying myBatis recently and I have been facing a problem when forming dynamic sqls.
When using myBatis to form dynamic sql like below, it works well with all the fields except when I send value in email field.
No data is fetched when emailId is passed. I have a doubt if the # symbol in emailId column is having a problem?
<select id = "getAllWithFilter" parameterType="java.util.Map" resultMap="result">
SELECT * FROM EMPLOYEE_LOOKUP
<where>
<if test = "firstName != null">
FIRST_NAME LIKE #{firstName}
</if>
<if test = "phoneNo != null">
AND PHONE LIKE #{phoneNo}
</if>
<if test = "emailId != null">
AND EMAIL LIKE #{emailId}
</if>
<if test = "analystGroup != null">
AND GROUP LIKE #{empGroup}
</if>
<choose>
<when test = "activeFlag != null">
AND EXPIRATION_DATE IS NULL
</when>
<when test = "inactiveFlag != null">
AND EXPIRATION_DATE IS NOT NULL
</when>
</choose>
</where>
</select>
This is the DEBUG Logs in myBatis - Log4J.
DEBUG [http-bio-9081-exec-1] - ==> Preparing: SELECT * FROM EMPLOYEE_LOOKUP WHERE EMAIL LIKE ? AND GROUP LIKE ? AND EXPIRATION_DATE IS NULL
DEBUG [http-bio-9081-exec-1] - ==> Parameters: abc(String), F(String)
Database, contains the following values
EMAIL | GROUP
----------------------
abc#gsf.com | F
You have simply forgotten the % surrounding the parameter value:
AND EMAIL LIKE '%' + #{emailId} + '%'
or
AND EMAIL LIKE '%' || #{emailId} || '%'
depending on DB vendor.
Alternatively, you can add the % in the param value after escaping those that might be in the param value.
otherwise it behaves as =

Mybatis mapper file, parameter was changed to null in foreach statement

Okay, I am trying to generate sql for a Map<String,List<String>>, I am having a nested foreach statements to generate sql. This is my mybatis xml mapper file:
<select id="UserScaleResult.listAccordingToScaleBrief" resultType="java.util.Map">
select distinct uu.id as uid, uu.fullname, ucn.card_number,
uu.mobile_number
from
<include refid="UserScaleResult.baseCondition.from" />
<include refid="UserScaleResult.UserType.from"/>
<include refid="UserScaleResult.listAccordingToScale.from"/>
<include refid="UserScaleResult.baseConditions.where" />
<include refid="UserScaleResult.UserType.where" />
<include refid="UserScaleResult.listAccordingToScale.where" />
limit #{offset}, #{pageSize}
</select>
<sql id="UserScaleResult.listAccordingToScale.where">
<if test="#Ognl#isNotEmpty(type)">
and musr.type = #{type}
</if>
<if
test="#Ognl#isNotEmpty(createTimeBegin) and #Ognl#isNotEmpty(createTimeEnd)">
and musr.create_time between #{createTimeBegin} and
#{createTimeEnd};
</if>
<if test="#Ognl#isNotEmpty(missionId)">
and musr.mission_id = #{missionId}
</if>
<if test="#Ognl#isNotEmpty(scaleId)">
and musr.scale_id = #{scaleId}
</if>
<if test="#Ognl#isNotEmpty(sdl)">
and (
<foreach collection="sdl.entrySet()" item="item" open="(" separator=") or (" close=")">
tsrdl.dimension_id = #{item.key}
<if test="#Ognl#isNotEmpty(item.value)">
and
<foreach collection="item.value" item="iitem" open="(" separator=" or " close=")">
tsrdl.level_id = #{iitem}
</foreach>
</if>
</foreach>
)
</if>
</sql>
The log shows the #{iitem} are all null when it put it down to sql, although the number of parameters are correct:
2016-05-13 14:47:27,837 DEBUG [java.sql.Connection] - <==> Preparing: select count(*) from user_user uu left join user_user_detail uud on uu.id = uud.user_id left join user_card_number ucn on uu.card_number_id =ucn.id left join org_org oo on ucn.org_id = oo.id inner join (SELECT user_id, MAX(IF(extension_item_id=?,DATA,'') )AS ? FROM user_extension_item_data GROUP BY user_id) AS ud on uu.id = ud.user_id inner join mission_user_scale_result musr on uu.id = musr.user_id inner join test_scale ts on musr.scale_id = ts.id inner join test_scale_result_dimension_level tsrdl on tsrdl.result_id = musr.id and ud.3 = ? and ( ( tsrdl.dimension_id = ? and ( tsrdl.level_id = ? or tsrdl.level_id = ? ) ) or ( tsrdl.dimension_id = ? and ( tsrdl.level_id = ? or tsrdl.level_id = ? or tsrdl.level_id = ? ) ) ) >
2016-05-13 14:47:27,838 DEBUG [java.sql.PreparedStatement] - <==> Parameters: 3(String), 3(String), 保密(String), 42(String), null, null, 47(String), null, null, null>
EDIT:
Just did some experiment, when I substitute the '#' sign in #{iitem} with '$', the value appears and the sql gets parameters all good.
We need to use ${dataBaseTable} instead of #. The difference is that # is used for PreparedStatement substitution. $ is for direct String substitution.
Replace your #{iitem} with ${iitem}
....
<foreach collection="item.value" item="iitem" open="(" separator=" or " close=")">
tsrdl.level_id = ${iitem}
</foreach>
....