Mybatis使用纪要

一、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中的方法(优先调用手写方法,逆向工程生成的方法被认为是手写的);而这个逆向工程生成的新增方法没有插入id(一开始表是自增主键,id由数据库自动生成)。

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

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

解决方法:使用mybatis注解@TableField(exist = false)
将表中没有的字段进行非表字段标识

3、自增主键的表,手动插入几条数据后,页面正常新增报id已存在、新增失败

这种情况是因为手动插入,占据了自增的主键;
对于自增主键,这一次id是几,是上一次新增时就计算出来的,存在缓存中;应该是mysql和mybatis各自都有自己的计算、自己的缓存,一般情况下两者是一样的(计算方式一样),但是手动新增,只会修改mysql下一次的自增主键,mybatis没改,所以页面新增调用mybatis新增接口取的自增主键,其实以及被前面手动新增占用了,这时就会报id已存在。

解决方法:重启一下后台就好了,mybatis重启的时候会更新缓存的下次id,这次更新应该是去查了mysql,重启完再新增就是最新的、正确的自增id了。

4、新增太快,雪花算法生成的id会出现负数、重复

雪花算法生成id和时间有关,mybatis雪花算法生成id出现重复、负数,都是时间导致的。
比如循环创建某表数据。

5、mybatisplus新增,对象字段无值也会对该字段执行新增。

1)可能现象
报错stat不能为null;
stat虽然不能为null,但数据库表设置了默认值,按理说新增时不赋值就是想用默认值,怎么mybatis新增方法会对为空的字段进行新增呢?
2)原因
mybatisplus新增方法save()或者saveOrUpdate()等,底层都是调用mapper的insert()方法;
单数plus的调用逻辑是,优先调用mapper中手写的方法;
用mybatisplus逆向工程插件生成mapper文件、实体类等时,如果不选mybatis3,生成的mapper接口会自动给加一些增删改查的方法,这些带的方法,就有一个instert()方法,这个方法是固定对所有字段进行新增。

其实如果不选mybatis3,生成的mapper文件也不会继承BaseMapper类,也就是说本来没办法用mabatisplus的接口;为了方便,手动继承BaseMapper、IService等接口,但是没有删除mapper中自动生成的insert()等方法,就会出现上述问题;
3)解决
要么不用mybatisplus的新增方法,自己手写,或者调用mapper自动生成的insertSlective()接口;
要么就把mapper()中的insert()接口删除掉。

二、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}||'%'
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值