MyBatis使用过程中的注意点

1. 模糊查询

 在查询时使用动态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

发现%'鼠标'%已经变成了%鼠标%

【解决】

  1. 使用'%${name}%'解决,但是${}可能引发SQL注入的危险,不推荐使用;
  2. 使用"%"#{name}"%"解决;
  3. 使用MySQL中自带的CONCAT(str1,str2,...)函数进行字符串拼接(和java中的可变参数一个含义),改为concat('%', concat(#{name},'%'))解决;

2. 关于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>

上述定义了两个resultMapBaseResultMap拥有rfa_id`update_time`7个字段,而`DCBaseResultMap`使用`extends`关键字继承了`BaseResultMap`,那此时`DCBaseResultMap`将拥有`rfa_id`update_time7个字段,此外还有自己独有的download_count~is_mobile3个字段。

3. 关于“Specified class is an interface”

在启动Springboot项目时出现下面的异常:

org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.goujianwu.core.dao.mapper.ContributionMapper]: Specified class is an interface

服务中依赖中有重复同名的mapper文件。

4. 插入记录时获取自增主键的值

业务中,遇到场景:在插入一条记录时,要获取该记录的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"

5. 让MySQL按照指定的id顺序返回

严格来说,这应该是MySQL的问题,只是顺便记一下,场景:在MySQL中作in条件查询时,如下:

SELECT
	*
FROM
	store
WHERE
	id IN (560684, 560007, 560678)

返回的结果是依次是id=560007id=560678id=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>

6. 特殊字符的处理

mapper文件中的特殊字符必须使用包裹。

你可能感兴趣的:(mybatis)