一、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)解决方式
法一:对这些符号进行转义,对应关系如下:
字符 | 转义 | 描述 |
---|---|---|
> |
|
|
>= |
|
|
< |
|
|
<= |
|
|
” |
|
|
’ |
|
|
& |
|
法二:使用 <![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}||'%'