欢迎访问悦橙教程(wld5.com),关注java教程。悦橙教程  java问答|  每日更新
页面导航 : > > 文章正文

mybatisplus开发过程中遇到的问题记录及解决,

来源: javaer 分享于  点击 13334 次 点评:146

mybatisplus开发过程中遇到的问题记录及解决,


目录
  • 一、使用 mp 生成代码时
    • 1、使用saveOrUpdateBatch或者saveBatch等新增修改方法时
    • 2、生成实体类时
    • 3、引用第2钟错误,当我们使用 mp 生成代码的时候,只想生成实体类
  • 二、使用 mp 封装的方法时
    • 1、使用修改方法,修改对象某一字段为null
  • 三、自定义 sql 查询
    • 1、mapper 接口中注解查询
  • 四、使用 mybatis plus 性能分析插件
    • 总结

      本文主要记录本人使用 mybatis plus 开发过程中碰到的问题,以及解决方案

      ## 以下 mybatis plus 统一简称 mp

      一、使用 mp 生成代码时

      1、使用saveOrUpdateBatch或者saveBatch等新增修改方法时

      问题情况:

      MybatisPlusException: error: can not execute. because can not find column for id from entity

      原因:

      不能执行。因为无法从实体中找到id列使用 mp 自动生成代码时,可能有这一行代码,会导致不生成主键 ID,变成自定义基础的Entity类,公共字段

      strategy.setSuperEntityColumns("id")

      解决方法:

      将上面这行代码注释即可

      可能生成 ID 之后任旧无法执行,检查下面的原因

      可能会存在实体类属性与数据库字段不一致的情况,所以我们在使用 mp 生成实体类时,最好在生成策略中加上这个行

      strategy.setEntityTableFieldAnnotationEnable(true);

      目的:生成之后的实体类中的每个属性会多出一个注解,来用于属性和数据库字段的对应

       @TableField("id")

      2、生成实体类时

      数据库中的 int 类型 ID 变成了 String

      问题情况:

      原因及解决方法:

      我这里是 Mysql 数据库,生成代码时,做数据库类型转换时,原本选择的是 Oracle 数据库,改成 Mysql 数据的类型就可以

      3、引用第2钟错误,当我们使用 mp 生成代码的时候,只想生成实体类

      问题情况:

      引用第2钟错误,当我们使用 mp 生成代码的时候,可能实体类或者某一个文件中的代码生成的有问题,需要重新生成,但是又不想覆盖其他 Controller 、Mapper 等文件,我们如何选择只生成实体类?

      解决方法:

      我百度搜过,大佬们生成代码时,基本上都是生成所有的文件,所以我就自己开始研究。

      请看下面这段代码,大家应该都明白这是什么意思,就是让我们生成的 mapper.xml 生成到我们指定的 resource 的文件夹下面,那么它原本的 xml 为什么不会生成了呢?

              // 自定义输出配置
              List<FileOutConfig> focList = new ArrayList<>();
              // 自定义配置会被优先输出
              focList.add(new FileOutConfig(templatePath) {
                  @Override
                  public String outputFile(TableInfo tableInfo) {
                      // 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
                      return projectPath + "/src/main/resources/mapperWorkDiscovery/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
                  }
              });
              cfg.setFileOutConfigList(focList);
              mpg.setCfg(cfg);
              // 配置模板
              TemplateConfig templateConfig = new TemplateConfig();
              //让默认生成 mapper 的目录不再生成
              templateConfig.setXml(null);
              mpg.setTemplate(templateConfig);

      关键点在这一行代码

      templateConfig.setXml(null);

      在配置模板的时候,我们设置了模板的 xml 为null,那么在我们改变 xml 的生成路径之后,原本的xml 就不会自己生成了,所以我想会不会有 .setMapper(null) 、 .setController(null) 的方法呢,于是我就自己尝试了一下,加上这几行代码

      templateConfig.setMapper(null);
      templateConfig.setController(null);
      templateConfig.setService(null);
      templateConfig.setServiceImpl(null);

      这样重新生成之后,就只会生成 Entity 实体类了。

      二、使用 mp 封装的方法时

      1、使用修改方法,修改对象某一字段为null

      问题情况:开发过程中,我们不可避免的会碰到这样的一个场景,需要修改对象某一个字段的值为 null,原本我们用 mybatis 时,修改方法会有两个

      • updateByPrimaryKey 对你注入的字段全部更新(不判断是否为Null);我们可以用这个来更新 null 值
      • updateByPrimaryKeySelective 会对字段进行判断再更新(如果为Null就忽略更新)

      但是 mp 中的 update 方法是默认只更新不为 null 的值

      例如:这样是更新不了对应字段为 null 的

              TSysUseraccount tSysUseraccount = sysUseraccountMapper.selectById(84);
              tSysUseraccount.setfExtensionnumber(null);
              sysUseraccountMapper.updateById(tSysUseraccount);
      //执行的 sql 语句
      UPDATE T_Sys_UserAccount SET F_UserCode='cs', F_DeleteFlag=0 WHERE F_UserId=84

      解决方法:

      使用 LambdaUpdateWrapper 强制设置字段值为 null

              TSysUseraccount tSysUseraccount = sysUseraccountMapper.selectById(84);
              LambdaUpdateWrapper<TSysUseraccount> userUpdateWrapper = new UpdateWrapper<TSysUseraccount>().lambda()
                      .eq(TSysUseraccount::getfUserid,tSysUseraccount.getfUserid())
                      .set(TSysUseraccount::getfExtensionnumber,null);
              sysUseraccountMapper.update(tSysUseraccount,userUpdateWrapper);
      // 执行的 sql 语句
      UPDATE T_Sys_UserAccount SET F_UserCode='cs', F_DeleteFlag=0, F_ExtensionNumber=null WHERE F_UserId = 84

      三、自定义 sql 查询

      1、mapper 接口中注解查询

       @Select({"<script>",
                  "SELECT IFNULL(sum( IFNULL(detail.amount,0) ),0) as total  FROM T_Lhgy_Work_Plan plan, T_Lhgy_Work_Plan_Detail detail WHERE plan.id = detail.plan_id AND plan.delete_flag = 0 AND detail.delete_flag = 0 AND plan.object_id = #{yhCompanyId}",
                  "<when test='startTime!=null'>",
                  "AND date_format(plan.create_date,'%Y-%m-%d')  >= #{startTime}",
                  "</when>",
                  "<when test='endTime!=null'>",
                  "AND date_format(plan.create_date,'%Y-%m-%d') <= #{endTime}",
                  "</when>",
                  "</script>"})
          List<Map<String, Object>> selectSumAmountByYhCompany(@Param("yhCompanyId") Integer yhCompanyId,@Param("startTime") String startTime,@Param("endTime") String endTime);

      问题情况:

      mybatis 报The content of elements must consist of well-formed character data or markup. 语法格式错误

      问题原因:

      原来在xml中使用“<” “>” “&” 等一些这样的操作符时,xml会把它当成一个新的元素开始;

      解决方法:

      使用< ![CDATA[" 标记开始,以"]]> 包裹在< ![CDATA[" 标记开始,以"]]> 里包裹的元素,在xml解析时会被解析器忽略

      比如 >= 可以写成 <![CDATA[ >= ]]>

      <= 可以写成 <![CDATA[ <= ]]>

       @Select({"<script>",
                  "SELECT IFNULL(sum( IFNULL(detail.amount,0) ),0) as total  FROM T_Lhgy_Work_Plan plan, T_Lhgy_Work_Plan_Detail detail WHERE plan.id = detail.plan_id AND plan.delete_flag = 0 AND detail.delete_flag = 0 AND plan.object_id = #{yhCompanyId}",
                  "<when test='startTime!=null'>",
                  "AND date_format(plan.create_date,'%Y-%m-%d') <![CDATA[ >= ]]> #{startTime}",
                  "</when>",
                  "<when test='endTime!=null'>",
                  "AND date_format(plan.create_date,'%Y-%m-%d') <![CDATA[ <= ]]> #{endTime}",
                  "</when>",
                  "</script>"})
          List<Map<String, Object>> selectSumAmountByYhCompany(@Param("yhCompanyId") Integer yhCompanyId,@Param("startTime") String startTime,@Param("endTime") String endTime);

      四、使用 mybatis plus 性能分析插件

      @Configuration
      public class MybatisPlusConfig {
          /**
           * 打印 sql
           */
          @Bean
          @Profile({"dev","pro"})// 设置 dev pro 环境开启日志打印
          public PerformanceInterceptor performanceInterceptor() {
              PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
              //格式化sql语句
              Properties properties = new Properties();
              properties.setProperty("format", "false");
              performanceInterceptor.setProperties(properties);
              return performanceInterceptor;
          }
      }

      问题:3.2 无法使用此性能分析插件,导包时无法导入

      原因:如果 mp 在3.1版本时使用是不会有问题的,但是如果你的 mp 是3.2的版本,就无法使用这个插件了,因为 mp 在3.2的版本已经移除了这个性能分析插件并推荐使用第三方插件。

      3.1 mp 源码

      3.2 mp 源码,已经没有了 PerformanceInterceptor

      解决方案:

      将 3.1 的源码复制出来,自定义一个 sql 性能分细插件

      //
      // Source code recreated from a .class file by IntelliJ IDEA
      // (powered by Fernflower decompiler)
      //
      package me.zhengjie.config;
      import cn.hutool.db.sql.SqlFormatter;
      import com.baomidou.mybatisplus.core.toolkit.Assert;
      import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
      import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
      import com.baomidou.mybatisplus.core.toolkit.StringUtils;
      import com.baomidou.mybatisplus.core.toolkit.SystemClock;
      import java.lang.reflect.Method;
      import java.lang.reflect.Proxy;
      import java.sql.Statement;
      import java.util.ArrayList;
      import java.util.Comparator;
      import java.util.HashSet;
      import java.util.List;
      import java.util.Properties;
      import java.util.Set;
      import org.apache.ibatis.executor.statement.StatementHandler;
      import org.apache.ibatis.logging.Log;
      import org.apache.ibatis.logging.LogFactory;
      import org.apache.ibatis.mapping.MappedStatement;
      import org.apache.ibatis.plugin.Interceptor;
      import org.apache.ibatis.plugin.Intercepts;
      import org.apache.ibatis.plugin.Invocation;
      import org.apache.ibatis.plugin.Plugin;
      import org.apache.ibatis.plugin.Signature;
      import org.apache.ibatis.reflection.MetaObject;
      import org.apache.ibatis.reflection.SystemMetaObject;
      import org.apache.ibatis.session.ResultHandler;
      /**
       *  由于 mybatis plus 3.2 升级之后移除了性能分析的插件,所以此处手动引入 3.1 的性能分析插件
       */
      @Intercepts({@Signature(
          type = StatementHandler.class,
          method = "query",
          args = {Statement.class, ResultHandler.class}
      ), @Signature(
          type = StatementHandler.class,
          method = "update",
          args = {Statement.class}
      ), @Signature(
          type = StatementHandler.class,
          method = "batch",
          args = {Statement.class}
      )})
      public class PerformanceInterceptor implements Interceptor {
          private static final Log logger = LogFactory.getLog(PerformanceInterceptor.class);
          private static final String DruidPooledPreparedStatement = "com.alibaba.druid.pool.DruidPooledPreparedStatement";
          private static final String T4CPreparedStatement = "oracle.jdbc.driver.T4CPreparedStatement";
          private static final String OraclePreparedStatementWrapper = "oracle.jdbc.driver.OraclePreparedStatementWrapper";
          private long maxTime = 0L;
          private boolean format = false;
          private boolean writeInLog = false;
          private Method oracleGetOriginalSqlMethod;
          private Method druidGetSQLMethod;
          private static final SqlFormatter SQL_FORMATTER = new SqlFormatter();
          public PerformanceInterceptor() {
          }
          /** @deprecated */
          @Deprecated
          public static String sqlFormat(String boundSql, boolean format) {
              if (format) {
                  try {
                      return SQL_FORMATTER.format(boundSql);
                  } catch (Exception var3) {
                      ;
                  }
              }
              return boundSql;
          }
          public Object intercept(Invocation invocation) throws Throwable {
              Object firstArg = invocation.getArgs()[0];
              Statement statement;
              if (Proxy.isProxyClass(firstArg.getClass())) {
                  statement = (Statement)SystemMetaObject.forObject(firstArg).getValue("h.statement");
              } else {
                  statement = (Statement)firstArg;
              }
              MetaObject stmtMetaObj = SystemMetaObject.forObject(statement);
              try {
                  statement = (Statement)stmtMetaObj.getValue("stmt.statement");
              } catch (Exception var20) {
                  ;
              }
              if (stmtMetaObj.hasGetter("delegate")) {
                  try {
                      statement = (Statement)stmtMetaObj.getValue("delegate");
                  } catch (Exception var19) {
                      ;
                  }
              }
              String originalSql = null;
              String stmtClassName = statement.getClass().getName();
              Class clazz;
              Object stmtSql;
              if ("com.alibaba.druid.pool.DruidPooledPreparedStatement".equals(stmtClassName)) {
                  try {
                      if (this.druidGetSQLMethod == null) {
                          clazz = Class.forName("com.alibaba.druid.pool.DruidPooledPreparedStatement");
                          this.druidGetSQLMethod = clazz.getMethod("getSql");
                      }
                      stmtSql = this.druidGetSQLMethod.invoke(statement);
                      if (stmtSql instanceof String) {
                          originalSql = (String)stmtSql;
                      }
                  } catch (Exception var18) {
                      var18.printStackTrace();
                  }
              } else if ("oracle.jdbc.driver.T4CPreparedStatement".equals(stmtClassName) || "oracle.jdbc.driver.OraclePreparedStatementWrapper".equals(stmtClassName)) {
                  try {
                      if (this.oracleGetOriginalSqlMethod != null) {
                          stmtSql = this.oracleGetOriginalSqlMethod.invoke(statement);
                          if (stmtSql instanceof String) {
                              originalSql = (String)stmtSql;
                          }
                      } else {
                          clazz = Class.forName(stmtClassName);
                          this.oracleGetOriginalSqlMethod = this.getMethodRegular(clazz, "getOriginalSql");
                          if (this.oracleGetOriginalSqlMethod != null) {
                              this.oracleGetOriginalSqlMethod.setAccessible(true);
                              if (null != this.oracleGetOriginalSqlMethod) {
                                  Object stmtSql1 = this.oracleGetOriginalSqlMethod.invoke(statement);
                                  if (stmtSql1 instanceof String) {
                                      originalSql = (String)stmtSql1;
                                  }
                              }
                          }
                      }
                  } catch (Exception var17) {
                      ;
                  }
              }
              if (originalSql == null) {
                  originalSql = statement.toString();
              }
              originalSql = originalSql.replaceAll("[\\s]+", " ");
              int index = this.indexOfSqlStart(originalSql);
              if (index > 0) {
                  originalSql = originalSql.substring(index);
              }
              long start = SystemClock.now();
              Object result = invocation.proceed();
              long timing = SystemClock.now() - start;
              Object target = PluginUtils.realTarget(invocation.getTarget());
              MetaObject metaObject = SystemMetaObject.forObject(target);
              MappedStatement ms = (MappedStatement)metaObject.getValue("delegate.mappedStatement");
              StringBuilder formatSql = (new StringBuilder()).append(" Time:").append(timing).append(" ms - ID:").append(ms.getId()).append("\n").append("Execute SQL:").append(sqlFormat(originalSql, this.format)).append("\n");
              if (this.isWriteInLog()) {
                  if (this.getMaxTime() >= 1L && timing > this.getMaxTime()) {
                      logger.error(formatSql.toString());
                  } else {
                      logger.debug(formatSql.toString());
                  }
              } else {
                  System.err.println(formatSql.toString());
                  Assert.isFalse(this.getMaxTime() >= 1L && timing > this.getMaxTime(), " The SQL execution time is too large, please optimize ! ", new Object[0]);
              }
              return result;
          }
          public Object plugin(Object target) {
              return target instanceof StatementHandler ? Plugin.wrap(target, this) : target;
          }
          public void setProperties(Properties prop) {
              String maxTime = prop.getProperty("maxTime");
              String format = prop.getProperty("format");
              if (StringUtils.isNotEmpty(maxTime)) {
                  this.maxTime = Long.parseLong(maxTime);
              }
              if (StringUtils.isNotEmpty(format)) {
                  this.format = Boolean.valueOf(format).booleanValue();
              }
          }
          public Method getMethodRegular(Class<?> clazz, String methodName) {
              if (Object.class.equals(clazz)) {
                  return null;
              } else {
                  Method[] var3 = clazz.getDeclaredMethods();
                  int var4 = var3.length;
                  for(int var5 = 0; var5 < var4; ++var5) {
                      Method method = var3[var5];
                      if (method.getName().equals(methodName)) {
                          return method;
                      }
                  }
                  return this.getMethodRegular(clazz.getSuperclass(), methodName);
              }
          }
          private int indexOfSqlStart(String sql) {
              String upperCaseSql = sql.toUpperCase();
              Set<Integer> set = new HashSet();
              set.add(upperCaseSql.indexOf("SELECT "));
              set.add(upperCaseSql.indexOf("UPDATE "));
              set.add(upperCaseSql.indexOf("INSERT "));
              set.add(upperCaseSql.indexOf("DELETE "));
              set.remove(Integer.valueOf(-1));
              if (CollectionUtils.isEmpty(set)) {
                  return -1;
              } else {
                  List<Integer> list = new ArrayList(set);
                  list.sort(Comparator.naturalOrder());
                  return ((Integer)list.get(0)).intValue();
              }
          }
          public PerformanceInterceptor setMaxTime(long maxTime) {
              this.maxTime = maxTime;
              return this;
          }
          public long getMaxTime() {
              return this.maxTime;
          }
          public PerformanceInterceptor setFormat(boolean format) {
              this.format = format;
              return this;
          }
          public boolean isFormat() {
              return this.format;
          }
          public PerformanceInterceptor setWriteInLog(boolean writeInLog) {
              this.writeInLog = writeInLog;
              return this;
          }
          public boolean isWriteInLog() {
              return this.writeInLog;
          }
      }

      总结

      以上为个人经验,希望能给大家一个参考,也希望大家多多支持3672js教程。

      您可能感兴趣的文章:
      • Mybatis-Plus开发提速器generator的使用
      • Mybatis-Plus开发提速器mybatis-plus-generator-ui详解
      • 从零搭建SpringBoot+MyBatisPlus快速开发脚手架
      • SpringBoot 开发提速神器 Lombok+MybatisPlus+SwaggerUI
      相关栏目:

      用户点评