package com.chinaztt.mes.common.aop; import cn.hutool.core.collection.CollectionUtil; import com.baomidou.mybatisplus.core.conditions.AbstractWrapper; import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException; import com.baomidou.mybatisplus.core.metadata.LinkFieldInfo; import com.baomidou.mybatisplus.core.metadata.TableFieldInfo; import com.baomidou.mybatisplus.core.metadata.TableInfo; import com.baomidou.mybatisplus.core.metadata.TableInfoHelper; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.aop.support.AopUtils; import org.springframework.cache.annotation.Cacheable; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Component; import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl; import java.util.*; import java.util.stream.Collectors; import static com.baomidou.mybatisplus.core.toolkit.Constants.WRAPPER_PARAM; /** * aop "伪外键"实现,删除数据时校验是否被引用 * * @Author: zhangxy * @Date: 2020-10-30 16:49 */ @Aspect @Component @AllArgsConstructor @Slf4j public class MybatisPlusDeleteAspect { private JdbcTemplate jdbcTemplate; public static final String CACHE_KEY = "TABLE_CLASS_CACHE"; private static String EXCEPTION_STR = "%s.%s 被 %s.%s 引用"; /** * 更新唯一编号 */ private static String UPDATE_NUM_SQL = "update %s set %s=%s||'('||id||'D)' where %s is not null %s and %s"; /** * 查询被引用的记录 */ private static String LINK_COUNT_SQL = "select count(*) from %s where %s %s "; @Pointcut("execution(public * com.chinaztt.mes.*.mapper.*.delete*(..)))") public void removeAspect() { } @Before("removeAspect()") public void doBeforeRemove(JoinPoint jp) { String methodName = jp.getSignature().getName(); Class typeClass = getTypeClassByJp(jp); //要删除的表 TableInfo tableInfo = TableInfoHelper.getTableInfo(typeClass); Set fieldInfoSet = TableInfoHelper.getLinkFieldInfosByTableName(tableInfo.getTableName()); if (CollectionUtil.isNotEmpty(fieldInfoSet)) { Iterator it = fieldInfoSet.iterator(); while (it.hasNext()) { doQuery(tableInfo, it.next(), methodName, jp.getArgs()[0]); } } //逻辑删除且存在唯一键时才需要处理 if (tableInfo.isLogicDelete() && tableInfo.isWithUnique()) { List uniqueFields = tableInfo.getFieldList().stream().filter(TableFieldInfo::isUnique).collect(Collectors.toList()); if (CollectionUtil.isNotEmpty(uniqueFields)) { for (TableFieldInfo tf : uniqueFields) { doUpdate(tableInfo, tf, methodName, jp.getArgs()[0]); } } } } private void doQuery(TableInfo sourceTable, LinkFieldInfo linkFieldInfo, String methodName, Object val) { if (val == null) { return; } Collection valList = new ArrayList<>(); TableInfo linkTableInfo = tableClassCache().get(linkFieldInfo.getLinkedTable()); String sql, appendSql; switch (methodName) { case "deleteById": appendSql = linkFieldInfo.getLinkedField() + "=?"; valList.add(val); break; case "deleteByMap": appendSql = linkFieldInfo.getLinkedField() + " in (select " + linkFieldInfo.getSourceField() + " from " + linkFieldInfo.getSourceTable() + " where "; Map paramMap = (Map) val; Iterator it = paramMap.keySet().iterator(); while (it.hasNext()) { String key = it.next(); appendSql += key + " =? "; if (it.hasNext()) { appendSql += " and "; } else { appendSql += (sourceTable.getLogicDeleteSql(true, true) + ")"); } valList.add(paramMap.get(key)); } break; case "delete": appendSql = linkFieldInfo.getLinkedField() + " in (select " + linkFieldInfo.getSourceField() + " from " + linkFieldInfo.getSourceTable() + " where "; appendSql += ((AbstractWrapper) val).getTargetSql(); appendSql += sourceTable.getLogicDeleteSql(true, true); appendSql += ")"; Map paramNameValuePairs = ((AbstractWrapper) val).getParamNameValuePairs(); List keys = new ArrayList<>(paramNameValuePairs.keySet()); keys = keys.stream().sorted((a, b) -> { Integer ai = Integer.valueOf(a.replace(WRAPPER_PARAM, "")); Integer bi = Integer.valueOf(b.replace(WRAPPER_PARAM, "")); return ai.compareTo(bi); }).collect(Collectors.toList()); for (String k : keys) { valList.add(paramNameValuePairs.get(k)); } break; case "deleteBatchIds": valList = (Collection) val; List list = new ArrayList<>(Collections.nCopies(valList.size(), "?")); appendSql = linkFieldInfo.getLinkedField() + " in (" + String.join(",", list) + ")"; break; default: return; } sql = String.format(LINK_COUNT_SQL, linkTableInfo.getTableName(), appendSql, linkTableInfo.getLogicDeleteSql(true, true)); System.out.println("执行sql=============================>"+sql); Long count = jdbcTemplate.queryForObject(sql, Long.class, valList.toArray()); if (count > 0) { String ex = String.format(EXCEPTION_STR, linkFieldInfo.getSourceTable(), linkFieldInfo.getSourceField(), linkFieldInfo.getLinkedTable(), linkFieldInfo.getLinkedField()); log.error(ex); throw new MybatisPlusException("删除失败,数据被引用中"); } } private void doUpdate(TableInfo tableInfo, TableFieldInfo tf, String methodName, Object val) { if (val == null) { return; } Collection valList = new ArrayList<>(); String sql, appendSql; switch (methodName) { case "deleteById": appendSql = tableInfo.getKeyColumn() + "=? "; valList.add(val); break; case "deleteByMap": appendSql = " "; Map paramMap = (Map) val; Iterator it = paramMap.keySet().iterator(); while (it.hasNext()) { String key = it.next(); appendSql += key + "=?"; if (it.hasNext()) { appendSql += " and "; } valList.add(paramMap.get(key)); } break; case "delete": appendSql = ((AbstractWrapper) val).getTargetSql(); Map paramNameValuePairs = ((AbstractWrapper) val).getParamNameValuePairs(); List keys = new ArrayList<>(paramNameValuePairs.keySet()); keys = keys.stream().sorted((a, b) -> { Integer ai = Integer.valueOf(a.replace(WRAPPER_PARAM, "")); Integer bi = Integer.valueOf(b.replace(WRAPPER_PARAM, "")); return ai.compareTo(bi); }).collect(Collectors.toList()); for (String k : keys) { valList.add(paramNameValuePairs.get(k)); } break; case "deleteBatchIds": valList = (Collection) val; List list = new ArrayList<>(Collections.nCopies(valList.size(), "?")); appendSql = tableInfo.getKeyColumn() + " in (" + String.join(",", list) + ")"; break; default: return; } sql = String.format(UPDATE_NUM_SQL, tableInfo.getTableName(), tf.getColumn(), tf.getColumn(), tf.getColumn(), tableInfo.getLogicDeleteSql(true, true), appendSql); jdbcTemplate.update(sql, valList.toArray()); } private Class getTypeClassByJp(JoinPoint jp) { Class actual = ((Class) AopUtils.getTargetClass(jp.getTarget()).getGenericInterfaces()[0]); return (Class) ((ParameterizedTypeImpl) actual.getGenericInterfaces()[0]).getActualTypeArguments()[0]; } @Cacheable(value = CACHE_KEY) public Map tableClassCache() { Map map = new HashMap<>(200); List list = TableInfoHelper.getTableInfos(); for (TableInfo tb : list) { map.put(tb.getTableName(), tb); } return map; } }