Mybatis自定義Sql模板語法問題
一、說明
mybatis 原有mapper.xml的語法標(biāo)簽寫法過于繁瑣,如下面的<if>標(biāo)簽:
<select id="selectByParams" parameterType="map" resultType="user"> select * from user <where> <if test="id != null ">id=#{id}</if> <if test="name != null and name.length()>0" >and name=#{name}</if> <if test="age != null and age.length()>0">and age = #{age}</if> </where> </select> <select id="selectByParams" parameterType="map" resultType="user"> select * from user <where> <if test="id != null ">id=#{id}</if> <if test="name != null and name.length()>0" >and name=#{name}</if> <if test="age != null and age.length()>0">and age = #{age}</if> </where> </select>
這些寫法功能單一還要寫很多不必要的代碼,于是可用自定義語法代替這類標(biāo)簽,如:
<select id="selectByParams" parameterType="map" resultType="user"> select * from user where status = 1 {{and id = [id]}} {{and code = [code,notEmpty]}} {{and name like [name,like,notBlank]}} {{and id in ([ids,list])}} </select>
使用自定義標(biāo)簽代替 Mybatis 原有的查詢條件相關(guān)語法,如:if、forEach 標(biāo)簽,解決原有語法寫法太繁瑣的問題,且增加了一些參數(shù)常用的操作和功能。
二、自定義語法和標(biāo)簽說明
1、自定義語法,由于mybatis已經(jīng)使用了 ${} 和 #{} 標(biāo)簽,所以自定義語法使用 { { }} 和 [ ] 作為標(biāo)簽
2、格式: { {sql [name,key]}} ,如:{ {and code = [code,notEmpty]}} ,本語句的意思:條件 code = ? 在參數(shù)code不為空且不為空字符串的條件下生效,比如 code=2 時,生成條件 code = 2。參數(shù)名稱必須放在第一個位置,關(guān)鍵字可為空。
3、參數(shù)說明:以上格式中的 sql 代表一般的sql語句,[ ] 中的 name 代表參數(shù)名稱,key 代表關(guān)鍵字。
4、關(guān)鍵字類型: notNull 、 notEmpty 、 notBlank 、 trim 、 upper 、 lower 、 list 、 like 、 llike 、 rlike 、 const 、 hidden 、 default
5、其中 list 、 like 、 llike 、 rlike 、 const 、hidden 為參數(shù)類型關(guān)鍵字,notNull 、 notEmpty 、 notBlank 為參數(shù)條件類型關(guān)鍵字, trim 、 upper 、 lower 為參數(shù)特殊處理關(guān)鍵字。
6、語法標(biāo)簽中所有參數(shù)值用英文逗號,分隔,關(guān)鍵字嚴(yán)格區(qū)分大小寫,除了參數(shù)類型關(guān)鍵字,其它關(guān)鍵字可組合使用。
7、參數(shù)類型關(guān)鍵字必須放在第二個位置,且一個參數(shù)只能是其中一種參數(shù)類型,同一個參數(shù)不能同時是兩個或多個參數(shù)類型。
8、條件類型和特殊處理類型關(guān)鍵字可組合使用,且位置不固定,可隨意搭配。當(dāng)參數(shù)沒有參數(shù)類型關(guān)鍵字時,條件類型和特殊處理類型關(guān)鍵字可放在第二個位置。
9、參數(shù)名稱最多可以為三級名稱,即:code 、user.code、user.dept.code,名稱層級取決于入?yún)㈩愋?。?dāng)mapper接口的參數(shù)為單個對象參數(shù),如:Map、User等,參數(shù)名可直接使用 id、code、name等。當(dāng)參數(shù)為兩個及以上,需要使用 @Param 定義參數(shù)名稱,如:func(@Param("user") User user, @Param("map") Map map),則參數(shù)名稱需要為:user.id, user.name, map.code 等。
三、關(guān)鍵字說明
default
本關(guān)鍵字用于為參數(shù)設(shè)置默認(rèn)值,當(dāng)參數(shù)不滿足指定條件時,使用默認(rèn)值生成sql語句。如:{ {and type = [type,default,1]}},當(dāng)參數(shù) type 為 null(空) 時,使用默認(rèn)值生成語句:and type = 1。當(dāng)參數(shù) type 的值為 2時,生成語句:and type = 2。
注意:默認(rèn)值關(guān)鍵字 default 后面必須跟一個長度不為0的默認(rèn)值,否則該關(guān)鍵字不生效。而且default 關(guān)鍵字和默認(rèn)值必須放在標(biāo)簽最后面。
notNull
本關(guān)鍵字用于聲明該參數(shù)不能為空,即該參數(shù)必填,否則會報參數(shù)為空異常。如:{ {and code = [code,notNull]}},當(dāng)參數(shù)code為空(null)時,報異常:參數(shù)[code]不能為空。但空字符串""不會觸發(fā)本條件,如需過濾空字符串,則需要與下面兩個關(guān)鍵字組合使用。
注意:notNull 和 default 在邏輯上是有沖突的,所以當(dāng)設(shè)置了默認(rèn)值default,則本關(guān)鍵字會失效。
notEmpty
本關(guān)鍵字用于聲明該參數(shù)不能為空且長度不能為0,即:str != null && str.length() != 0。且本關(guān)鍵字主要用于字符串參數(shù)。如:{ {code = [code,notEmpty]}},當(dāng)參數(shù)code為null或字符串""時,條件不生效。
關(guān)鍵字組合:{ {and code = [code,notNull,notEmpty]}},即參數(shù)code必填且不能為空字符串。
notBlank
本關(guān)鍵字與 notEmpty 的區(qū)別就是多了一個 trim 操作,即:str != null && str.trim().length() != 0。且本關(guān)鍵字主要用于字符串參數(shù)。
notBlank 的功能包含了 notEmpty,所以這兩個關(guān)鍵字只需要存在一個即可。
trim
本關(guān)鍵字也是作用于字符串參數(shù),用于自動 trim 字符串前后空格。如:{ {and code = [code,trim]}},當(dāng)參數(shù)code=" a "時,生成語句:and code = 'a'。
關(guān)鍵字組合:{ {and code = [code,notNull,notBlank,trim]}}
upper
本關(guān)鍵字的作用即自動將參數(shù)轉(zhuǎn)換為大寫,如參數(shù) code = 'abc' 自動轉(zhuǎn)換成 code = 'ABC'。
關(guān)鍵字組合:{ {and upper(code) = [code,notNull,notBlank,trim,upper]}}
lower
本關(guān)鍵字即把參數(shù)自動轉(zhuǎn)換成小寫,與 upper 的作用相同。
like
、llike
、rlike
參數(shù)類型關(guān)鍵字:即模糊查詢,自動給參數(shù)加上 % 符號。分別對應(yīng):'%value%'、'%value'、'value%'。如:{ {and name like [name,like,trim,notEmpty]}}。當(dāng)參數(shù) name = ' 張三 '時,生成語句:and name like '%張三%'。
注意:所有參數(shù)類型關(guān)鍵字需要放在標(biāo)簽 [ ] 中的第二個位置,第一個位置是參數(shù)名稱。
list
參數(shù)類型關(guān)鍵字:本參數(shù)作用于 in 查詢條件,參數(shù)必須是 Controller<?> 類的子類,如 List、ArrayList等。語法:{ {and id in ([ids,list])}}。當(dāng)參數(shù) ids = [1,2,3] 時,生成語句:and id in (1,2,3)。
注意:list 類型的參數(shù),當(dāng)參數(shù) ids == null || ids.size() == 0 時,條件都不會生效。
關(guān)鍵字組合:
1、default 當(dāng) list 與 default 組合使用時,設(shè)置默認(rèn)值需要用 | 代替 , 號,如需要默認(rèn)值 1,2,3,需要填寫成 1|2|3。即:{{and id in (ids,list,default,1|2|3)}}
2、notEmpty、notBlank、trim、upper、lower 當(dāng) list 與 以上五個關(guān)鍵字組合使用時,以上五個關(guān)鍵字都是作用于 list 中的元素而不是list本身。如:{{and id in ([ids,trim,notBlank,upper])}},當(dāng)參數(shù)ids=['a',' b ',' ', 'Ef'] 時,生成語句:and id in ('A','B','EF')。其中 trim 關(guān)鍵字去掉了第二個元素的前后空格,notBlank 過濾掉了第三個空字符串元素,upper 則將所有元素轉(zhuǎn)換成大寫。
3、notNull 本關(guān)鍵字與list組合即參數(shù)ids必填。
const
參數(shù)類型關(guān)鍵字:本關(guān)鍵字與mybatis中的 ${} 類似,即將參數(shù)當(dāng)成sql語句放在sql中,本參數(shù)有Sql注入的風(fēng)險。如:order by {{[orderby,const,notBlank,default,t.id]}}。當(dāng)參數(shù) orderby 為空或空字符串時,使用默認(rèn)值生成語句:order by t.id,當(dāng)參數(shù) orderby = 't.code desc' 時,生成語句:order by t.code desc
注意:當(dāng)默認(rèn)值中有逗號 , 時,需要替換成 |, 即:t.id,t.code,t.name 需要寫成:t.id|t.code|t.name,如:order by {{[orderby,default,t.id|t.code|t.name]}}
hidden
參數(shù)類型關(guān)鍵字:本關(guān)鍵字用于隱藏參數(shù)本身,即當(dāng)參數(shù)生效時,生成的語句中不包含參數(shù)本身。如:{{order by id [orderby,hidden]}}, 當(dāng)參數(shù) orderby 不為空時(可以是任意值),生成語句:order by id。
四、實現(xiàn)邏輯
1、實現(xiàn) mybatis 的 InnerInterceptor 攔截器,并在本攔截器中處理自定義語法的邏輯。
public class MyBatisSqlInnerInterceptor implements InnerInterceptor { @Override public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { String sql = boundSql.getSql(); // 獲取原始sql //TODO 處理自己的自定義語法邏輯和參數(shù) String newSql = ..... // 邏輯處理完成,將新sql放回去 PluginUtils.MPBoundSql mpBoundSql = PluginUtils.mpBoundSql(boundSql); mpBoundSql.sql(newSql); InnerInterceptor.super.beforeQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql); } }
2、將自定義攔截器添加到 mybaits 中。(注意:自定義攔截器必須放在分頁插件之前)
@Configuration public class MybatisPlusConfig { ? ? @Bean ? ? public MybatisPlusInterceptor mybatisPlusInterceptor() { ? ? ? ? MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); ? ? ? ? interceptor.addInnerInterceptor(new MyBatisSqlInnerInterceptor()); // 自定義Sql語法攔截器 ? ? ? ? interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); // 分頁插件 ? ? ? ? return interceptor; ? ? } }
五、實現(xiàn)代碼
1、新增關(guān)鍵字類型枚舉類
/** ?* 自定義Sql語法關(guān)鍵字類型枚舉 ?*/ public enum SqlWrapperType { ? ? /** 普通參數(shù),默認(rèn)值 */ ? ? Param { ? ? ? ? @Override ? ? ? ? String getKey() { ? ? ? ? ? ? return "param"; ? ? ? ? } ? ? ? ? @Override ? ? ? ? Integer getType() { ? ? ? ? ? ? return 0; ? ? ? ? } ? ? }, ? ? /** 必填參數(shù) */ ? ? NotNull { ? ? ? ? @Override ? ? ? ? String getKey() { ? ? ? ? ? ? return "notNull"; ? ? ? ? } ? ? ? ? @Override ? ? ? ? Integer getType() { ? ? ? ? ? ? return 0; ? ? ? ? } ? ? }, ? ? /** 參數(shù)不能為空或長度為0 */ ? ? NotEmpty { ? ? ? ? @Override ? ? ? ? String getKey() { ? ? ? ? ? ? return "notEmpty"; ? ? ? ? } ? ? ? ? @Override ? ? ? ? Integer getType() { ? ? ? ? ? ? return 0; ? ? ? ? } ? ? }, ? ? /** 字符串不能為空或 trim() 后長度不能為0 */ ? ? NotBlank { ? ? ? ? @Override ? ? ? ? String getKey() { ? ? ? ? ? ? return "notBlank"; ? ? ? ? } ? ? ? ? @Override ? ? ? ? Integer getType() { ? ? ? ? ? ? return 0; ? ? ? ? } ? ? }, ? ? /** 如果參數(shù)是字符串,則自動trum前后的空格 */ ? ? Trim { ? ? ? ? @Override ? ? ? ? String getKey() { ? ? ? ? ? ? return "trim"; ? ? ? ? } ? ? ? ? @Override ? ? ? ? Integer getType() { ? ? ? ? ? ? return 0; ? ? ? ? } ? ? }, ? ? /** 參數(shù)值自動轉(zhuǎn)換成大寫 */ ? ? Upper { ? ? ? ? @Override ? ? ? ? String getKey() { ? ? ? ? ? ? return "upper"; ? ? ? ? } ? ? ? ? @Override ? ? ? ? Integer getType() { ? ? ? ? ? ? return 0; ? ? ? ? } ? ? }, ? ? /** 參數(shù)值自動轉(zhuǎn)換成小寫 */ ? ? Lower { ? ? ? ? @Override ? ? ? ? String getKey() { ? ? ? ? ? ? return "lower"; ? ? ? ? } ? ? ? ? @Override ? ? ? ? Integer getType() { ? ? ? ? ? ? return 0; ? ? ? ? } ? ? }, ? ? /** 模糊查詢 %value% */ ? ? Like { ? ? ? ? @Override ? ? ? ? String getKey() { ? ? ? ? ? ? return "like"; ? ? ? ? } ? ? ? ? @Override ? ? ? ? Integer getType() { ? ? ? ? ? ? return 1; ? ? ? ? } ? ? }, ? ? /** 模糊查詢 %value */ ? ? LLike { ? ? ? ? @Override ? ? ? ? String getKey() { ? ? ? ? ? ? return "llike"; ? ? ? ? } ? ? ? ? @Override ? ? ? ? Integer getType() { ? ? ? ? ? ? return 2; ? ? ? ? } ? ? }, ? ? /** 模糊查詢 value% */ ? ? RLike { ? ? ? ? @Override ? ? ? ? String getKey() { ? ? ? ? ? ? return "rlike"; ? ? ? ? } ? ? ? ? @Override ? ? ? ? Integer getType() { ? ? ? ? ? ? return 3; ? ? ? ? } ? ? }, ? ? /** in 查詢 */ ? ? List { ? ? ? ? @Override ? ? ? ? String getKey() { ? ? ? ? ? ? return "list"; ? ? ? ? } ? ? ? ? @Override ? ? ? ? Integer getType() { ? ? ? ? ? ? return 4; ? ? ? ? } ? ? }, ? ? /** sql語句參數(shù),需要注意Sql注入 */ ? ? Const { ? ? ? ? @Override ? ? ? ? String getKey() { ? ? ? ? ? ? return "const"; ? ? ? ? } ? ? ? ? @Override ? ? ? ? Integer getType() { ? ? ? ? ? ? return 5; ? ? ? ? } ? ? }, ? ? /** 設(shè)置參數(shù)默認(rèn)值 */ ? ? Default { ? ? ? ? @Override ? ? ? ? String getKey() { ? ? ? ? ? ? return "default"; ? ? ? ? } ? ? ? ? @Override ? ? ? ? Integer getType() { ? ? ? ? ? ? return 6; ? ? ? ? } ? ? }, ? ? /** 隱藏參數(shù)值 */ ? ? Hidden { ? ? ? ? @Override ? ? ? ? String getKey() { ? ? ? ? ? ? return "hidden"; ? ? ? ? } ? ? ? ? @Override ? ? ? ? Integer getType() { ? ? ? ? ? ? return 7; ? ? ? ? } ? ? }; ? ? public static SqlWrapperType get(String key){ ? ? ? ? for (SqlWrapperType type : SqlWrapperType.values()) { ? ? ? ? ? ? if(type.getKey().equals(key)){ ? ? ? ? ? ? ? ? return type; ? ? ? ? ? ? } ? ? ? ? } ? ? ? ? throw new IllegalArgumentException("自定義Sql語法關(guān)鍵字[" + key + "]不存在"); ? ? } ? ? abstract String getKey(); ? ? abstract Integer getType(); ? ? public boolean equal(SqlWrapperType type){ ? ? ? ? return this.getType().equals(type.getType()); ? ? } } 2、新增參數(shù)對象實體類 import lombok.AllArgsConstructor; import lombok.Data; /** ?* 自定義Sql語法參數(shù)封裝器參數(shù)對象 ?*/ @Data @AllArgsConstructor public class SqlParamEntity { ? ? private String key; ? ? private String sign; ? ? private SqlWrapperType type; ? ? private boolean hasDefault; ? ? private String defaultValue; ? ? private boolean notNull; ? ? private boolean notEmpty; ? ? private boolean notBlank; ? ? private boolean trim; ? ? private Integer change; }
3、新增參數(shù)處理類
import com.zorgn.common.ExtList; import com.zorgn.common.ExtUtil; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; import static com.zorgn.core.mybatis.SqlWrapper.*; /** ?* 自定義Sql語法參數(shù)封裝器 ?*/ public class SqlParamWrapper { ? ? private static final String LikeSign = "%"; ? ? private static final String SplitSign = ","; ? ? private static final Pattern pattern = Pattern.compile("(?<=\\[)(.+?)(?=])"); ? ? private String paramBody; ? ? private final int paramEntitiesSize; ? ? private final List<SqlParamEntity> paramEntities = new ArrayList<>(); ? ? /** ? ? ?* 自定義Sql語法參數(shù)封裝器 ? ? ?*/ ? ? public SqlParamWrapper(String paramSql){ ? ? ? ? this.paramBody = paramSql; ? ? ? ? initParamSql(); ? ? ? ? paramEntitiesSize = paramEntities.size(); ? ? } ? ? /** ? ? ?* 初始化自定義Sql語法參數(shù) ? ? ?*/ ? ? private void initParamSql(){ ? ? ? ? Matcher matcher = pattern.matcher(paramBody); ? ? ? ? int count = 1; ? ? ? ? while (matcher.find()){ ? ? ? ? ? ? String key = matcher.group(), parSign = SignPar + count++ + SignPar, defaultVal = null; ? ? ? ? ? ? this.paramBody = this.paramBody.replace(ParBgtStr + key + ParEndStr, parSign); ? ? ? ? ? ? SqlWrapperType type = SqlWrapperType.Param; ? ? ? ? ? ? boolean hasDefault = false,notNull = false,notEmpty = false,notBlank = false,trim = false; ? ? ? ? ? ? Integer change = 0; ? ? ? ? ? ? if(key.contains(SplitSign)){ ? ? ? ? ? ? ? ? List<String> keyList = ExtList.splitStr(key, ",", ExtUtil::isBlank); ? ? ? ? ? ? ? ? key = keyList.get(0).toLowerCase(); ? ? ? ? ? ? ? ? if(keyList.size() >= 2){ ? ? ? ? ? ? ? ? ? ? type = SqlWrapperType.get(keyList.get(1)); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? if(keyList.contains(SqlWrapperType.Default.getKey())){ ? ? ? ? ? ? ? ? ? ? int idx = keyList.indexOf(SqlWrapperType.Default.getKey()) + 1; ? ? ? ? ? ? ? ? ? ? if(keyList.size() > idx){ ? ? ? ? ? ? ? ? ? ? ? ? String defstr = keyList.get(idx); ? ? ? ? ? ? ? ? ? ? ? ? if(!isBlank(defstr)) { ? ? ? ? ? ? ? ? ? ? ? ? ? ? hasDefault = true; ? ? ? ? ? ? ? ? ? ? ? ? ? ? if(SqlWrapperType.List.equal(type)){ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? defaultVal = defstr; ? ? ? ? ? ? ? ? ? ? ? ? ? ? }else{ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? defaultVal = defstr.replace("|",","); ? ? ? ? ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? if(keyList.contains(SqlWrapperType.NotNull.getKey())){ ? ? ? ? ? ? ? ? ? ? notNull = true; ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? if(keyList.contains(SqlWrapperType.NotEmpty.getKey())){ ? ? ? ? ? ? ? ? ? ? notEmpty = true; ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? if(keyList.contains(SqlWrapperType.NotBlank.getKey())){ ? ? ? ? ? ? ? ? ? ? notBlank = true; ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? if(keyList.contains(SqlWrapperType.Trim.getKey())){ ? ? ? ? ? ? ? ? ? ? trim = true; ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? if(keyList.contains(SqlWrapperType.Upper.getKey())){ ? ? ? ? ? ? ? ? ? ? change = 1; ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? if(keyList.contains(SqlWrapperType.Lower.getKey())){ ? ? ? ? ? ? ? ? ? ? change = 2; ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? ? ? paramEntities.add(new SqlParamEntity(key, parSign, type, hasDefault, defaultVal, notNull, notEmpty, notBlank, trim, change)); ? ? ? ? } ? ? } ? ? /** ? ? ?* 根據(jù)參數(shù)初始化自定義sql查詢條件參數(shù) ? ? ?*/ ? ? public String getParamSql(Map<?,?> map) { ? ? ? ? String newParSql = this.paramBody; ? ? ? ? List<String> notValueKeys = new ArrayList<>(); ? ? ? ? for (SqlParamEntity entity : paramEntities) { ? ? ? ? ? ? Object val = map.get(entity.getKey()); ? ? ? ? ? ? if(null == val){ ? ? ? ? ? ? ? ? if(entity.isHasDefault()){ ? ? ? ? ? ? ? ? ? ? val = initDefaultValue(entity); ? ? ? ? ? ? ? ? }else if(entity.isNotNull()){ ? ? ? ? ? ? ? ? ? ? if(paramEntitiesSize == 1){ ? ? ? ? ? ? ? ? ? ? ? ? throw new NullPointerException("參數(shù)[" + entity.getKey() + "]不能為空"); ? ? ? ? ? ? ? ? ? ? }else{ ? ? ? ? ? ? ? ? ? ? ? ? notValueKeys.add(entity.getKey()); ? ? ? ? ? ? ? ? ? ? ? ? continue; ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? }else { ? ? ? ? ? ? ? ? ? ? if(paramEntitiesSize == 1){ ? ? ? ? ? ? ? ? ? ? ? ? return ""; ? ? ? ? ? ? ? ? ? ? }else{ ? ? ? ? ? ? ? ? ? ? ? ? notValueKeys.add(entity.getKey()); ? ? ? ? ? ? ? ? ? ? ? ? continue; ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? } ? ? ? ? ? ? }else if ((isEmpty(val.toString()) && entity.isNotEmpty()) || (isBlank(val.toString()) && entity.isNotBlank())){ ? ? ? ? ? ? ? ? if (entity.isHasDefault()) { ? ? ? ? ? ? ? ? ? ? val = initDefaultValue(entity); ? ? ? ? ? ? ? ? } else if (entity.isNotNull()) { ? ? ? ? ? ? ? ? ? ? throw new NullPointerException("參數(shù)[" + entity.getKey() + "]不能為空"); ? ? ? ? ? ? ? ? } else if (paramEntitiesSize > 1){ ? ? ? ? ? ? ? ? ? ? if(notValueKeys.size() < paramEntitiesSize - 1){ ? ? ? ? ? ? ? ? ? ? ? ? notValueKeys.add(entity.getKey()); ? ? ? ? ? ? ? ? ? ? ? ? continue; ? ? ? ? ? ? ? ? ? ? } else if (paramEntitiesSize != notValueKeys.size() + 1){ ? ? ? ? ? ? ? ? ? ? ? ? notValueKeys.add(entity.getKey()); ? ? ? ? ? ? ? ? ? ? ? ? continue; ? ? ? ? ? ? ? ? ? ? } else if(paramEntitiesSize == notValueKeys.size() + 1){ ? ? ? ? ? ? ? ? ? ? ? ? return ""; ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? } else { ? ? ? ? ? ? ? ? ? ? return ""; ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? ? ? if(val instanceof String && entity.isTrim()){ ? ? ? ? ? ? ? ? val = val.toString().trim(); ? ? ? ? ? ? } ? ? ? ? ? ? if(entity.getChange() > 0 && val instanceof String){ ? ? ? ? ? ? ? ? val = entity.getChange() == 1 ? val.toString().toUpperCase() : val.toString().toLowerCase(); ? ? ? ? ? ? } ? ? ? ? ? ? if(SqlWrapperType.Hidden.equal(entity.getType())){ ? ? ? ? ? ? ? ? newParSql = newParSql.replace(entity.getSign(), ""); ? ? ? ? ? ? } else if(SqlWrapperType.List.equal(entity.getType())){ ? ? ? ? ? ? ? ? if(val instanceof Collection){ ? ? ? ? ? ? ? ? ? ? Collection<?> collection = (Collection<?>) val; ? ? ? ? ? ? ? ? ? ? if(collection.size() > 0){ ? ? ? ? ? ? ? ? ? ? ? ? StringJoiner sj = new StringJoiner(SplitSign); ? ? ? ? ? ? ? ? ? ? ? ? for (Object o : collection) { ? ? ? ? ? ? ? ? ? ? ? ? ? ? if(o instanceof String){ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? String str = (String) o; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if(entity.isNotBlank() && isBlank(str)){ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? continue; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if(entity.isNotEmpty() && isEmpty(str)){ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? continue; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if(entity.isTrim()){ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? str = str.trim(); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if(entity.getChange() > 0){ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? str = entity.getChange() == 1 ? str.toUpperCase() : str.toLowerCase(); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? sj.add("'" + str + "'"); ? ? ? ? ? ? ? ? ? ? ? ? ? ? }else { ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? sj.add(initValue(o) + ""); ? ? ? ? ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? ? ? newParSql = newParSql.replace(entity.getSign(), sj.toString()); ? ? ? ? ? ? ? ? ? ? }else{ ? ? ? ? ? ? ? ? ? ? ? ? notValueKeys.add(entity.getKey()); ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? continue; ? ? ? ? ? ? ? ? } else { ? ? ? ? ? ? ? ? ? ? throw new IllegalArgumentException("參數(shù)[" + entity.getKey() + "]必須為list集合"); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } else if(SqlWrapperType.Like.equal(entity.getType())){ ? ? ? ? ? ? ? ? val = LikeSign + val + LikeSign; ? ? ? ? ? ? } else if(SqlWrapperType.LLike.equal(entity.getType())){ ? ? ? ? ? ? ? ? val = LikeSign + val; ? ? ? ? ? ? } else if(SqlWrapperType.RLike.equal(entity.getType())){ ? ? ? ? ? ? ? ? val = val + LikeSign; ? ? ? ? ? ? } ? ? ? ? ? ? newParSql = newParSql.replace(entity.getSign(), (SqlWrapperType.Const.equal(entity.getType()) ? val : initValue(val)).toString()); ? ? ? ? } ? ? ? ? if(notValueKeys.size() > 0 && notValueKeys.size() != paramEntitiesSize){ ? ? ? ? ? ? //多參數(shù)時,所有參數(shù)必填 ? ? ? ? ? ? throw new NullPointerException("參數(shù)" + notValueKeys + "不能為空"); ? ? ? ? }else if(notValueKeys.size() == paramEntitiesSize){ ? ? ? ? ? ? //多參數(shù)且全部為空,則條件不生產(chǎn) ? ? ? ? ? ? newParSql = ""; ? ? ? ? } ? ? ? ? return newParSql; ? ? } ? ? private Object initDefaultValue(SqlParamEntity entity){ ? ? ? ? if(SqlWrapperType.List.equal(entity.getType())){ ? ? ? ? ? ? return Arrays.asList(entity.getDefaultValue().split("\\|")); ? ? ? ? }else{ ? ? ? ? ? ? return entity.getDefaultValue(); ? ? ? ? } ? ? } ? ? private Object initValue(Object value){ ? ? ? ? if (null == value) return null; ? ? ? ? if(value instanceof String || value instanceof Character){ ? ? ? ? ? ? return "'" + value + "'"; ? ? ? ? }else { ? ? ? ? ? ? return value; ? ? ? ? } ? ? } ? ? /** 判斷字符串是否為空或 trim() 后長度為0 */ ? ? private static boolean isBlank(String str){ ? ? ? ? return null == str || str.trim().length() == 0; ? ? } ? ? /** 判斷字符串是否為空或長度為0 */ ? ? private static boolean isEmpty(String str){ ? ? ? ? return null == str || str.length() == 0; ? ? } }
4、新增sql處理類
import java.util.HashMap; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; /** ?* 自定義Sql語法封裝器 ?*/ public class SqlWrapper { ? ? static final String SqlBgtStr = "{{"; ? ? static final String SqlEndStr = "}}"; ? ? static final String ParBgtStr = "["; ? ? static final String ParEndStr = "]"; ? ? static final String SignSql = "$"; ? ? static final String SignPar = "?"; ? ? private static final Pattern pattern = Pattern.compile("(?<=\\{\\{)(.+?)(?=}})"); ? ? private String sqlBody; ? ? private final Map<String, SqlParamWrapper> paramWrappers = new HashMap<>(); ? ? /** ? ? ?* 自定義Sql語法封裝器 ? ? ?*/ ? ? public SqlWrapper(String sql){ ? ? ? ? this.sqlBody = sql; ? ? ? ? initSqlWrapper(); ? ? } ? ? /** ? ? ?* 初始化自定義Sql語法 ? ? ?*/ ? ? private void initSqlWrapper(){ ? ? ? ? Matcher matcher = pattern.matcher(this.sqlBody); ? ? ? ? int count = 1; ? ? ? ? while (matcher.find()){ ? ? ? ? ? ? String group = matcher.group(); ? ? ? ? ? ? String parSign = SignSql + count++ + SignSql; ? ? ? ? ? ? paramWrappers.put(parSign, new SqlParamWrapper(group)); ? ? ? ? ? ? this.sqlBody = this.sqlBody.replace(SqlBgtStr + group + SqlEndStr, " " + parSign + " "); ? ? ? ? } ? ? } ? ? /** ? ? ?* 根據(jù)參數(shù)初始化自定義sql查詢條件 ? ? ?*/ ? ? public String getSql(Map<?,?> map) { ? ? ? ? String newSqlBody = this.sqlBody.replace(" ?", " "); ? ? ? ? if(null == map || map.size() == 0){ ? ? ? ? ? ? for (Map.Entry<String, SqlParamWrapper> next : paramWrappers.entrySet()) { ? ? ? ? ? ? ? ? newSqlBody = newSqlBody.replace(next.getKey(), ""); ? ? ? ? ? ? } ? ? ? ? ? ? return newSqlBody; ? ? ? ? } ? ? ? ? for (Map.Entry<String, SqlParamWrapper> next : paramWrappers.entrySet()) { ? ? ? ? ? ? newSqlBody = newSqlBody.replace(next.getKey(), next.getValue().getParamSql(map)); ? ? ? ? } ? ? ? ? return newSqlBody; ? ? } ? ? /** ? ? ?* 判斷sql是否包含自定義語法 ? ? ?*/ ? ? public static boolean matcherSql(String sql){ ? ? ? ? return pattern.matcher(sql).find(); ? ? } }
5、添加自定義語法攔截器
import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.conditions.AbstractWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.PluginUtils; import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.zorgn.common.ExtMap; import com.zorgn.core.mybatis.MyBaitsSqlInnerException; import com.zorgn.core.mybatis.SqlWrapper; import org.apache.ibatis.binding.MapperMethod; import org.apache.ibatis.executor.Executor; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.session.ResultHandler; import org.apache.ibatis.session.RowBounds; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.math.BigDecimal; import java.sql.SQLException; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.Map; /** ?* 自定義Sql語法攔截器 ?*/ public class MyBatisSqlInnerInterceptor implements InnerInterceptor { ? ? private static final Map<String, SqlWrapper> SQL_WRAPPER_MAP = new HashMap<>(); ? ? private static final Logger logger = LoggerFactory.getLogger("SQL"); ? ? private static final String regex = "[\\s]+"; // 替換空格、換行、tab縮進(jìn)等; ? ? private static final String LogPreparing ?= "{} - ==> ?Preparing: {}"; ? ? private static final String LogParameters = "{} - ==> Parameters: {}"; ? ? private final boolean openThreeLevelParams; // 是否開啟三級參數(shù)名,默認(rèn)是二級:user.id,三級:user.creator.id ? ? /** ? ? ?* 構(gòu)造器:自定義Sql語法攔截器,默認(rèn)為二級參數(shù)名稱:user.id ? ? ?*/ ? ? public MyBatisSqlInnerInterceptor(){ ? ? ? ? this(false); ? ? } ? ? /** ? ? ?* 構(gòu)造器:自定義Sql語法攔截器,并設(shè)置是否開啟三級參數(shù)名稱,三級參數(shù)名稱:user.creator.id ? ? ?*/ ? ? public MyBatisSqlInnerInterceptor(Boolean openThreeLevelParams){ ? ? ? ? this.openThreeLevelParams = openThreeLevelParams; ? ? } ? ? /** ? ? ?* Query 前置事件,處理自定義Sql邏輯,打印 Sql 語句和參數(shù) ? ? ?*/ ? ? @Override ? ? public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { ? ? ? ? String sql = boundSql.getSql().replaceAll(regex, " "); ? ? ? ? String sqlId = ms.getId(); ? ? ? ? if(SQL_WRAPPER_MAP.containsKey(sqlId) || SqlWrapper.matcherSql(sql)) { ? ? ? ? ? ? SqlWrapper sqlWrapper = SQL_WRAPPER_MAP.get(sqlId); ? ? ? ? ? ? if (null == sqlWrapper) { ? ? ? ? ? ? ? ? sqlWrapper = new SqlWrapper(sql); ? ? ? ? ? ? ? ? SQL_WRAPPER_MAP.put(sqlId, sqlWrapper); ? ? ? ? ? ? ? ? logger.info("初始化Sql模板, SqlId: {}, SQL: {}", sqlId, sql); ? ? ? ? ? ? } ? ? ? ? ? ? String newSql = null; ? ? ? ? ? ? try { ? ? ? ? ? ? ? ? PluginUtils.MPBoundSql mpBoundSql = PluginUtils.mpBoundSql(boundSql); ? ? ? ? ? ? ? ? Map<?,?> newParamMap = initParameter(parameter, sqlId); ? ? ? ? ? ? ? ? newSql = sqlWrapper.getSql(newParamMap); ? ? ? ? ? ? ? ? mpBoundSql.sql(newSql); ? ? ? ? ? ? ? ? logger.info(LogPreparing, sqlId, newSql); ? ? ? ? ? ? ? ? logger.info(LogParameters, sqlId, getSqlParameter(parameter)); ? ? ? ? ? ? } catch (Exception e) { ? ? ? ? ? ? ? ? throw new MyBaitsSqlInnerException("Sql模板異常:" + e.getMessage(), sqlId, (null == newSql ? sql : newSql), e); ? ? ? ? ? ? } ? ? ? ? }else{ ? ? ? ? ? ? logger.info(LogPreparing, sqlId, sql); ? ? ? ? ? ? logger.info(LogParameters, sqlId, getSqlParameter(parameter)); ? ? ? ? } ? ? ? ? InnerInterceptor.super.beforeQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql); ? ? } ? ? /** ? ? ?* Insert、Update、Delete 前置任務(wù),打印 Sql 語句及參數(shù) ? ? ?*/ ? ? @Override ? ? public void beforeUpdate(Executor executor, MappedStatement ms, Object parameter) throws SQLException { ? ? ? ? BoundSql boundSql = ms.getBoundSql(parameter); ? ? ? ? String sqlId = ms.getId(); ? ? ? ? logger.info(LogPreparing, sqlId, boundSql.getSql().replaceAll(regex, " ")); ? ? ? ? logger.info(LogParameters, sqlId, getSqlParameter(parameter)); ? ? ? ? InnerInterceptor.super.beforeUpdate(executor, ms, parameter); ? ? } ? ? /** ? ? ?* 初始化多參數(shù) ? ? ?*/ ? ? private Map<?,?> initParameter(Object parameter, String sqlId){ ? ? ? ? if(null == parameter){ ? ? ? ? ? ? return null; ? ? ? ? } ? ? ? ? // 多參數(shù)處理,去除分頁參數(shù) ? ? ? ? if(parameter instanceof MapperMethod.ParamMap){ ? ? ? ? ? ? MapperMethod.ParamMap<?> paramMap = (MapperMethod.ParamMap<?>) parameter; ? ? ? ? ? ? return initMapperMethodParamMap(paramMap); ? ? ? ? } ? ? ? ? if(parameter instanceof Map){ ? ? ? ? ? ? return (Map<?,?>)parameter; ? ? ? ? } ? ? ? ? if(isBaseClassTypes(parameter)){ ? ? ? ? ? ? Map<String,Object> newParamMap = new HashMap<>(); ? ? ? ? ? ? newParamMap.put(getSqlIdMethodParamName(sqlId, parameter).toLowerCase(), parameter); ? ? ? ? ? ? return newParamMap; ? ? ? ? } ? ? ? ? return objectToMap(parameter); ? ? } ? ? /** ? ? ?* 多參數(shù)處理 ? ? ?*/ ? ? private Map<String,Object> initMapperMethodParamMap( MapperMethod.ParamMap<?> paramMap){ ? ? ? ? Map<String,Object> newParamMap = new HashMap<>(); ? ? ? ? for (String key : paramMap.keySet()) { ? ? ? ? ? ? if(key.startsWith("param")){ ? ? ? ? ? ? ? ? continue; ? ? ? ? ? ? } ? ? ? ? ? ? Object object = paramMap.get(key); ? ? ? ? ? ? if(null == object || object instanceof AbstractWrapper){ ? ? ? ? ? ? ? ? // 自定義sql查詢語句不需要 QueryWrapper 對象 ? ? ? ? ? ? ? ? continue; ? ? ? ? ? ? } ? ? ? ? ? ? if(object instanceof Page){ ? ? ? ? ? ? ? ? Page<?> page = (Page<?>) object; ? ? ? ? ? ? ? ? newParamMap.put("page.current", page.getCurrent()); ? ? ? ? ? ? ? ? newParamMap.put("page.size", page.getSize()); ? ? ? ? ? ? ? ? continue; ? ? ? ? ? ? } ? ? ? ? ? ? if(isBaseClassTypes(object)){ ? ? ? ? ? ? ? ? newParamMap.put(key, object); ? ? ? ? ? ? ? ? continue; ? ? ? ? ? ? } ? ? ? ? ? ? Map<?,?> map = (object instanceof Map) ? (Map<?,?>) object : objectToMap(object); ? ? ? ? ? ? for (Map.Entry<?, ?> entry : map.entrySet()) { ? ? ? ? ? ? ? ? Object value = entry.getValue(); ? ? ? ? ? ? ? ? if(null == value){ ? ? ? ? ? ? ? ? ? ? continue; ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? String sonKey = (key + "." + entry.getKey()).toLowerCase(); ? ? ? ? ? ? ? ? if(!openThreeLevelParams || isBaseClassTypes(value)){ ? ? ? ? ? ? ? ? ? ? newParamMap.put(sonKey, value); ? ? ? ? ? ? ? ? }else{ ? ? ? ? ? ? ? ? ? ? initThreeLevelParams(newParamMap, sonKey, value); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? } ? ? ? ? return newParamMap; ? ? } ? ? /** ? ? ?* 處理三級參數(shù)名稱 ? ? ?*/ ? ? private void initThreeLevelParams(Map<String,Object> newParamMap, String sonKey, Object value){ ? ? ? ? if(value instanceof Map){ ? ? ? ? ? ? Map<?,?> sonMap = (Map<?,?>) value; ? ? ? ? ? ? for (Map.Entry<?, ?> son : sonMap.entrySet()) { ? ? ? ? ? ? ? ? Object sonValue = son.getValue(); ? ? ? ? ? ? ? ? if(null == sonValue){ ? ? ? ? ? ? ? ? ? ? continue; ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? newParamMap.put(sonKey + "." + son.getKey(), sonValue); ? ? ? ? ? ? } ? ? ? ? ? ? return; ? ? ? ? } ? ? ? ? for (Map.Entry<?, ?> son : objectToMap(value).entrySet()) { ? ? ? ? ? ? Object sonValue = son.getValue(); ? ? ? ? ? ? if(null == sonValue){ ? ? ? ? ? ? ? ? continue; ? ? ? ? ? ? } ? ? ? ? ? ? newParamMap.put(sonKey + "." + son.getKey(), sonValue); ? ? ? ? } ? ? } ? ? /** ? ? ?* 根據(jù) sqlId 獲取 mapper 方法參數(shù)名稱 ? ? ?*/ ? ? private String getSqlIdMethodParamName(String sqlId, Object parameter){ ? ? ? ? try { ? ? ? ? ? ? int index = sqlId.lastIndexOf("."); ? ? ? ? ? ? String mapperId = sqlId.substring(0, index); ? ? ? ? ? ? String methodId = sqlId.substring(++ index); ? ? ? ? ? ? return Class.forName(mapperId).getMethod(methodId, parameter.getClass()).getParameters()[0].getName(); ? ? ? ? } catch (Exception ex){ ? ? ? ? ? ? throw new RuntimeException("獲取Sql模板參數(shù)名稱失敗", ex); ? ? ? ? } ? ? } ? ? /** ? ? ?* 判斷參數(shù)類型是否是常用基本類型 ? ? ?*/ ? ? private boolean isBaseClassTypes(Object parameter){ ? ? ? ? return parameter instanceof String ? ? ? ? ? ? ? ? || parameter instanceof Integer ? ? ? ? ? ? ? ? || parameter instanceof Collection ? ? ? ? ? ? ? ? || parameter instanceof Long ? ? ? ? ? ? ? ? || parameter instanceof Date ? ? ? ? ? ? ? ? || parameter instanceof Double ? ? ? ? ? ? ? ? || parameter instanceof Boolean ? ? ? ? ? ? ? ? || parameter instanceof Float ? ? ? ? ? ? ? ? || parameter instanceof Short ? ? ? ? ? ? ? ? || parameter instanceof Character ? ? ? ? ? ? ? ? || parameter instanceof BigDecimal; ? ? } ? ? /** ? ? ?* 對象屬性轉(zhuǎn)map集合 ? ? ?*/ ? ? private Map<?,?> objectToMap(Object parameter){ ? ? ? ? return ((JSONObject)JSONObject.toJSON(parameter)).toJavaObject(Map.class); ? ? } ? ? /** ? ? ?* 獲取sql參數(shù),用于日志打印 ? ? ?*/ ? ? private Object getSqlParameter(Object parameter){ ? ? ? ? if(null == parameter){ ? ? ? ? ? ? return null; ? ? ? ? } ? ? ? ? if(parameter instanceof Map){ ? ? ? ? ? ? Map<Object,Object> map = new HashMap<>(); ? ? ? ? ? ? Map<?,?> pm = (Map<?,?>) parameter; ? ? ? ? ? ? pm.forEach((k, v) -> { ? ? ? ? ? ? ? ? if(null == v || k.toString().startsWith("param")){ ? ? ? ? ? ? ? ? ? ? return; ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? if(v instanceof IPage) { ? ? ? ? ? ? ? ? ? ? IPage<?> page = (IPage<?>) v; ? ? ? ? ? ? ? ? ? ? map.put(k, ExtMap.parse("current", page.getCurrent(), "size", page.getSize())); ? ? ? ? ? ? ? ? ? ? return; ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? if(v instanceof AbstractWrapper){ ? ? ? ? ? ? ? ? ? ? map.put(k, getAbstractWrapperInfo(v)); ? ? ? ? ? ? ? ? ? ? return; ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? map.put(k,v); ? ? ? ? ? ? }); ? ? ? ? ? ? return map; ? ? ? ? } ? ? ? ? if(parameter instanceof IPage) { ? ? ? ? ? ? IPage<?> page = (IPage<?>) parameter; ? ? ? ? ? ? return ExtMap.parse("page", ExtMap.parse("current", page.getCurrent(), "size", page.getSize())); ? ? ? ? } ? ? ? ? if(parameter instanceof AbstractWrapper){ ? ? ? ? ? ? return getAbstractWrapperInfo(parameter); ? ? ? ? } ? ? ? ? return parameter; ? ? } ? ? private ExtMap<Object, Object> getAbstractWrapperInfo(Object parameter){ ? ? ? ? AbstractWrapper<?,?,?> wrapper = (AbstractWrapper<?,?,?>) parameter; ? ? ? ? ExtMap<Object, Object> vars = new ExtMap<>(); ? ? ? ? vars.put("entity", JSONObject.toJSONString(wrapper.getEntity())); ? ? ? ? vars.put("sql_segment", wrapper.getCustomSqlSegment()); ? ? ? ? vars.put("sql_vars", wrapper.getParamNameValuePairs()); ? ? ? ? return vars; ? ? } }
6、將自定義攔截器添加到mysql中,即可使用本自定義sql語法(詳情請看 四 - 2 )
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Spring?Security中如何獲取AuthenticationManager對象
有時需要使用AuthenticationManager(以下簡稱Manager)對象,可是這個對象不是Bean,沒有直接保存在Spring的Bean庫中,那么如何獲取Spring Security中的這個對象呢,需要的朋友可以參考下2022-11-11SpringBoot中忽略實體類中的某個屬性不返回給前端的方法(示例詳解)
本文介紹了在Spring Boot中使用Jackson和Fastjson忽略實體類屬性不返回給前端的方法,在Jackson中,同時使用@JsonProperty和@JsonIgnore時,@JsonIgnore可能失效,Fastjson中可以使用@JSONField(serialize=false)來實現(xiàn),本文結(jié)合實例代碼介紹的非常詳細(xì),需要的朋友參考下吧2024-11-11SpringBoot實現(xiàn)接口數(shù)據(jù)的加解密功能
這篇文章主要介紹了SpringBoot實現(xiàn)接口數(shù)據(jù)的加解密功能,對接口的加密解密操作主要有兩種實現(xiàn)方式,文中給大家詳細(xì)介紹,需要的朋友可以參考下2019-10-10Spring Boot 2結(jié)合Spring security + JWT實現(xiàn)微信小程序登錄
這篇文章主要介紹了Spring Boot 2結(jié)合Spring security + JWT實現(xiàn)微信小程序登錄,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-01-01使用Swagger2實現(xiàn)自動生成RESTful?API文檔
在開發(fā)?RESTful?API?的過程中,文檔是非常重要的一部分,可以幫助開發(fā)者了解?API?的功能和使用方法,本文將使用Swagger2?實現(xiàn)自動生成?RESTful?API?文檔,需要的可以參考一下2023-06-06idea 創(chuàng)建 maven web 工程流程(圖文教程)
這篇文章主要介紹了idea 創(chuàng)建 maven web 工程流程(圖文教程),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-05-05