AbstractLikeSqlConverter.java 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. package com.lantone.dblayermbg.config.mybatisLike;
  2. import lombok.extern.slf4j.Slf4j;
  3. import org.apache.commons.lang3.StringUtils;
  4. import java.beans.IntrospectionException;
  5. import java.beans.PropertyDescriptor;
  6. import java.lang.reflect.InvocationTargetException;
  7. import java.lang.reflect.Method;
  8. import java.util.Set;
  9. /**
  10. * @Description: 包含like的SQL语句转义模板
  11. * @author: gaodm
  12. * @time: 2020/11/2 16:05
  13. */
  14. @Slf4j
  15. public abstract class AbstractLikeSqlConverter<T> {
  16. /**
  17. * SQL语句like使用关键字%
  18. */
  19. private final static String LIKE_SQL_KEY = "%";
  20. /**
  21. * SQL语句需要转义的关键字
  22. */
  23. private final static String[] ESCAPE_CHAR = new String[] { LIKE_SQL_KEY, "_", "\\" };
  24. /**
  25. * mybatis-plus中like的SQL语句样式
  26. */
  27. private final static String MYBATIS_PLUS_LIKE_SQL = " like ?";
  28. /**
  29. * mybatis-plus中参数前缀
  30. */
  31. private final static String MYBATIS_PLUS_WRAPPER_PREFIX = "ew.paramNameValuePairs.";
  32. /**
  33. * mybatis-plus中参数键
  34. */
  35. final static String MYBATIS_PLUS_WRAPPER_KEY = "ew";
  36. /**
  37. * mybatis-plus中参数分隔符
  38. */
  39. final static String MYBATIS_PLUS_WRAPPER_SEPARATOR = ".";
  40. /**
  41. * mybatis-plus中参数分隔符替换器
  42. */
  43. final static String MYBATIS_PLUS_WRAPPER_SEPARATOR_REGEX = "\\.";
  44. /**
  45. * 已经替换过的标记
  46. */
  47. final static String REPLACED_LIKE_KEYWORD_MARK = "replaced.keyword";
  48. /**
  49. * 转义特殊字符
  50. *
  51. * @param sql SQL语句
  52. * @param fields 字段列表
  53. * @param parameter 参数对象
  54. */
  55. public void convert(String sql, Set<String> fields, T parameter) {
  56. for (String field : fields) {
  57. if (this.hasMybatisPlusLikeSql(sql)) {
  58. if (this.hasWrapper(field)) {
  59. // 第一种情况:在业务层进行条件构造产生的模糊查询关键字,使用QueryWrapper,LambdaQueryWrapper
  60. this.transferWrapper(field, parameter);
  61. } else {
  62. // 第二种情况:未使用条件构造器,但是在service层进行了查询关键字与模糊查询符`%`手动拼接
  63. this.transferSelf(field, parameter);
  64. }
  65. } else {
  66. // 第三种情况:在Mapper类的注解SQL中进行了模糊查询的拼接
  67. this.transferSplice(field, parameter);
  68. }
  69. }
  70. }
  71. /**
  72. * 转义条件构造的特殊字符
  73. * 在业务层进行条件构造产生的模糊查询关键字,使用QueryWrapper,LambdaQueryWrapper
  74. *
  75. * @param field 字段名称
  76. * @param parameter 参数对象
  77. */
  78. public abstract void transferWrapper(String field, T parameter);
  79. /**
  80. * 转义自定义条件拼接的特殊字符
  81. * 未使用条件构造器,但是在service层进行了查询关键字与模糊查询符`%`手动拼接
  82. *
  83. * @param field 字段名称
  84. * @param parameter 参数对象
  85. */
  86. public abstract void transferSelf(String field, T parameter);
  87. /**
  88. * 转义自定义条件拼接的特殊字符
  89. * 在Mapper类的注解SQL中进行了模糊查询的拼接
  90. *
  91. * @param field 字段名称
  92. * @param parameter 参数对象
  93. */
  94. public abstract void transferSplice(String field, T parameter);
  95. /**
  96. * 转义通配符
  97. *
  98. * @param before 待转义字符串
  99. * @return 转义后字符串
  100. */
  101. String escapeChar(String before) {
  102. if (StringUtils.isNotBlank(before)) {
  103. before = before.replaceAll("\\\\", "\\\\\\\\");
  104. before = before.replaceAll("_", "\\\\_");
  105. before = before.replaceAll("%", "\\\\%");
  106. }
  107. return before;
  108. }
  109. /**
  110. * 是否包含需要转义的字符
  111. *
  112. * @param obj 待判断的对象
  113. * @return true/false
  114. */
  115. boolean hasEscapeChar(Object obj) {
  116. if (!(obj instanceof String)) {
  117. return false;
  118. }
  119. return this.hasEscapeChar((String) obj);
  120. }
  121. /**
  122. * 处理对象like问题
  123. *
  124. * @param field 对象字段
  125. * @param parameter 对象
  126. */
  127. void resolveObj(String field, Object parameter) {
  128. if (parameter == null || StringUtils.isBlank(field)) {
  129. return;
  130. }
  131. try {
  132. PropertyDescriptor descriptor = new PropertyDescriptor(field, parameter.getClass());
  133. Method readMethod = descriptor.getReadMethod();
  134. Object param = readMethod.invoke(parameter);
  135. if (this.hasEscapeChar(param)) {
  136. Method setMethod = descriptor.getWriteMethod();
  137. setMethod.invoke(parameter, this.escapeChar(param.toString()));
  138. } else if (this.cascade(field)) {
  139. int index = field.indexOf(MYBATIS_PLUS_WRAPPER_SEPARATOR) + 1;
  140. this.resolveObj(field.substring(index), param);
  141. }
  142. } catch (IntrospectionException | IllegalAccessException | InvocationTargetException e) {
  143. log.error("反射 {} 的 {} get/set方法出现异常", parameter, field, e);
  144. }
  145. }
  146. /**
  147. * 判断是否是级联属性
  148. *
  149. * @param field 字段名
  150. * @return true/false
  151. */
  152. boolean cascade(String field) {
  153. if (StringUtils.isBlank(field)) {
  154. return false;
  155. }
  156. return field.contains(MYBATIS_PLUS_WRAPPER_SEPARATOR) && !this.hasWrapper(field);
  157. }
  158. /**
  159. * 是否包含mybatis-plus的包含like的SQL语句格式
  160. *
  161. * @param sql 完整SQL语句
  162. * @return true/false
  163. */
  164. private boolean hasMybatisPlusLikeSql(String sql) {
  165. if (StringUtils.isBlank(sql)) {
  166. return false;
  167. }
  168. return sql.toLowerCase().contains(MYBATIS_PLUS_LIKE_SQL);
  169. }
  170. /**
  171. * 判断是否使用mybatis-plus条件构造器
  172. *
  173. * @param field 字段
  174. * @return true/false
  175. */
  176. private boolean hasWrapper(String field) {
  177. if (StringUtils.isBlank(field)) {
  178. return false;
  179. }
  180. return field.contains(MYBATIS_PLUS_WRAPPER_PREFIX);
  181. }
  182. /**
  183. * 判断字符串是否含有需要转义的字符
  184. *
  185. * @param str 待判断的字符串
  186. * @return true/false
  187. */
  188. private boolean hasEscapeChar(String str) {
  189. if (StringUtils.isBlank(str)) {
  190. return false;
  191. }
  192. for (String s : ESCAPE_CHAR) {
  193. if (str.contains(s)) {
  194. return true;
  195. }
  196. }
  197. return false;
  198. }
  199. }