传统的配置文件xml很必要(查询修改)

业务场景

若业务查询条件为排列组合条件的时候,就最好写xml,全写Plus代码会使得逻辑复杂
比如过滤查询场景…
xml还是很强大的.

业务场景

image.png
过滤和列表查询应该合并为一个接口,为什么?
因为我们的分行业数据是点一页返回一页的,不输入分页信息默认返回第一页
如果过滤功能的查询返回结果不止一页,那么用户查看第二页的过滤结果的时候,他点击页码2跟他不过滤查询点击页码2调用的肯定是同一个查询接口,所以合并起来写

SQL基础分页方式

mybatis接口

  1. List<TaiModelChange> filterAndLikeQuery(TaiModelChangeQryRequest taiModelChangeQryRequest);

xml文件,$可以做计算

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  3. <mapper namespace="com.iwhalecloud.aiFactory.aiResource.modelChange.dao.TaiModelChangeMapper">
  4. <select id="filterAndLikeQuery" parameterType="com.iwhalecloud.aiFactory.aiResource.modelChange.vo.TaiModelChangeNewRequest" resultType="com.iwhalecloud.aiFactory.aiResource.modelChange.dto.TaiModelChange" useCache="false">
  5. SELECT * FROM tai_model_change t
  6. <where>
  7. <if test="name != null and name != ''" >
  8. and t.name LIKE concat('%',#{name},'%')
  9. </if>
  10. <if test="status != null and status != ''" >
  11. and t.status = #{status}
  12. </if>
  13. </where>
  14. ORDER BY
  15. t.CREATE_TIME DESC
  16. LIMIT ${(pageInfo.current-1)*pageInfo.size} , #{pageInfo.size}
  17. </select>
  18. </mapper>


实现类

  1. /**
  2. * @Author: liuyan
  3. * @Date: 2021/8/5 14:30
  4. */
  5. @Service
  6. public class TaiModelChangeServiceImpl implements TaiModelChangeService {
  7. @Autowired
  8. TaiModelChangeMapper taiModelChangeMapper;
  9. @Autowired
  10. TaiModelTamplateValMapper taiModelTamplateValMapper;
  11. @Autowired
  12. TaiModelTemplateAttrMapper taiModelTemplateAttrMapper;
  13. @Autowired
  14. TaiModelChangeTemplateMapper taiModelChangeTemplateMapper;
  15. @Autowired
  16. InferenceTaiModelMapper inferenceTaiModelMapper;
  17. /**
  18. * 列表展示 与 过滤查询 二合一
  19. *
  20. * @param taiModelChangeQryRequest
  21. * @return
  22. */
  23. @Override
  24. public PageInfo<TaiModelChangeResponse> getChangeModel(TaiModelChangeQryRequest taiModelChangeQryRequest) {
  25. Page pageInfo = taiModelChangeQryRequest.getPageInfo();
  26. if (pageInfo == null) {
  27. pageInfo = new Page();
  28. pageInfo.setCurrent(0);
  29. pageInfo.setSize(10);
  30. taiModelChangeQryRequest.setPageInfo(pageInfo);
  31. }
  32. List<TaiModelChange> taiModelChanges = taiModelChangeMapper.filterAndLikeQuery(taiModelChangeQryRequest);
  33. ArrayList<TaiModelChangeResponse> taiModelChangeResponses = new ArrayList<>();
  34. Iterator<TaiModelChange> iterator = taiModelChanges.iterator();
  35. while (iterator.hasNext()) {
  36. TaiModelChange next = iterator.next();
  37. TaiModelChangeResponse taiModelChangeResponse = queryChangeModel(next);
  38. taiModelChangeResponses.add(taiModelChangeResponse);
  39. }
  40. PageInfo<TaiModelChangeResponse> taiModelChangeResponsePageInfo = new PageInfo<>(pageInfo.getCurrent(), pageInfo.getSize(), pageInfo.getTotal(), taiModelChangeResponses);
  41. return taiModelChangeResponsePageInfo;
  42. }
  43. }

实体类Page:

  1. import com.baomidou.mybatisplus.plugins.Page;

实体类PageInfo:

  1. package com.iwhalecloud.aiFactory.common.dto;
  2. import lombok.ToString;
  3. import java.io.Serializable;
  4. import java.util.Collections;
  5. import java.util.List;
  6. @ToString
  7. public class PageInfo<T> implements Serializable {
  8. /**
  9. * 总数
  10. */
  11. private int total = 0;
  12. /**
  13. * 每页显示条数,默认 10
  14. */
  15. private int size = 10;
  16. /**
  17. * 总页数
  18. */
  19. private int pages = 0;
  20. /**
  21. * 当前页
  22. */
  23. private int current = 1;
  24. private int pageCount; // 总页数
  25. /**
  26. * 查询数据列表
  27. */
  28. private List<T> list = Collections.emptyList();
  29. public PageInfo() {
  30. }
  31. public PageInfo(int current, int size) {
  32. this.size = size;
  33. this.current = current;
  34. }
  35. public PageInfo(int current, int size, int total, List<T> list) {
  36. this.total = total;
  37. this.size = size;
  38. this.current = current;
  39. this.list = list;
  40. this.pages = (total + size - 1) / size;
  41. }
  42. public int getTotal() {
  43. return total;
  44. }
  45. public PageInfo<T> setTotal(int total) {
  46. this.total = total;
  47. pages = (total + size - 1) / size;
  48. return this;
  49. }
  50. public int getSize() {
  51. return size;
  52. }
  53. public PageInfo<T> setSize(int size) {
  54. this.size = size;
  55. pages = (total + size - 1) / size;
  56. return this;
  57. }
  58. public int getPages() {
  59. return pages;
  60. }
  61. public int getCurrent() {
  62. return current;
  63. }
  64. public PageInfo<T> setCurrent(int current) {
  65. this.current = current;
  66. return this;
  67. }
  68. public List<T> getList() {
  69. return list;
  70. }
  71. public PageInfo<T> setList(List<T> list) {
  72. this.list = list;
  73. return this;
  74. }
  75. public int getPageCount() {
  76. return pageCount;
  77. }
  78. public void setPageCount(int pageCount) {
  79. this.pageCount = pageCount;
  80. }
  81. }

Mybatis 进行数值计算

场景:分页的时候,传入参数为当前页数,每页数量,使用limt时要自己计算初始下标。
使用 $,用{}将要计算的数据包起来。

  1. LIMIT ${pageNum * pageSize}, #{callRecordQueryDto.pageSize}

#、$的区别:
#{ }是预编译处理,MyBatis在处理#{ }时,会将sql中的#{ }替换为?,然后调用PreparedStatement的set方法来赋值,传入字符串后,会在值两边加上单引号,如传入1,2,3就会变成’1,2,3’。
${ }是字符串替换,MyBatis在处理${ }时,会将sql中的${ }替换为变量的值,传入的数据不会在两边加上单引号。

注意:使用${ }会导致sql注入,不利于系统的安全性! SQL注入:就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。常见的有匿名登录(在登录框输入恶意的字符串)、借助异常获取数据库信息等

一般能用#的就别用$
$方式一般用于传入数据库对象,例如传入字段名、表名等,例如order by ${column}。
$方式还用于传递数值计算公式,因为#方式时预编译处理PreparedStatement,只能识别字段?代表已有字段,所以识别不了计算公式引用。

标准的两层数据模型【新增】架构

dto文件夹:Dto(Bean)不必多说
vo文件夹:MsgTaskRequest,继承dto MsgTask,可以封装额外的集合

两层数据结构定义

  1. //MsgTask略
  2. //MsgUser略
  3. @Data
  4. public class MsgTaskRequest extends MsgTask {
  5. private List<MsgUser> msgUser;
  6. }

持久层与业务层实现

Mapper

  1. 无需定义,serviceimpl层使用Plus-Mapper自带接口实现插入,使用Plus-IService自带接口实现批量插入,

Service事务控制

  1. public interface MsgTaskService extends IService<MsgTask> {
  2. public void msgTaskAdd(MsgTaskRequest msgTaskRequest);
  3. }
  4. package com.iwhalecloud.aiFactory.aiResource.aimessage.service.impl;
  5. import java.util.ArrayList;
  6. import java.util.Date;
  7. import java.util.List;
  8. import com.baomidou.mybatisplus.plugins.Page;
  9. import com.baomidou.mybatisplus.service.impl.ServiceImpl;
  10. import com.iwhalecloud.aiFactory.aiResource.aimessage.dao.MsgLogMapper;
  11. import com.iwhalecloud.aiFactory.aiResource.aimessage.dto.MsgLog;
  12. import com.iwhalecloud.aiFactory.aiResource.aimessage.dto.MsgTask;
  13. import com.iwhalecloud.aiFactory.aiResource.aimessage.dto.MsgUser;
  14. import com.iwhalecloud.aiFactory.aiResource.aimessage.dao.MsgUserMapper;
  15. import com.iwhalecloud.aiFactory.aiResource.aimessage.middle.MsgTaskConditionMiddle;
  16. import com.iwhalecloud.aiFactory.aiResource.aimessage.middle.MsgTaskSendDetailMiddle;
  17. import com.iwhalecloud.aiFactory.aiResource.aimessage.service.MsgTaskService;
  18. import com.iwhalecloud.aiFactory.aiResource.aimessage.dao.MsgTaskMapper;
  19. import com.iwhalecloud.aiFactory.aiResource.aimessage.service.MsgUserService;
  20. import com.iwhalecloud.aiFactory.aiResource.aimessage.vo.*;
  21. import com.iwhalecloud.aiFactory.common.util.BeanUtil;
  22. import org.apache.bcel.generic.IF_ACMPEQ;
  23. import org.springframework.beans.factory.annotation.Autowired;
  24. import org.springframework.stereotype.Service;
  25. import org.springframework.transaction.annotation.Transactional;
  26. /**
  27. * @author hasee
  28. * @description 针对表【msg_task】的数据库操作Service实现
  29. * @createDate 2022-04-18 15:06:27
  30. */
  31. @Service
  32. public class MsgTaskServiceImpl extends ServiceImpl<MsgTaskMapper, MsgTask>
  33. implements MsgTaskService {
  34. @Autowired
  35. MsgTaskMapper msgTaskMapper;
  36. @Autowired
  37. MsgUserMapper msgUserMapper;
  38. @Autowired
  39. MsgLogMapper msgLogMapper;
  40. @Override
  41. @Transactional("resourceTransactionManager")
  42. public void msgTaskAdd(MsgTaskRequest msgTaskRequest) {
  43. List<MsgUser> msgUserList = msgTaskRequest.getMsgUser();
  44. MsgTask msgTask = new MsgTask();
  45. msgTask.setMsgTmplId(msgTaskRequest.getMsgTmplId());
  46. msgTask.setMsgTaskTitle(msgTaskRequest.getMsgTaskTitle());
  47. msgTask.setPlatformContent(msgTaskRequest.getPlatformContent());
  48. msgTask.setDingdingContent(msgTaskRequest.getDingdingContent());
  49. msgTask.setMsgContent(msgTaskRequest.getMsgContent());
  50. msgTask.setMsgType(msgTaskRequest.getMsgType());
  51. msgTask.setCreateTime(new Date());
  52. msgTask.setSendChannel(msgTaskRequest.getSendChannel());
  53. msgTask.setMsgTaskParam(msgTaskRequest.getMsgTaskParam());
  54. msgTask.setSuccessNum(msgTaskRequest.getSuccessNum());
  55. msgTask.setAttribute(msgTaskRequest.getAttribute());
  56. msgTask.setSendStatus(MsgTask.CONST_NOSEND_STATUS);
  57. msgTask.setSceneType(msgTaskRequest.getSceneType());
  58. msgTaskMapper.insert(msgTask);
  59. //TODO 发送时间由前端定时触发
  60. Integer msgTaskId = msgTask.getMsgTaskId();
  61. msgUserList.stream().forEach(e->{
  62. e.setMsgTaskId(msgTaskId);
  63. msgUserMapper.insert(e);
  64. });
  65. // msgUserService.insertBatch(msgUserList);
  66. for (MsgUser user:msgUserList) {
  67. String msgContentReplaced = replaceContent(msgTaskRequest, user)[0];
  68. MsgLog msgLog = new MsgLog();
  69. msgLog.setMsgTaskId(msgTaskId);
  70. msgLog.setUserId(user.getMsgUserId());
  71. msgLog.setMsgTitle(msgTaskRequest.getMsgTaskTitle());
  72. msgLog.setMsgContent(msgContentReplaced);
  73. msgLog.setMsgType(msgTaskRequest.getMsgType());
  74. msgLog.setMsgChannel(msgTaskRequest.getSendChannel());
  75. msgLog.setMsgState(MsgLog.HAVED_NO_READ);
  76. msgLog.setReceiveTime(new Date());
  77. msgLog.setSendState(MsgLog.SEND_STATUS_SUCCESS);
  78. msgLog.setCreateTime(new Date());
  79. msgLog.setUpdateTime(new Date());
  80. msgLog.setStatus(MsgLog.DELETE_STATUS_VALIDATED);
  81. msgLogMapper.insert(msgLog);
  82. }
  83. }
  84. public String[] replaceContent(MsgTaskRequest msgTaskRequest,MsgUser user){
  85. String platformContentReplaced= null;
  86. String dingdingContentReplaced= null;
  87. String msgContentReplaced = null;
  88. String userName = user.getUserName();
  89. if(msgTaskRequest.getPlatformContent()!=null){
  90. platformContentReplaced = msgTaskRequest.getPlatformContent().replace(MsgTask.CONTENT_VAR_USERNAME,userName);
  91. System.out.println("replace"+platformContentReplaced);
  92. }
  93. if(msgTaskRequest.getDingdingContent()!=null){
  94. dingdingContentReplaced = msgTaskRequest.getDingdingContent().replace(MsgTask.CONTENT_VAR_USERNAME,userName);
  95. }
  96. if(msgTaskRequest.getMsgContent()!=null){
  97. msgContentReplaced = msgTaskRequest.getMsgContent().replace(MsgTask.CONTENT_VAR_USERNAME,userName);
  98. }
  99. return new String[]{platformContentReplaced,dingdingContentReplaced,msgContentReplaced};
  100. }
  101. }

标准的三层数据模型【查询】架构

复杂且标准的三层结构如下: Entity:实体 Bo:business object业务层对象【也就是中间层middle】,接收业务连表聚合返回 Vo:表示层对象,Bo的扩充就是最终的Response


  • 正常应该是Bean Dto Vo(req,response)
  • 结果项目里是把Dto当作Bean来用了
  • 所以我自己加了层Middle作为Dto,手写Mapper和XML去映射连接表Middle

    注意:若是单表查询,直接写vo与逻辑业务mapper返回即可,因为不考虑连接查询的结果映射所以不需要新增中间层模型middle。也无需手写Mapper接口与XML


  • 已知标准的两层数据模型新增架构中,新增接口的Request建议继承实体,对新增请求的参数封装
  • 查询接口的Request无需继承实体且不建议继承Page,而是自定义currentsize字段,不然手写分页SQL实现会导致框架出错

dto文件夹:Dto(Bean)不必多说
middle文件夹:MsgTaskConditionMiddle具体业务SQL的返回结果有时候无法完全构成最终的响应结果(有些数据不在数据库Invalid bound statement),所以需要这一中间层要求字段与SQL返回字段严格映射。手写Mapper接口与XML
vo文件夹:MsgTaskConditionRequestMsgTaskConditionResponse,有middle则MsgTaskConditionResponse继承MsgTaskConditionMiddle,没有middle则MsgTaskConditionResponse继承Entity即可,属于HTTP层模型

三层数据结构定义:

  1. //dto略
  2. @Data
  3. public class MsgTaskConditionMiddle implements Serializable {
  4. private String msgTaskTitle;
  5. private String sendTime;
  6. private String msgType;
  7. private String sendChannel;
  8. }
  9. @Data
  10. public class MsgTaskConditionRequest implements Serializable {
  11. //分页参数
  12. private Integer current;
  13. private Integer size;
  14. //实际参数
  15. private String msgTaskTitle;
  16. private String headTime;
  17. private String tailTime;
  18. private String msgType;
  19. public static final String CONST_MSGTYPE_PLATFORM = "platform";
  20. public static final String CONST_MSGTYPE_ENT = "ent";
  21. }
  22. @Data
  23. public class MsgTaskConditionResponse extends MsgTaskConditionMiddle implements Serializable {
  24. //目标数
  25. private Integer targetUserCounts;
  26. //发送人
  27. private String sendBy;
  28. }

持久层与业务层实现:

Mapper

  1. @Mapper
  2. public interface MsgTaskMapper extends BaseMapper<MsgTask> {
  3. public List<MsgTaskConditionMiddle> getTaskPage(MsgTaskConditionRequest msgTaskConditionRequest);
  4. }
  5. <?xml version="1.0" encoding="UTF-8"?>
  6. ...
  7. <select id="getTaskPage"
  8. resultType="com.iwhalecloud.aiFactory.aiResource.aimessage.middle.MsgTaskConditionMiddle">
  9. SELECT a.msg_task_title,a.send_time,a.msg_type,a.send_channel
  10. FROM msg_task a
  11. <where>
  12. <if test="headTime != null and headTime != '' and tailTime != null and tailTime != ''">
  13. and a.send_time between #{headTime} and #{tailTime}
  14. </if>
  15. <if test="msgTaskTitle != null and msgType != ''">
  16. and a.msg_task_title = #{msgTaskTitle}
  17. </if>
  18. <if test="msgType != null and msgType != ''">
  19. and a.msg_type = #{msgType}
  20. </if>
  21. </where>
  22. ORDER BY
  23. a.CREATE_TIME DESC
  24. LIMIT ${(current-1)*size},#{size}
  25. </select>
  26. </mapper>
  27. ...

Service

  1. public interface MsgTaskService extends IService<MsgTask> {
  2. public List<MsgTaskConditionResponse> getTaskPage(MsgTaskConditionRequest msgTaskConditionRequest);
  3. }
  4. /**
  5. * @author hasee
  6. * @description 针对表【msg_task】的数据库操作Service实现
  7. * @createDate 2022-04-18 15:06:27
  8. */
  9. @Service
  10. public class MsgTaskServiceImpl extends ServiceImpl<MsgTaskMapper, MsgTask>
  11. implements MsgTaskService {
  12. @Autowired
  13. MsgTaskMapper msgTaskMapper;
  14. @Override
  15. public List<MsgTaskConditionResponse> getTaskPage(MsgTaskConditionRequest msgTaskConditionRequest) {
  16. if (msgTaskConditionRequest.getSize()==null){
  17. int defaultSize = 10;
  18. msgTaskConditionRequest.setSize(defaultSize);
  19. }
  20. if (msgTaskConditionRequest.getCurrent()==null){
  21. msgTaskConditionRequest.setCurrent(1);
  22. }
  23. List<MsgTaskConditionMiddle> msgTaskConditionMiddles = msgTaskMapper.getTaskPage(msgTaskConditionRequest);
  24. ArrayList<MsgTaskConditionResponse> responses = new ArrayList<>();
  25. for (MsgTaskConditionMiddle msgTaskConditionMiddle:msgTaskConditionMiddles) {
  26. MsgTaskConditionResponse msgTaskConditionResponse = new MsgTaskConditionResponse();
  27. BeanUtil.copyProperties(msgTaskConditionMiddle,msgTaskConditionResponse);
  28. msgTaskConditionResponse.setTargetUserCounts(100);
  29. }
  30. return responses;
  31. }
  32. }

写XML,SQL语句怎么写?

下面就来看一下MySQL的SQL执行顺序吧。

SQL执行顺序

  1. <select id="getSendDetailPage"
  2. resultType="com.iwhalecloud.aiFactory.aiResource.aimessage.middle.MsgTaskSendDetailMiddle">
  3. SELECT b.user_code,b.user_name,c.send_time,a.send_state,a.msg_state
  4. FROM msg_log a LEFT JOIN msg_user b ON a.user_id = b.msg_user_id
  5. LEFT JOIN msg_task c ON a.msg_task_id = c.msg_task_id
  6. <where>
  7. <if test="sendChannel != null and sendChannel != ''">
  8. and a.msg_channel = #{sendChannel}
  9. </if>
  10. <if test="sendStatus != null and sendStatus != ''">
  11. and a.send_state = #{sendStatus}
  12. </if>
  13. </where>
  14. ORDER BY
  15. a.CREATE_TIME DESC
  16. LIMIT ${(current-1)*size},#{size}
  17. </select>

WHERE过滤条件

等于

  1. <select id="getSendDetailPage"
  2. resultType="com.iwhalecloud.aiFactory.aiResource.aimessage.middle.MsgTaskSendDetailMiddle">
  3. SELECT b.user_code,b.user_name,c.send_time,a.send_state,a.msg_state
  4. FROM msg_log a LEFT JOIN msg_user b ON a.user_id = b.msg_user_id
  5. LEFT JOIN msg_task c ON a.msg_task_id = c.msg_task_id
  6. <where>
  7. <if test="sendChannel != null and sendChannel != ''">
  8. and a.msg_channel = #{sendChannel}
  9. </if>
  10. <if test="sendStatus != null and sendStatus != ''">
  11. and a.send_state = #{sendStatus}
  12. </if>
  13. </where>
  14. ORDER BY
  15. a.CREATE_TIME DESC
  16. LIMIT ${(current-1)*size},#{size}
  17. </select>

between

  1. <select id="getTaskPage"
  2. resultType="com.iwhalecloud.aiFactory.aiResource.aimessage.middle.MsgTaskConditionMiddle">
  3. SELECT a.msg_task_title,a.send_time,a.msg_type,a.send_channel
  4. FROM msg_task a
  5. <where>
  6. <if test="headTime != null and headTime != '' and tailTime != null and tailTime != ''">
  7. and a.send_time between #{headTime} and #{tailTime}
  8. </if>
  9. <if test="msgTaskTitle != null and msgType != ''">
  10. and a.msg_task_title = #{msgTaskTitle}
  11. </if>
  12. <if test="msgType != null and msgType != ''">
  13. and a.msg_type = #{msgType}
  14. </if>
  15. </where>
  16. ORDER BY
  17. a.CREATE_TIME DESC
  18. LIMIT ${(current-1)*size},#{size}
  19. </select>

like

  1. <select id="filterAndLikeQuery" parameterType="com.iwhalecloud.aiFactory.aiResource.modelChange.vo.TaiModelChangeQryRequest" resultType="com.iwhalecloud.aiFactory.aiResource.modelChange.dto.TaiModelChange" useCache="false">
  2. SELECT * FROM tai_model_change t
  3. <where>
  4. <if test="name != null and name != ''" >
  5. and t.name LIKE concat('%',#{name},'%')
  6. </if>
  7. <if test="status != null and status != ''" >
  8. and t.status = #{status}
  9. </if>
  10. <if test="modeChangeType != null and modeChangeType != ''">
  11. and t.mode_change_type = #{modeChangeType}
  12. </if>
  13. <if test="createStaffId != null">
  14. and create_staff_id = #{createStaffId}
  15. </if>
  16. </where>
  17. ORDER BY
  18. t.CREATE_TIME DESC
  19. </select>

GROUP和HAVING

GROUP BY语法可以根据给定数据列的每个成员对查询结果进行分组统计,最终得到一个分组汇总表。
select子句中的列名必须为分组列或列函数,列函数对于group by子句定义的每个组返回一个结果。

某个员工信息表结构和数据如下:

  1. id name dept salary edlevel hiredate
  2. 1 张三 开发部 2000 3 2009-10-11
  3. 2 李四 开发部 2500 3 2009-10-01
  4. 3 王五 设计部 2600 5 2010-10-02
  5. 4 王六 设计部 2300 4 2010-10-03
  6. 5 马七 设计部 2100 4 2010-10-06
  7. 6 赵八 销售部 3000 5 2010-10-05
  8. 7 钱九 销售部 3100 7 2010-10-07
  9. 8 孙十 销售部 3500 7 2010-10-06

我想列出每个部门最高薪水的结果,sql语句如下:

  1. select dept , max(salary) AS MAXIMUM
  2. FROM STAFF
  3. GROUP BY DEPT
  4. 查询结果如下:
  5. dept MAXIMUM
  6. 开发部 4500
  7. 设计部 2600
  8. 销售部 3500
  9. 解释一下这个结果:
  10. 1 满足“SELECT子句中的列名必须为分组列或列函数”,因为SELECTgroup by中包含的列dept;
  11. 2、“列函数对于group by子句定义的每个组各返回一个结果”,根据部门分组,对每个部门返回一个结果,就是每个部门的最高薪水。

将where子句与group by子句一起使用
分组查询可以在形成组和计算列函数之前具有消除非限定行的标准where子句。必须在group by子句之前指定where子句
例如,查询公司2010年入职的各个部门每个级别里的最高薪水

  1. SELECT dept,edlevel,MAX(salary) AS MAXIMUM
  2. FROM STAFF
  3. WHERE hiredate > '2010-01-01'
  4. GROUP BY dept,edlevel
  5. 查询结果如下:
  6. dept edlevel MAXIMUM
  7. 设计部 4 2300
  8. 设计部 5 2600
  9. 销售部 5 3000
  10. 销售部 7 3500

在SELECT语句中指定的每个列名也在GROUP BY子句中提到,未在这两个地方提到的列名将产生错误。
GROUP BY子句要对dept和edlevel的每个唯一组合各返回一行。


GROUP BY子句之后使用Having子句
可应用限定条件进行分组,以便系统仅对满足条件的组返回结果。因此,在GROUP BY子句后面包含了一个HAVING子句。
HAVING类似于WHERE(唯一的差别是WHERE过滤行,HAVING过滤组)HAVING支持所有WHERE操作符。
例如,查找雇员数超过2个的部门的最高和最低薪水:

  1. SELECT dept ,MAX(salary) AS MAXIMUM ,MIN(salary) AS MINIMUM
  2. FROM STAFF
  3. GROUP BY dept
  4. HAVING COUNT(*) > 2
  5. ORDER BY dept
  6. 查询结果如下:
  7. dept MAXIMUM MINIMUM
  8. 设计部 2600 2100
  9. 销售部 3500 3000

例如,查找雇员平均工资大于3000的部门的最高薪水和最低薪水:

  1. SELECT dept,MAX(salary) AS MAXIMUM,MIN(salary) AS MINIMUM
  2. FROM STAFF
  3. GROUP BY dept
  4. HAVING AVG(salary) > 3000
  5. ORDER BY dept
  6. 查询结果如下:
  7. dept MAXIMUM MINIMUM
  8. 销售部 3500 3000

写XML,SQL标签怎么写?

if-test

  1. <select id="getSendDetailPage"
  2. resultType="com.iwhalecloud.aiFactory.aiResource.aimessage.middle.MsgTaskSendDetailMiddle">
  3. SELECT b.user_code,b.user_name,c.send_time,a.send_state,a.msg_state
  4. FROM msg_log a LEFT JOIN msg_user b ON a.user_id = b.msg_user_id
  5. LEFT JOIN msg_task c ON a.msg_task_id = c.msg_task_id
  6. <where>
  7. <if test="sendChannel != null and sendChannel != ''">
  8. and a.msg_channel = #{sendChannel}
  9. </if>
  10. <if test="sendStatus != null and sendStatus != ''">
  11. and a.send_state = #{sendStatus}
  12. </if>
  13. </where>
  14. ORDER BY
  15. a.CREATE_TIME DESC
  16. LIMIT ${(current-1)*size},#{size}
  17. </select>

choose (when, otherwise)标签

  1. 有时候我们并不想应用所有的条件,而只是想从多个选项中选择一个。而使用if标签时,只要test中的表达式为 true,就会执行 if 标签中的条件。MyBatis 提供了 choose 元素。if标签是与(and)的关系,而 choose 是或(or)的关系。<br /> choose标签是按顺序判断其内部when标签中的test条件出否成立,如果有一个成立,则 choose 结束。当 choose 中所有 when 的条件都不满则时,则执行 otherwise 中的sql。类似于Java switch 语句,choose switchwhen caseotherwise 则为 default。<br /> 例如下面例子,同样把所有可以限制的条件都写上,方面使用。choose会从上到下选择一个when标签的testtruesql执行。安全考虑,我们使用wherechoose包起来,防止关键字多余错误。
  1. <!-- choose(判断参数) - 按顺序将实体类 User 第一个不为空的属性作为:where条件 -->
  2. <select id="getUserList_choose" resultMap="resultMap_user" parameterType="com.yiibai.pojo.User">
  3. SELECT *
  4. FROM User u
  5. <where>
  6. <choose>
  7. <when test="username !=null ">
  8. u.username LIKE CONCAT(CONCAT('%', #{username, jdbcType=VARCHAR}),'%')
  9. </when >
  10. <when test="sex != null and sex != '' ">
  11. AND u.sex = #{sex, jdbcType=INTEGER}
  12. </when >
  13. <when test="birthday != null ">
  14. AND u.birthday = #{birthday, jdbcType=DATE}
  15. </when >
  16. <otherwise>
  17. </otherwise>
  18. </choose>
  19. </where>
  20. </select>

foreach模板

你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach

  • 当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。
  • 当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。

    separator=” , “—in

    属性separator 为逗号,前段传过来的myList 参数是list集合
  1. <if test="myList != null">
  2. AND dm in
  3. <foreach collection="myList " item="item" open="(" separator="," close=")">
  4. #{item , jdbcType=VARCHAR }
  5. </foreach>
  6. </if>
  7. 最后渲染为sql语句为
  8. AND dm in ( '03' , '04')

separator=” OR “

属性separator 为or,前端传过来的myList 参数是list集合

  1. <if test="myList != null">
  2. AND
  3. <foreach collection="myList " index="index" item="item" open="(" separator="or" close=")">
  4. dm = #{item , jdbcType=VARCHAR }
  5. </foreach>
  6. </if>
  7. 最后渲染为sql语句为
  8. AND ( dm = '01'or dm = '02' or dm = '03')

【Mybatis连接查询】

一直对association和collection有点混淆,现整理一篇文章,用于加强记忆。
现在有2个表book表、bookshelf书架表。

BOOK
字段名称 类型 备注
id int 主键
name varchar 书名
type int 类型
shelf_id int 书架id
Book_shelf
字段名称 类型 备注
id int 主键
number varchar 书架编号
num int 可存放数量

association【嵌套对象】

association用于一对一、多对一场景使用。
现有需求:查询根据书籍id查询书籍信息和所在书架编号。
Book.PoJo

  1. public class Book{
  2. private Integer id;
  3. private String name;
  4. private String type;
  5. private Integer shelfId;
  6. private BookShelf bookShelfDto;
  7. }

BookShelf.Pojo

  1. public class BookShelf {
  2. private Integer id;
  3. private String number;
  4. private String num;
  5. }

mapper:type=”com.abc.Book”Bo

  1. <resultMap id="bookResultMap" type="com.abc.Book">
  2. <id property="id" column="id"/>
  3. <result property="name" column="name"/>
  4. <result property="type" column="type"/>
  5. <!--关联属性-->
  6. <association property="bookShelfDto" ofType="com.abc.BookShelf">
  7. <id property="id" column="shelf_id"/>
  8. <result property="number" column="number"/>
  9. <result property="num" column="num"/>
  10. </association>
  11. </resultMap>
  12. <select id="getBookInfo" resultMap="bookResultMap">
  13. select book.id,book.name,book.type,book.shelf_id,shelf.number,shelf.num
  14. from book left join book_shelf shelf on book.shelf_id = shelf.id
  15. where book.id = #{id}
  16. </select>

collection【嵌套集合】

应用场景为一对多关系,即实体里放集合。
表不变
现有需求:根据书架ID查询书架信息及书架存放的书籍信息。
Book.POJO

  1. public class Book{
  2. private Integer id;
  3. private String name;
  4. private String type;
  5. private Integer shelfId;
  6. }

BookShelf.Pojo

  1. public class BookShelf {
  2. private Integer id;
  3. private String number;
  4. private String num;
  5. private List<Book> bookList;
  6. }

mapper

  1. <resultMap id="bookShelfResultMap" type="com.abc.BookShelf">
  2. <id property="id" column="shelf_id"/>
  3. <result property="number" column="number"/>
  4. <result property="num" column="num"/>
  5. <!--关联属性-->
  6. <collection property="bookList" javaType="com.abc.Book">
  7. <id property="id" column="id"/>
  8. <result property="name" column="name"/>
  9. <result property="type" column="type"/>
  10. </collection>
  11. </resultMap>
  12. <select id="getBookShelfInfo" resultMap="bookShelfResultMap">
  13. select book.id,book.name,book.type,book.shelf_id,shelf.number,shelf.num
  14. from book left join book_shelf shelf on book.shelf_id = shelf.id
  15. where shelf.id = #{id}
  16. </select>
  17. Mapper.java
  18. BookShelf getBookShelfInfo(Integer id);

排列组合【无嵌套连接】

任务场景:1条任务发送给N个用户,并且保存消息日志。但是1条任务当中有M种消息内容

  • A表一条数据有m种内容字段
  • A表与B表是常规的1对多关系

  • C表的最终消息日志入库:由于是排列组合,所以最终的入库结果就是mn条记录。*第三张表是用来算乘积结果的

image.png
数据库建表

  1. --A
  2. CREATE TABLE `msg_task` (
  3. `msg_task_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '消息任务id',
  4. `msg_tmpl_id` int(11) DEFAULT NULL COMMENT '消息模板 id',
  5. `msg_task_title` varchar(256) DEFAULT NULL COMMENT '消息任务标题',
  6. `platform_content` varchar(1024) DEFAULT NULL COMMENT '平台消息任务内容模板',
  7. `dingding_content` varchar(1024) DEFAULT NULL COMMENT '钉钉消息模板内容',
  8. `msg_content` varchar(256) DEFAULT NULL COMMENT '短息内容',
  9. `msg_type` varchar(32) DEFAULT NULL COMMENT '消息类型,数据字典维护: 平台=platform,企业=ent',
  10. `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  11. `update_time` datetime DEFAULT NULL COMMENT '修改时间',
  12. `send_time` datetime DEFAULT NULL COMMENT '发送时间',
  13. `send_channel` varchar(32) DEFAULT NULL COMMENT '发送渠道,多个用逗号分隔.数据字典维护:平台=platform_channel,钉钉=dingding_channel,短信=msg_channel',
  14. `msg_task_param` varchar(256) DEFAULT NULL COMMENT '消息参数,json 存储',
  15. `success_num` int(11) DEFAULT NULL COMMENT '失败数量',
  16. `attribute` varchar(256) DEFAULT NULL COMMENT '扩展字段',
  17. `send_status` varchar(32) DEFAULT NULL COMMENT '发送状态,已发送=1,未发送=0',
  18. `scene_type` varchar(32) DEFAULT NULL COMMENT '场景类型,具体场景和环节使用对应的类型前缀,用点拼接.系统=sys,安全=security,审核=audit;',
  19. `send_user_id` bigint(20) DEFAULT NULL COMMENT '发送人ID',
  20. `send_user_name` varchar(20) DEFAULT NULL COMMENT '发送人名称',
  21. PRIMARY KEY (`msg_task_id`)
  22. ) ENGINE=InnoDB AUTO_INCREMENT=121 DEFAULT CHARSET=utf8;
  23. --B表:
  24. CREATE TABLE `msg_user` (
  25. `msg_user_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '消息发送用户 id',
  26. `msg_task_id` int(11) DEFAULT NULL COMMENT '消息任务 id',
  27. `user_name` varchar(32) DEFAULT NULL COMMENT '用户名称',
  28. `user_code` varchar(32) DEFAULT NULL COMMENT '用户编码',
  29. `phone_num` varchar(32) DEFAULT NULL COMMENT '用户电话',
  30. `dingding_code` varchar(64) DEFAULT NULL COMMENT '钉钉账号',
  31. `attribute` varchar(256) DEFAULT NULL COMMENT '扩展属性',
  32. PRIMARY KEY (`msg_user_id`)
  33. ) ENGINE=InnoDB AUTO_INCREMENT=173 DEFAULT CHARSET=utf8;
  34. --C
  35. CREATE TABLE `msg_log` (
  36. `msg_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '消息id',
  37. `msg_task_id` bigint(20) NOT NULL COMMENT '消息任务id',
  38. `user_id` int(11) DEFAULT NULL COMMENT '接收人 id',
  39. `msg_title` varchar(256) DEFAULT NULL COMMENT '消息标题',
  40. `msg_content` varchar(1024) DEFAULT NULL COMMENT '消息内容',
  41. `msg_type` varchar(32) DEFAULT NULL COMMENT '消息类型, 数据字典维护:平台=platform,企业=ent',
  42. `msg_channel` varchar(32) DEFAULT NULL COMMENT '发送渠道,数据字典维护:平台=platform_channel,钉钉=dingding_channel,短信=msg_channel',
  43. `msg_state` varchar(32) DEFAULT NULL COMMENT '消息状态,数据字典维护:read = 已读, unread =未读',
  44. `receive_time` datetime DEFAULT NULL COMMENT '接收时间',
  45. `send_state` varchar(32) DEFAULT NULL COMMENT '发送状态,数据字典维护:success=成功,fail=失败',
  46. `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  47. `update_time` datetime DEFAULT NULL COMMENT '修改时间',
  48. `status` varchar(255) DEFAULT NULL COMMENT '删除状态,有效 = 00A,失效 = 00X',
  49. `attribute` varchar(256) DEFAULT NULL COMMENT '扩展属性',
  50. `scene_type` varchar(255) DEFAULT NULL COMMENT '场景类型,具体场景和环节使用对应的类型前缀,用点拼接.系统=sys,安全=security,审核=audit.xxx.xxxx;',
  51. PRIMARY KEY (`msg_id`)
  52. ) ENGINE=InnoDB AUTO_INCREMENT=172 DEFAULT CHARSET=utf8;

数据结构定义:无嵌套数据结构

  1. package com.iwhalecloud.aiFactory.aiResource.aimessage.bo;
  2. import com.iwhalecloud.aiFactory.aiResource.aimessage.dto.MsgLog;
  3. import lombok.Data;
  4. import java.io.Serializable;
  5. import java.util.Date;
  6. @Data
  7. public class MsgTaskSendDetailBo extends MsgLog implements Serializable {
  8. //user
  9. String userCode;
  10. String userName;
  11. //task
  12. Date sendTime;
  13. //log
  14. String sendState;
  15. String msgState;
  16. }

SQL:无嵌套连接

  1. <select id="getSendDetailPage"
  2. resultType="com.iwhalecloud.aiFactory.aiResource.aimessage.bo.MsgTaskSendDetailBo">
  3. SELECT b.user_code,b.user_name,c.send_time,a.send_state,a.msg_state,a.attribute
  4. FROM msg_log a LEFT JOIN msg_user b ON a.user_id = b.msg_user_id
  5. LEFT JOIN msg_task c ON a.msg_task_id = c.msg_task_id
  6. <where>
  7. <if test="msgTaskId != null and msgTaskId != ''">
  8. and c.msg_task_id = #{msgTaskId}
  9. </if>
  10. <if test="sendChannel != null and sendChannel != ''">
  11. and a.msg_channel = #{sendChannel}
  12. </if>
  13. <if test="sendStatus != null and sendStatus != ''">
  14. and a.send_state = #{sendStatus}
  15. </if>
  16. and a.status = "00A"
  17. </where>
  18. ORDER BY
  19. a.CREATE_TIME DESC
  20. </select>

传入列表实现批量查询

item内单条件查询

我知道如何只有一个入参的list,可以使用类似下面的语句实现

  1. public List<XXXBean> getXXXBeanList(List<String> list);

xml

  1. <select id="getXXXBeanList" resultType="XXBean">
  2. select 字段... from XXX where id in
  3. <foreach item="item" index="index" collection="list"
  4. open="(" separator="," close=")">
  5. #{item}
  6. </foreach>
  7. </select>

item内多条件查询

但是传入多个条件的list, 在mybatis中的xml怎么写?麻烦指导下。
问题描述:比如有一张用户表,有以下字段:
id:主键
userName:用户名
userAge:用户年龄
userAddress:用户住址
我想根据id和userName两个条件构造出一个list入参,实现批量查询
SQL:

  1. select * from user where (user_id,type) in ((568,6),(569,6),(600,8));

MyBatis XML:

  1. select date_format(create_Time,'%Y-%m-%d %H:%i:%s') as createTime ,reason,operator,remarks,operation
  2. from refund_order_log
  3. where (order_no,channel) in
  4. <foreach collection="list" item="item" open="(" close=")" separator=",">
  5. (#{item.orderNo},#{item.channel})
  6. </foreach>
  7. and status =1
  8. order by create_time desc

连接查询出规格参数【自定义映射规则】

查询结果自定义封装

  1. @Data
  2. @ToString
  3. public class SpuItemAttrGroupVo {
  4. private String groupName;
  5. private List<Attr> attrs;
  6. }

查询语句

  1. List<SpuItemAttrGroupVo> getAttrGroupWithAttrsBySpuId(@Param("spuId") Long spuId, @Param("catalogId") Long catalogId);

根据业务条件正确的查询出满足要求的结果,然后取出想要的resultMap返回

  1. <resultMap id="spuAttrGroup" type="com.xunqi.gulimall.product.vo.SpuItemAttrGroupVo">
  2. <result property="groupName" column="attr_group_name"/>
  3. <collection property="attrs" ofType="com.xunqi.gulimall.product.vo.Attr">
  4. <result property="attrId" column="attr_id"></result>
  5. <result property="attrName" column="attr_name"></result>
  6. <result property="attrValue" column="attr_value"></result>
  7. </collection>
  8. </resultMap>
  9. <select id="getAttrGroupWithAttrsBySpuId" resultMap="spuAttrGroup">
  10. SELECT
  11. product.spu_id,
  12. pag.attr_group_id,
  13. pag.attr_group_name,
  14. product.attr_id,
  15. product.attr_name,
  16. product.attr_value
  17. FROM
  18. pms_product_attr_value product
  19. LEFT JOIN pms_attr_attrgroup_relation paar ON product.attr_id = paar.attr_id
  20. LEFT JOIN pms_attr_group pag ON paar.attr_group_id = pag.attr_group_id
  21. WHERE
  22. product.spu_id = #{spuId}
  23. AND pag.catelog_id = #{catalogId}
  24. </select>

连接查询当前spu对应的skus【分组技巧】

未写分组之前

image.png

写上分组

查询结果自定义封装

  1. <resultMap id="skuItemSaleAttrVo" type="com.xunqi.gulimall.product.vo.SkuItemSaleAttrVo">
  2. <result column="attr_id" property="attrId"></result>
  3. <result column="attr_name" property="attrName"></result>
  4. <collection property="attrValues" ofType="com.xunqi.gulimall.product.vo.AttrValueWithSkuIdVo">
  5. <result column="attr_value" property="attrValue"></result>
  6. <result column="sku_ids" property="skuIds"></result>
  7. </collection>
  8. </resultMap>

SkuItemSaleAttrVo

  1. @Data
  2. @ToString
  3. public class SkuItemSaleAttrVo {
  4. private Long attrId;
  5. private String attrName;
  6. private List<AttrValueWithSkuIdVo> attrValues;
  7. }

AttrValueWithSkuIdVo

  1. @Data
  2. public class AttrValueWithSkuIdVo {
  3. private String attrValue;
  4. private String skuIds;
  5. }

查询语句

  1. List<SkuItemSaleAttrVo> getSaleAttrBySpuId(@Param("spuId") Long spuId);
  1. <resultMap id="skuItemSaleAttrVo" type="com.xunqi.gulimall.product.vo.SkuItemSaleAttrVo">
  2. <result column="attr_id" property="attrId"></result>
  3. <result column="attr_name" property="attrName"></result>
  4. <collection property="attrValues" ofType="com.xunqi.gulimall.product.vo.AttrValueWithSkuIdVo">
  5. <result column="attr_value" property="attrValue"></result>
  6. <result column="sku_ids" property="skuIds"></result>
  7. </collection>
  8. </resultMap>
  9. <select id="getSaleAttrBySpuId" resultMap="skuItemSaleAttrVo">
  10. SELECT
  11. ssav.attr_id attr_id,
  12. ssav.attr_name attr_name,
  13. ssav.attr_value,
  14. group_concat( DISTINCT info.sku_id ) sku_ids
  15. FROM
  16. pms_sku_info info
  17. LEFT JOIN pms_sku_sale_attr_value ssav ON ssav.sku_id = info.sku_id
  18. WHERE
  19. info.spu_id = #{spuId}
  20. GROUP BY
  21. ssav.attr_id,
  22. ssav.attr_name,
  23. ssav.attr_value
  24. </select>

GROUP BY:mysql规定select里的字段必须存在分组下面写出 ssav.attr_id attr_id,ssav.attr_name attr_name,ssav.attr_value,
未写出的也想要拿到怎么办?group_concat:为返回的select字段新增一个函数处理

SQL语法的规定,用了group by,则select之后的字段除聚合函数外都必须出现在group by中, 你可以少于group by中的字段,但不能包含group by中没有的字段

image.png

方便sku切换:查询结果中任选一个颜色对应的skuids和内存对应的skuids
将两个ids集合求交集,就能够确定出唯一的一个sku进行切换

yml

  1. spring:
  2. datasource:
  3. resource:
  4. type: jdbc
  5. driver-class-name: com.mysql.jdbc.Driver
  6. url: jdbc:mysql://172.21.72.163:3306/ai_factory_resource_test?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowMultiQueries=true
  7. username: ai_platform_test
  8. password: ai_platform_test
  9. initial-size: 5
  10. max-active: 30
  11. min-idle: 5
  12. max-wait: 60000
  13. validation-query: SELECT 1
  14. validationQueryTimeout: 5
  15. encrypt: false
  16. test-on-borrow: false
  17. test-while-idle: true
  18. mybatis-plus:
  19. resource:
  20. global-config:
  21. db-column-underline: true
  22. configuration:
  23. map-underscore-to-camel-case: true
  24. cache-enabled: false
  25. database-id: mysql
  26. mapper-locations: classpath*:mapper/**/*Mapper.xml
  27. type-aliases-package: com.iwhalecloud.aiFactory.aiResource.*.dto

常用注解

@TableName

  • 描述:表名注解 | 属性 | 类型 | 必须指定 | 默认值 | 描述 | | —- | —- | —- | —- | —- | | value | String | 否 | “” | 表名 | | schema | String | 否 | “” | schema | | keepGlobalPrefix | boolean | 否 | false | 是否保持使用全局的 tablePrefix 的值(如果设置了全局 tablePrefix 且自行设置了 value 的值) | | resultMap | String | 否 | “” | xml 中 resultMap 的 id | | autoResultMap | boolean | 否 | false | 是否自动构建 resultMap 并使用(如果设置 resultMap 则不会进行 resultMap 的自动构建并注入) | | excludeProperty | String[] | 否 | {} | 需要排除的属性名(@since 3.3.1) |

关于autoResultMap的说明:
mp会自动构建一个ResultMap并注入到mybatis里(一般用不上).
下面讲两句: 因为mp底层是mybatis,所以一些mybatis的常识你要知道,mp只是帮你注入了常用crud到mybatis里 注入之前可以说是动态的(根据你entity的字段以及注解变化而变化),但是注入之后是静态的(等于你写在xml的东西) 而对于直接指定typeHandler,mybatis只支持你写在2个地方:

  1. 定义在resultMap里,只作用于select查询的返回结果封装
  2. 定义在insertupdatesql的#{property}里的property后面(例:#{property,typehandler=xxx.xxx.xxx}),只作用于设置值 而除了这两种直接指定typeHandler,mybatis有一个全局的扫描你自己的typeHandler包的配置,这是根据你的property的类型去找typeHandler并使用.

@TableId

mybatis_plus 默认会使用 “id” 为主键字段,若你的主键不叫id,则需加上@TableId(value =“数据库你的主键字段”)注解即可

  1. value:指定数据表主键字段名
  2. type:指定数据表主键类型,如:ID自增、UUID等。该属性的值是一个 IdType 枚举类型,默认为 IdType.NONE

IdType:

描述
AUTO 数据库ID自增
NONE 无状态,该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)
INPUT insert前自行set主键值
ASSIGN_ID 分配ID(主键类型为Number(Long和Integer)或String)(since 3.3.0),使用接口IdentifierGenerator
的方法nextId
(默认实现类为DefaultIdentifierGenerator
雪花算法)
ASSIGN_UUID 分配UUID,主键类型为String(since 3.3.0),使用接口IdentifierGenerator
的方法nextUUID
(默认default方法)
ID_WORKER 分布式全局唯一ID 长整型类型(please use ASSIGN_ID
)
UUID 32位UUID字符串(please use ASSIGN_UUID
)
ID_WORKER_STR 分布式全局唯一ID 字符串类型(please use ASSIGN_ID
)

数据库设置自增

注意:如果使用@TableId(value = “id”, type = IdType.AUTO),
数据库表主键字段一定不要忘记设置自增:
当我用SQLyog尝试修改已有记录的mysql数据表的主键为自动增长时,报出以下错误
ALTER TABLE causes auto_increment resequencing, resulting in duplicate entry ’1′ for key ‘PRIMARY’
解决方法:
第1步:将主键字段值为0的那条记录值改为其他大于0且不重复的任意数
第2步:修改主键字段为auto_increment
第3步:把刚才修改过的那条记录的值还原

数据库主键名称

项目:Springboot mybatis-plus mysql
今天项目中新建了一张表,表的主键是area_code,在程序中直接使用mybatis-plus内置的selectById方法进行表数据查询,查询时直接报错: 运行时异常

  1. : Invalid bound statement (not found): com.huanong.avatar.shuidi.mapper.SdWeatherThresholdMapper.selectById

原因
mybatis的selectById方法默认使用的主键名是id,而数据库中表的id是area_code,所以查询时会报错
解决
使用注解的方式,更改mybatis的selectById方法查询主键的名称即可,不多说,直接上代码
@TableId(“area_code”)
private String areaCode;
注:@TableId里面的字段需要同数据库的字段保持一致,不能同程序一致,
比如数据库的表字段area_code,程序是areaCode,注解中则需要写area_code,不然执行sql还会报错

  1. org.springframework.jdbc.BadSqlGrammarException:
  2. ### Error querying database. Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'areacode' in 'field list'

@TableField

Mybatis plus 中数据库中字段有SQL关键字的处理方法
在实体类关键字字段的位置使用注解:
@TableField(“‘关键字字段名’”)

  1. @TableField("'number'")
  2. private String number;

描述:字段注解(非主键)

属性 类型 必须指定 默认值 描述
value String “” 数据库字段名
el String “” 映射为原生 #{ ... }
逻辑,相当于写在 xml 里的 #{ ... }
部分
exist boolean true 是否为数据库表字段
condition String “” 字段 where
实体查询比较条件,有值设置则按设置的值为准,没有则为默认全局的 %s=#{%s}
,参考(opens new window)
update String “” 字段 update set
部分注入, 例如:update=”%s+1”:表示更新时会set version=version+1(该属性优先级高于 el
属性)
insertStrategy Enum N DEFAULT 举例:NOT_NULL: insert into table_a(<if test="columnProperty != null">column</if>) values (<if test="columnProperty != null">#{columnProperty}</if>)
updateStrategy Enum N DEFAULT 举例:IGNORED: update table_a set column=#{columnProperty}
whereStrategy Enum N DEFAULT 举例:NOT_EMPTY: where <if test="columnProperty != null and columnProperty!=''">column=#{columnProperty}</if>
fill Enum FieldFill.DEFAULT 字段自动填充策略
select boolean true 是否进行 select 查询
keepGlobalFormat boolean false 是否保持使用全局的 format 进行处理
jdbcType JdbcType JdbcType.UNDEFINED JDBC类型 (该默认值不代表会按照该值生效)
typeHandler Class<? extends TypeHandler> UnknownTypeHandler.class 类型处理器 (该默认值不代表会按照该值生效)
numericScale String “” 指定小数点后保留的位数

bean当中添加数据库当中不存在的字段名
其中@JsonInclude(JsonInclude.Include.NON_EMPTY)表示,如果返回值当中该字段为空,则不显示该字段

  1. @JsonInclude(JsonInclude.Include.NON_EMPTY)
  2. @TableField(exist=false)
  3. private List<CategoryEntity> children;

关于jdbcTypetypeHandler以及numericScale的说明:
numericScale只生效于 update 的sql. jdbcTypetypeHandler如果不配合@TableName#autoResultMap = true一起使用,也只生效于 update 的sql. 对于typeHandler如果你的字段类型和set进去的类型为equals关系,则只需要让你的typeHandler让Mybatis加载到即可,不需要使用注解

FieldStrategy

描述
IGNORED 忽略判断
NOT_NULL 非NULL判断
NOT_EMPTY 非空判断(只对字符串类型字段,其他类型字段依然为非NULL判断)
DEFAULT 追随全局配置

FieldFill

描述
DEFAULT 默认不处理
INSERT 插入时填充字段
UPDATE 更新时填充字段
INSERT_UPDATE 插入和更新时填充字段

@Version

  • 描述:乐观锁注解、标记 @Verison 在字段上

    @EnumValue

    描述:通枚举类注解(注解在枚举字段上)

    @TableLogic

  • 描述:表字段逻辑处理注解(逻辑删除) | 属性 | 类型 | 必须指定 | 默认值 | 描述 | | —- | —- | —- | —- | —- | | value | String | 否 | “” | 逻辑未删除值 | | delval | String | 否 | “” | 逻辑删除值 |

@KeySequence

  • 描述:序列主键策略 oracle
  • 属性:value、resultMap | 属性 | 类型 | 必须指定 | 默认值 | 描述 | | —- | —- | —- | —- | —- | | value | String | 否 | “” | 序列名 | | clazz | Class | 否 | Long.class | id的类型, 可以指定String.class,这样返回的Sequence值是字符串”1” |

@OrderBy

  • 描述:内置 SQL 默认指定排序,优先级低于 wrapper 条件查询 | 属性 | 类型 | 必须指定 | 默认值 | 描述 | | —- | —- | —- | —- | —- | | isDesc | boolean | 否 | 是 | 是否倒序查询 | | sort | short | 否 | Short.MAX_VALUE | 数字越小越靠前 |

代码规范

Mapper上加@Repository注解,不然autowired报错