在查询时使用动态SQL,其中有一个需求是可以根据名字进行模糊匹配,我在映射文件mapper
是这么写的:
<select id="list" resultMap="BaseResultMap">
select 'false' as QUERYID,
<include refid="Base_Column_List"/>
from activity_award where 1=1
<if test="name!=null">
and name like %#{name}%
if>
<if test="status!=null">
and status = #{status,jdbcType=TINYINT}
if>
order by create_time desc
<if test="offset!=null and pageSize != null">
limit #{offset,jdbcType=INTEGER},
#{pageSize,jdbcType=INTEGER}
if>
select>
测试时出现SQL错误,如下:
### SQL: select 'false' as QUERYID, award_id, activity_id, name, limit_count, amount, price, completed_amount, status, create_time, update_time from activity_award where 1=1 and name like %?% order by create_time desc limit ?,?
### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '%'鼠标'% order by create_time desc' at line 9
可以发现在接口传过来的参数name=鼠标
传过来被转成了一个字符串%'鼠标'%
,而不是预期的%鼠标%
,想到SQL注入时$
和#
差异时,这种场景不需要它帮我作转换处理,是什么就是什么,不要加''
,料想此时选用${}
的方式就可以解决这个问题,将上述的%#{name}%
改成%${name}%
还是报错:
### SQL: select 'false' as QUERYID, award_id, activity_id, name, limit_count, amount, price, completed_amount, status, create_time, update_time from activity_award where 1=1 and name like %鼠标% order by create_time desc limit ?,?
### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '%鼠标% order by create_time desc' at line 9
发现%'鼠标'%
已经变成了%鼠标%
,
【解决】
'%${name}%'
解决,但是${}
可能引发SQL注入的危险,不推荐使用;"%"#{name}"%"
解决;CONCAT(str1,str2,...)
函数进行字符串拼接(和java中的可变参数一个含义),改为concat('%', concat(#{name},'%'))
解决;resultMap
如果Mapper文件中需要多个返回值类型(这里只讨论返回对象类型,比如A和B两个对象),并且存在A对象的属性上加上几个属性后就是B对象,那在定义A的resultMap后,可以在定义B的resultMap时
继承A的resultMap,然后再添加几个额外属性即可,如:
<resultMap id="BaseResultMap" type="com.xx.common.dao.model.RfaPublic">
<result column="rfa_id" jdbcType="BIGINT" property="rfaId"/>
<result column="name" jdbcType="VARCHAR" property="name"/>
<result column="category_id" jdbcType="INTEGER" property="categoryId"/>
<result column="score" jdbcType="REAL" property="score"/>
<result column="level" jdbcType="TINYINT" property="level"/>
<result column="create_time" jdbcType="TIMESTAMP" property="createTime"/>
<result column="update_time" jdbcType="TIMESTAMP" property="updateTime"/>
resultMap>
<result column="download_count" jdbcType="INTEGER" property="downloadCount"/>
<result column="owner" jdbcType="VARCHAR" property="owner"/>
<result column="is_mobile" jdbcType="TINYINT" property="isMobile"/>
resultMap>
上述定义了两个resultMap
,BaseResultMap
拥有rfa_id
`update_time`7个字段,而`DCBaseResultMap`使用`extends`关键字继承了`BaseResultMap`,那此时`DCBaseResultMap`将拥有`rfa_id`update_time
7个字段,此外还有自己独有的download_count
~is_mobile
3个字段。
在启动Springboot项目时出现下面的异常:
org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.goujianwu.core.dao.mapper.ContributionMapper]: Specified class is an interface
服务中依赖中有重复同名的mapper文件。
业务中,遇到场景:在插入一条记录时,要获取该记录的id去作其他事情,由于id采用自增策略,只能通过数据库获取,想想总不能先插入再查询吧(这特么也太挫了……),MyBatis可以在插入的同时返回主键,修改逆向工程的insert
方法,作如下配置:
<insert id="insert" parameterType="com.***.core.dao.model.Topic" useGeneratedKeys="true" keyProperty="id">
insert into topic (id, channel_id, title,
status, pv, `like`, thumbnail,
type, create_time, update_time,
publish_time, summary, content
)
values (#{id,jdbcType=INTEGER}, #{channelId,jdbcType=INTEGER}, #{title,jdbcType=VARCHAR},
#{status,jdbcType=TINYINT}, #{pv,jdbcType=INTEGER}, #{like,jdbcType=INTEGER}, #{thumbnail,jdbcType=VARCHAR},
#{type,jdbcType=TINYINT}, #{createTime,jdbcType=TIMESTAMP}, #{updateTime,jdbcType=TIMESTAMP},
#{publishTime,jdbcType=TIMESTAMP}, #{summary,jdbcType=LONGVARCHAR}, #{content,jdbcType=LONGVARCHAR}
)
insert>
重点是在标签中加上useGeneratedKeys="true" keyProperty="id"
id
顺序返回严格来说,这应该是MySQL的问题,只是顺便记一下,场景:在MySQL中作in
条件查询时,如下:
SELECT
*
FROM
store
WHERE
id IN (560684, 560007, 560678)
返回的结果是依次是id=560007
,id=560678
,id=560684
的3条记录,和传入的560684, 560007, 560678
不一致,事实是in
查询在扫表的时候是先将记录按id
排序,然后再查询的,所以给出来的查询结果是按id
排序的,但业务场景中又确实有按照传入id
的顺序返回,MySQL提供了一些方法:
# 方法1
SELECT
*
FROM
store
WHERE
id IN (560684, 560007, 560678)
ORDER BY
FIELD(id, 560684, 560007, 560678)
# 方法2
SELECT
*
FROM
store
WHERE
id IN (560684, 560007, 560678)
ORDER BY
FIND_IN_SET(id, '560684, 560007, 560678')
注意方式1不需要在id上加单引号,方式2需要加单引号'
,实际MyBatis中我采用方式1,写法如下:
<select id="selectByMultiId" resultMap="BaseResultMap">
select <include refid="Base_Column_List" />
from store where id in
<foreach collection="collection" item="id" separator="," open="(" close=")">
#{id,jdbcType=BIGINT}
foreach>
order by field
<foreach collection="collection" item="id" separator="," open="(id," close=")">
#{id,jdbcType=BIGINT}
foreach>
select>
mapper
文件中的特殊字符必须使用包裹。