mybatis plus開發(fā)過程中遇到的問題記錄及解決
本文主要記錄本人使用 mybatis plus 開發(fā)過程中碰到的問題,以及解決方案
## 以下 mybatis plus 統(tǒng)一簡稱 mp
一、使用 mp 生成代碼時
1、使用saveOrUpdateBatch或者saveBatch等新增修改方法時
問題情況:
MybatisPlusException: error: can not execute. because can not find column for id from entity
原因:
不能執(zhí)行。因為無法從實體中找到id列使用 mp 自動生成代碼時,可能有這一行代碼,會導致不生成主鍵 ID,變成自定義基礎的Entity類,公共字段
strategy.setSuperEntityColumns("id")解決方法:
將上面這行代碼注釋即可
可能生成 ID 之后任舊無法執(zhí)行,檢查下面的原因
可能會存在實體類屬性與數(shù)據(jù)庫字段不一致的情況,所以我們在使用 mp 生成實體類時,最好在生成策略中加上這個行
strategy.setEntityTableFieldAnnotationEnable(true);
目的:生成之后的實體類中的每個屬性會多出一個注解,來用于屬性和數(shù)據(jù)庫字段的對應
@TableField("id")2、生成實體類時
數(shù)據(jù)庫中的 int 類型 ID 變成了 String
問題情況:

原因及解決方法:
我這里是 Mysql 數(shù)據(jù)庫,生成代碼時,做數(shù)據(jù)庫類型轉換時,原本選擇的是 Oracle 數(shù)據(jù)庫,改成 Mysql 數(shù)據(jù)的類型就可以

3、引用第2鐘錯誤,當我們使用 mp 生成代碼的時候,只想生成實體類
問題情況:
引用第2鐘錯誤,當我們使用 mp 生成代碼的時候,可能實體類或者某一個文件中的代碼生成的有問題,需要重新生成,但是又不想覆蓋其他 Controller 、Mapper 等文件,我們?nèi)绾芜x擇只生成實體類?
解決方法:
我百度搜過,大佬們生成代碼時,基本上都是生成所有的文件,所以我就自己開始研究。
請看下面這段代碼,大家應該都明白這是什么意思,就是讓我們生成的 mapper.xml 生成到我們指定的 resource 的文件夾下面,那么它原本的 xml 為什么不會生成了呢?
// 自定義輸出配置
List<FileOutConfig> focList = new ArrayList<>();
// 自定義配置會被優(yōu)先輸出
focList.add(new FileOutConfig(templatePath) {
@Override
public String outputFile(TableInfo tableInfo) {
// 自定義輸出文件名 , 如果你 Entity 設置了前后綴、此處注意 xml 的名稱會跟著發(fā)生變化??!
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
問題情況:開發(fā)過程中,我們不可避免的會碰到這樣的一個場景,需要修改對象某一個字段的值為 null,原本我們用 mybatis 時,修改方法會有兩個
- updateByPrimaryKey 對你注入的字段全部更新(不判斷是否為Null);我們可以用這個來更新 null 值
- updateByPrimaryKeySelective 會對字段進行判斷再更新(如果為Null就忽略更新)
但是 mp 中的 update 方法是默認只更新不為 null 的值
例如:這樣是更新不了對應字段為 null 的
TSysUseraccount tSysUseraccount = sysUseraccountMapper.selectById(84);
tSysUseraccount.setfExtensionnumber(null);
sysUseraccountMapper.updateById(tSysUseraccount);
//執(zhí)行的 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);
// 執(zhí)行的 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 環(huán)境開啟日志打印
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的版本已經(jīng)移除了這個性能分析插件并推薦使用第三方插件。
3.1 mp 源碼

3.2 mp 源碼,已經(jīng)沒有了 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;
}
}總結
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
Guava是Google發(fā)布的一個開源庫,主要提供了一些在Java開發(fā)中非常有用的工具類和API,不管是工作還是學習都是非常值得我們?nèi)ナ煜さ?,一起來看看?/div> 2023-03-03
基于ElasticSearch Analyzer的使用規(guī)則詳解
這篇文章主要介紹了基于ElasticSearch Analyzer的使用規(guī)則,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07
詳解springboot+mybatis多數(shù)據(jù)源最簡解決方案
本篇文章主要介紹了詳解springboot+mybatis多數(shù)據(jù)源最簡解決方案,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-05-05最新評論

