欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Mybatis自定義Sql模板語法問題

 更新時間:2023年09月22日 11:34:29   作者:horgn黃小錘、  
這篇文章主要介紹了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、llikerlike

參數(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)文章

最新評論