• mybatis-3.5.0使用插件拦截sql以及通用字段赋值


    1、添加插件配置类

    1. import org.apache.commons.lang3.StringUtils;
    2. import org.apache.commons.lang3.reflect.FieldUtils;
    3. import org.apache.ibatis.executor.Executor;
    4. import org.apache.ibatis.mapping.BoundSql;
    5. import org.apache.ibatis.mapping.MappedStatement;
    6. import org.apache.ibatis.mapping.SqlCommandType;
    7. import org.apache.ibatis.mapping.SqlSource;
    8. import org.apache.ibatis.plugin.*;
    9. import org.apache.ibatis.session.defaults.DefaultSqlSession;
    10. import org.slf4j.Logger;
    11. import org.slf4j.LoggerFactory;
    12. import org.springframework.stereotype.Component;
    13. import tk.mybatis.mapper.annotation.LogicDelete;
    14. import tk.mybatis.mapper.annotation.Version;
    15. import java.io.File;
    16. import java.lang.reflect.Field;
    17. import java.util.*;
    18. import java.util.regex.Matcher;
    19. import java.util.regex.Pattern;
    20. @Component
    21. @Intercepts({@Signature(
    22. type = Executor.class,
    23. method = "update",
    24. args = {MappedStatement.class, Object.class}
    25. )})
    26. public class MybatisInterception implements Interceptor {
    27. public MybatisInterception() {
    28. }
    29. public Object intercept(Invocation invocation) throws Throwable {
    30. MappedStatement mappedStatement = (MappedStatement)invocation.getArgs()[0];
    31. SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
    32. Object parameter = invocation.getArgs()[1];
    33. if (parameter == null) {
    34. return invocation.proceed();
    35. } else {
    36. Field[] declaredFields = this.getAllFields(parameter);
    37. Date now = new Date();
    38. String userId = ContextUtil.getUserId();
    39. Integer dteptId = ContextUtil.getDteptId();
    40. Field[] var9 = declaredFields;
    41. if (SqlCommandType.DELETE.equals(sqlCommandType)) {
    42. BoundSql boundSql = mappedStatement.getBoundSql(parameter);
    43. //转换DELETE语句为逻辑删除语句
    44. String logicDeleteSql = convertToLogicDelete(boundSql.getSql());
    45. FieldUtils.writeField(boundSql, "sql", logicDeleteSql, true);
    46. MappedStatement newMappedStatement = new MappedStatement.Builder(mappedStatement.getConfiguration(),
    47. mappedStatement.getId(), new BoundSqlSqlSource(boundSql), mappedStatement.getSqlCommandType())
    48. .build();
    49. invocation.getArgs()[0] = newMappedStatement;
    50. return invocation.proceed();
    51. }
    52. if(parameter instanceof DefaultSqlSession.StrictMap) {
    53. DefaultSqlSession.StrictMap strictMap = (DefaultSqlSession.StrictMap) parameter;
    54. Object collection = strictMap.get("collection");
    55. if(collection instanceof Collection) {
    56. for (Object obj : ((Collection) collection)) {
    57. Field[] fields = this.getAllFields(obj);
    58. for (Field field : fields) {
    59. extracted(sqlCommandType, obj, now, userId, dteptId, field);
    60. }
    61. }
    62. }
    63. } else {
    64. for (Field field : var9) {
    65. extracted(sqlCommandType, parameter, now, userId, dteptId, field);
    66. }
    67. }
    68. return invocation.proceed();
    69. }
    70. }
    71. private void extracted(SqlCommandType sqlCommandType, Object parameter, Date now, String userId, Integer dteptId, Field field) throws IllegalAccessException {
    72. if (field.getAnnotation(NotAutoFill.class) == null) {
    73. Class clazz;
    74. if (SqlCommandType.INSERT.equals(sqlCommandType)) {
    75. if (field.getAnnotation(CreateTime.class) != null || Objects.equals(field.getName(), "createTime")) {
    76. field.setAccessible(true);
    77. field.set(parameter, now);
    78. }
    79. if (field.getAnnotation(Version.class) != null || Objects.equals(field.getName(), "version")) {
    80. field.setAccessible(true);
    81. field.set(parameter, 1);
    82. }
    83. if (field.getAnnotation(LogicDelete.class) != null || Objects.equals(field.getName(), "isDeleted")) {
    84. field.setAccessible(true);
    85. field.set(parameter, 0);
    86. }
    87. if (Objects.equals(field.getName(), "status")) {
    88. field.setAccessible(true);
    89. if (field.getType() == Integer.class) {
    90. field.set(parameter, 0);
    91. }
    92. }
    93. if (Objects.equals(field.getName(), "createBy")) {
    94. field.setAccessible(true);
    95. if (StringUtils.isNotEmpty(userId)) {
    96. clazz = field.getType();
    97. if (clazz == Integer.class) {
    98. field.set(parameter, Integer.parseInt(userId));
    99. } else {
    100. field.set(parameter, userId);
    101. }
    102. }
    103. }
    104. if (Objects.equals(field.getName(), "createDeptId")) {
    105. field.setAccessible(true);
    106. field.set(parameter, dteptId);
    107. }
    108. }
    109. if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {
    110. if (field.getAnnotation(UpdateTime.class) != null || Objects.equals(field.getName(), "updateTime")) {
    111. field.setAccessible(true);
    112. field.set(parameter, now);
    113. }
    114. if (Objects.equals(field.getName(), "updateBy")) {
    115. field.setAccessible(true);
    116. clazz = field.getType();
    117. if (StringUtils.isNotEmpty(userId)) {
    118. if (clazz == Integer.class) {
    119. field.set(parameter, Integer.parseInt(userId));
    120. } else {
    121. field.set(parameter, userId);
    122. }
    123. }
    124. }
    125. }
    126. }
    127. }
    128. public static Class findEntityClassByTableName(String tableName, String basePackage) {
    129. ClassPathScanningCandidateComponentProvider scanner =
    130. new ClassPathScanningCandidateComponentProvider(false);
    131. scanner.addIncludeFilter(new AnnotationTypeFilter(Table.class));
    132. Set beanDefinitions = scanner.findCandidateComponents(basePackage);
    133. for (BeanDefinition beanDefinition : beanDefinitions) {
    134. try {
    135. Class clazz = Class.forName(beanDefinition.getBeanClassName());
    136. Table table = clazz.getAnnotation(Table.class);
    137. if (table != null && table.name().equals(tableName)) {
    138. return clazz;
    139. }
    140. } catch (ClassNotFoundException e) {
    141. continue;
    142. }
    143. }
    144. return null;
    145. }
    146. private String convertToLogicDelete(String sql) throws NoSuchFieldException {
    147. Pattern pattern = Pattern.compile("DELETE\\s+FROM\\s+(\w+)\\s+WHERE\\s+(.*)", Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
    148. Matcher matcher = pattern.matcher(sql);
    149. if (matcher.matches()) {
    150. String tableName = matcher.group(1); //获取表名
    151. Class classByTableName = findEntityClassByTableName(tableName, "com.demo.entity");
    152. if(classByTableName == null) {
    153. return sql;
    154. }
    155. Field isDeletedField = getDeletedField(classByTableName);
    156. if(isDeletedField == null || isDeletedField.getAnnotation(LogicDelete.class) == null) {
    157. return sql;
    158. }
    159. String whereClause = matcher.group(2).trim(); //获取WHERE子句并去除两端空白字符
    160. // 构建逻辑删除的UPDATE语句
    161. StringBuilder logicDeleteSql = new StringBuilder();
    162. logicDeleteSql.append("UPDATE ").append(tableName).append(" SET ").append("is_deleted").append(" = ").append(1);
    163. //如果WHERE子句不为空或不全为空白字符,则加上WHERE子句
    164. if (!whereClause.isEmpty()) {
    165. logicDeleteSql.append(" WHERE ").append(whereClause);
    166. }
    167. return logicDeleteSql.toString();
    168. } else {
    169. // 如果SQL语句格式不匹配,则返回原始SQL或抛出异常,这里为了简单起见,直接返回原始 SQL
    170. return sql;
    171. }
    172. }
    173. public Object plugin(Object o) {
    174. return Plugin.wrap(o, this);
    175. }
    176. public void setProperties(Properties properties) {
    177. }
    178. private Field getDeletedField(Class clazz) {
    179. String isDeleted = "isDeleted";
    180. Field isDeletedField = null;
    181. for (Class c = clazz; c != null; c = c.getSuperclass()) {
    182. Field[] declaredFields = c.getDeclaredFields();
    183. for (Field field : declaredFields) {
    184. if(field.getName().equals(isDeleted)) {
    185. isDeletedField = field;
    186. break;
    187. }
    188. }
    189. }
    190. return isDeletedField;
    191. }
    192. private Field[] getAllFields(Object object) {
    193. Class clazz = object.getClass();
    194. ArrayList fieldList;
    195. for(fieldList = new ArrayList(); clazz != null; clazz = clazz.getSuperclass()) {
    196. fieldList.addAll(new ArrayList(Arrays.asList(clazz.getDeclaredFields())));
    197. }
    198. Field[] fields = new Field[fieldList.size()];
    199. fieldList.toArray(fields);
    200. return fields;
    201. }
    202. public class BoundSqlSqlSource implements SqlSource {
    203. private BoundSql boundSql;
    204. public BoundSqlSqlSource(BoundSql boundSql) {
    205. this.boundSql = boundSql;
    206. }
    207. @Override
    208. public BoundSql getBoundSql(Object parameterObject) {
    209. return boundSql;
    210. }
    211. }
    212. }
    213. 2、修改mybatis-config.xml

      切身经历但不一定是真相:项目已经有拦截器和mybatis-config.xml比如在公共依赖中,再新建mybatis-config.xml会存在覆盖xml配置的情况,虽然拦截器都生效了。如果命名为mybatis-config-xxx.xml等拦截器就没有生效。通过采用配置类的形式,暂时未发现不良影响。

      1. "1.0" encoding="utf-8"?>
      2. configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
      3. <configuration>
      4. <plugins>
      5. <plugin interceptor="xxx.xxx.MybatisInterceptor">
      6. plugin>
      7. plugins>

      3、配置类,一般叫MybatisConfig,显然不可重名。于是另起炉灶。

      1. @Configuration
      2. @AutoConfigureAfter({PageHelperAutoConfiguration.class})
      3. public class MybatisFeatureConfig {
      4. @Autowired
      5. private List sqlSessionFactoryList;
      6. public MybatisFeatureConfig() {
      7. }
      8. @PostConstruct
      9. public void addMyInterceptor() {
      10. MybatisInterception e = new MybatisInterception();
      11. Iterator var2 = this.sqlSessionFactoryList.iterator();
      12. while(var2.hasNext()) {
      13. SqlSessionFactory sqlSessionFactory = (SqlSessionFactory)var2.next();
      14. sqlSessionFactory.getConfiguration().addInterceptor(e);
      15. }
      16. }
      17. }

      4、基本思路,如果使用mybatis-plus我尚未注意过批量操作的问题,所以不知会有什么坑

    214. 相关阅读:
      如何用神经网络预测数据,人工神经网络分析方法
      【U8+】用友U8-UFO录入关键字,计算后乱码
      [python-大语言模型]从浅到深一系列学习笔记记录
      ELK介绍以及搭建
      包分配并不是个好制度
      c++学习--第二部分
      ifconfig看不见自己外网地址?
      AI加速(八)| 循环展开Unrooling——你肯定能学会的程序加速方法
      ceph 线程池分析
      第十四届蓝桥杯模拟赛(第二期)——C语言版
    215. 原文地址:https://blog.csdn.net/z275598733/article/details/137949421