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<LinkFieldInfo> fieldInfoSet = TableInfoHelper.getLinkFieldInfosByTableName(tableInfo.getTableName());
|
if (CollectionUtil.isNotEmpty(fieldInfoSet)) {
|
Iterator<LinkFieldInfo> it = fieldInfoSet.iterator();
|
while (it.hasNext()) {
|
doQuery(tableInfo, it.next(), methodName, jp.getArgs()[0]);
|
}
|
}
|
|
//逻辑删除且存在唯一键时才需要处理
|
if (tableInfo.isLogicDelete() && tableInfo.isWithUnique()) {
|
List<TableFieldInfo> 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<Object> 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<String, Object> paramMap = (Map<String, Object>) val;
|
Iterator<String> 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<String, Object> paramNameValuePairs = ((AbstractWrapper) val).getParamNameValuePairs();
|
List<String> 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<Object>) val;
|
List<String> 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));
|
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<Object> valList = new ArrayList<>();
|
String sql, appendSql;
|
switch (methodName) {
|
case "deleteById":
|
appendSql = tableInfo.getKeyColumn() + "=? ";
|
valList.add(val);
|
break;
|
case "deleteByMap":
|
appendSql = " ";
|
Map<String, Object> paramMap = (Map<String, Object>) val;
|
Iterator<String> 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<String, Object> paramNameValuePairs = ((AbstractWrapper) val).getParamNameValuePairs();
|
List<String> 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<Object>) val;
|
List<String> 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<String, TableInfo> tableClassCache() {
|
Map<String, TableInfo> map = new HashMap<>(200);
|
List<TableInfo> list = TableInfoHelper.getTableInfos();
|
for (TableInfo tb : list) {
|
map.put(tb.getTableName(), tb);
|
}
|
return map;
|
}
|
|
}
|