Mybatis使用纪要

  • Post author:
  • Post category:其他




一、mybatis生产问题记录



1、id由自增主键改为雪花算法

1)场景:

新建了一个表,开始设置为自增主键,后来考虑到数据迁移、合并数据自增主键会很麻烦,所以想改用雪花算法生成的id

2)问题:

第一步改数据库表设置,取消主键自增;这个没问题。

第二步,对应实体类,主键用mybatis的注解@TableId(type= IdType.ASSIGN_ID)设置为雪花算法生成。要用雪花算法生成主键,新增方法必须用mybatis自带的插入方法,手写sql则mybatis不会生成主键,会导致插入错误。

完成上述修改后,我以为就改好了,但是自测新增发现还是报错

SQLException: Field 'id' doesn't have a default value

意思是,没有传id值,表配置又不是自增主键,所以id为空、插入失败

3)解决方法:

经过排查我发现,我的新增接口调用的不是mybatis的basemapper中的新增方法。

我的mapper接口文件、xml文件,是用mybatis逆向工程插件生成的,可能是勾选了某些配置,它生成mapper接口问题、xml文件时,给里边添加了一些常用的增删改查方法;新增方法

insert(T entity);

与basemapper中的新增方法名称格式完全一样,当功能方法调用insert()新增方法时,会优先调用mapper中的方法(因为优先调用手写方法,而mapper中逆向工程生成的方法被认为是手写的);而这个逆向工程生成的新增方法没有插入id(一开始表是自增主键,id由数据库自动生成)。

所以解决方法就是,删掉逆向工程生成的insert方法(或者改名字,不要与basemapper插入方法一样)。



2、实体类中有表中没有的字段,导致使用basemapper新增方法插入失败

解决方法:使用mybatis注解

@TableField(exist = false)


将表中没有的字段进行非表字段标识



二、mybatis常用操作



1、使用resultMap标签,完成一对多合并查询,一个sql就把一个父对象、多个子对象映射到一个类中



1)场景

假设有一个指南(就是一系列课程),每个指南中包含多节课程;数据库中有两张表,一个指南表、一个课程表。

现在要指南列表,每个指南包含多节课程,数据要一起返回。

数据结构应该是一个指南List,每个指南中又包含一个课程List。

在这里插入图片描述



2)实现方式

方法一:先查询出指南列表,然后后台遍历这个列表,分别查询其下包含的课程。(太麻烦、不优雅,不推荐)

这样当然也可以实现,但是要连接非常多次数据库,很耗性能;而且很不优雅,一个sql可以搞定的事情,却要分为多次完成,不合适。

方法二:把结果放到一个表中,用mybatis的resultMap标签规定映射关系,得到我们想要的格式。(推荐)

首先,以指南为主表、关联课程表进行关联,目标是把结果放到一个表中,查询其结果如下

在这里插入图片描述

可以看到,前两条数据是一个指南、两节不同的课程;返回前端时,应该放到一个指南对象中,只是这个指南对象包含一个课程List、里面有两个课程元素。

目前的结构并不是我们想要的样子。

然后,使用mybatis的resultMap标签规定映射关系

  <resultMap type="com.hys.system.domain.vo.GuideCourseVo" id="guideCourseVoMap">
    <id column="g_id" jdbcType="VARCHAR" property="gId" />
    <result column="name" jdbcType="VARCHAR" property="guideName" />
    <result column="sort" jdbcType="INTEGER" property="sort" />
    <result column="status" jdbcType="CHAR" property="status" />
    <result column="del_flag" jdbcType="CHAR" property="delFlag" />
    <collection ofType="com.hys.system.domain.NhmsCourse" property="courseList">
      <id column="id" jdbcType="CHAR" property="id" />
      <result column="course_name" jdbcType="VARCHAR" property="courseName" />
      <result column="expert_name" jdbcType="VARCHAR" property="expertName" />
      <result column="expert_hospital" jdbcType="VARCHAR" property="expertHospital" />
      <result column="guide_id" jdbcType="VARCHAR" property="guideId" />
      <result column="cc_id" jdbcType="VARCHAR" property="ccId" />
      <result column="cc_site_id" jdbcType="VARCHAR" property="ccSiteId" />
      <result column="course_status" jdbcType="INTEGER" property="courseStatus" />
      <result column="course_sort" jdbcType="INTEGER" property="courseSort" />
      <result column="stat" jdbcType="INTEGER" property="stat" />
      <result column="create_time" jdbcType="DATE" property="createTime" />
      <result column="update_time" jdbcType="DATE" property="updateTime" />
      <result column="pic_path" jdbcType="VARCHAR" property="picPath" />
    </collection>
  </resultMap>

注意其中的collection标签,它的作用就是把课程数据放入List中;

最后,查询标签,返回属性选择resultMap、其值设置我们刚才写的的id

    <select id="getGuideCourseList" resultMap="guideCourseVoMap">
        SELECT g.id g_id, g.name, g.sort, g.del_flag,
        c.id, c.course_name, c.expert_name, c.expert_hospital, c.guide_id,
        c.cc_id, c.cc_site_id, c.course_status, c.course_sort, c.stat,
        c.create_time, c.update_time, c.pic_path
        FROM sys_guide g
        LEFT JOIN nhms_course c on g.id=c.guide_id
        WHERE g.`status`=0 AND g.del_flag=0
    </select>

打断点测试,可以看到数据已成我们想要的格式

在这里插入图片描述



3)细节补充

不知大家有没有注意到,resultMap中指南id的column我用的是别名’g_id’,而不是数据库字段名id。

在这里插入图片描述

这是因为,column对应的是查询结果(临时表)的字段名;示例中的查询,两张表的主键都是id、重名了,为了区分指南表的主键取了个别名g_id。

在这里插入图片描述

那么这时下的,column属性就要对应别名g_id。



2、对大于、小于等符号进行转义



1)问题描述

SQL语句中会有使用 >, <, >=, <= 的情况,这在数据库可以正常执行,但是当这样的SQL语句粘贴到 Mapper.xml 文件中时,会报错。

xml文件会把 <、> 当作标签符号, 会认为<是一个标签的开始,>是一个标签的结束。



2)解决方式

法一:对这些符号进行转义,对应关系如下:

字符 转义 描述
>
&gt;
>=
&gt;=
<
&lt;
<=
&lt;=

&quot;

&apos;
&
&amp;

法二:使用 <![CDATA[…]]> 标签包裹有这些符号sql语句

<![CDATA[…]]>是一种XML语法,他的作用是,可以忽略xml的转义

# 例如
select * from user where isDeleted = 0 
<if test=" age != 0 ">
	<![CDATA[ and  age < 18 ]]>
</if>

# 最后拼接为SQL语句为
select * from user where isDeleted = 0 and age < 18


注意



a、不要包裹if、foreach、where这些标签,像if、foreach、where等标签一但被 <![CDATA[ ]]>标签包裹,将忽略xml的解析并出错;

b、<![CDATA[ ]]>标签中不可嵌套<![CDATA[ ]]>标签;

c、<![CDATA[ ]]>尽量缩小范围,以免出错。



3、动态SQL



1)动态更新

# 示例
  <update id="updateNotNullPropertyById">
    update foucus_pic
    <set>
      <if test="params.picUrl != null and params.picUrl != ''">
        pic_url=#{params.picUrl},
      </if>
      <if test="params.operationType != null and params.operationType != ''">
        operation_type=#{params.operationType},
      </if>
      <if test="params.operationParam != null and params.operationParam != ''">
        operation_param=#{params.operationParam},
      </if>
      <if test="params.targetName != null and params.targetName != ''">
        target_name=#{params.targetName},
      </if>
      <if test="params.operationUrl != null and params.operationUrl != ''">
        operation_url=#{params.operationUrl},
      </if>
      <if test="params.clientType != null and params.clientType != ''">
        client_type=#{params.clientType},
      </if>
      <if test="params.sortNum != null and params.sortNum != ''">
        sort_num=#{params.sortNum},
      </if>
      <if test="params.areaId != null and params.areaId != ''">
        area_id=#{params.areaId},
      </if>
      <if test="params.deptId != null and params.operationType != ''">
        dept_id=#{params.deptId},
      </if>
    </set>
    where id=#{params.id}
  </update>



4、模糊查询

在mybatis的xml文件写模糊查询,不能直接写

where xxx like "%#{param}%"

因为引号内的部分会因为转义字符,导致识别不到占位符、进而把#{}当作普通字符串处理,导致sql出问题。

解决方式有多种,我这里只写最常用的一种:

# 用concat()函数
where xxx like concat('%', #{param}, '%')

# Oracle中的处理方式
where xxx like '%'||#{param}||'%'



版权声明:本文为Derek7117原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。