文章目录

  • 如何封装if的非空校验
  • 如何封装if return
  • Optional orElseThrow如何记录错误日志
  • 如何避免字段名被修改无法感知问题 如何避免魔法值
  • list如何存取一个元素
  • 如何简洁的往list里面添加有限个数元素
  • map如何存取一个键值对
  • 如何简洁的往map里面添加有限个数元素
  • 如何批量删除mysql中某前缀开头的表


如何封装if的非空校验

(注:@valid不是阐述重点,主要讲述如何封装if )

什么叫封装if呢 比如我们要判断名字是否为空 常规写法:

if(StringUtils.isBlank(name){
  throw new RuntimeException("名字不能为空");
}

还可能需要校验住址:

if(StringUtils.isBlank(address){
  throw new RuntimeException("地址不能为空");
}

在很多业务场景中,可能需要大量的判断,那就会导致大量的if 以及大括号 ,非常占用代码行数,那我们该如何简化呢?可以如下的写一个方法:

public static void checkBlank(String param,String message){
	if(StringUtils.isBlank(param){
	  throw new RuntimeException(message);
	}
}

调用方使用:

// 假设类名为 Check
Check.checkBlank(name,"姓名不能为空");
Check.checkBlank(address,"地址不能为空");

这样在业务流程代码 就不会看到大量的if以及大括号了,当然 针对不同类型的参数进行非空校验、是否相等校验 按照checkBlank方法自己去拓展就好了。

如何封装if return

我们以mybatis plus为例,in查询时,如果in参数为空数组 查询会报错

// 该方法会报错
List<Long> idList = new ArrayList();
orderService.list(new QueryWrapper<Order>().lambda()
				.in(Order::getId, idList))

传统校验写法:

if (CollectionUtils.isEmpty(idList)) {
			return  Collections.emptyList();
	}
orderService.list(new QueryWrapper<Order>().lambda()
				.in(Order::getId, idList))

封装if return写法:

public List<Order> listOrder(List<Long> idList) {
		// 减少了每次查询时 手动写if判断参数是否为空数组
		return MybatisPlusUtils.checkThenExecuteInQuery(
			idList,
			// mybatis plus 的 in 查询 : 不做任何处理时  当idList为空,会报错
			nonEmptyIdList -> orderService.list(new QueryWrapper<Order>().lambda()
				.in(Order::getId, idList))
		);
	}

MybatisPlusUtils:

public class MybatisPlusUtils {


	@SuppressWarnings("unchecked")
	public static <T, R> R checkThenExecuteInQuery(Collection<T> param, Function<Collection<T>, R> action) {
		if (CollectionUtils.isEmpty(param)) {
			return (R) Collections.emptyList();
		}
		return action.apply(param);
	}


}

或者

public List<Order> listOrder(List<Long> idList) {
		// 减少了每次查询时 手动写if判断参数是否为空数组
		return MybatisPlusUtils.checkThenExecuteInQuery(
			idList, Collections::emptyList,
			// mybatis plus 的 in 查询 : 不做任何处理时  当idList为空,会报错
			nonEmptyIdList -> orderService.list(new QueryWrapper<Order>().lambda()
				.in(Order::getId, idList))
		);
	}

utils则对应为:

public class MybatisPlusUtils {


	public static <T, R> R checkThenExecuteInQuery(Collection<T> param, Supplier<R> emptyResult, Function<Collection<T>, R> action) {
		if (CollectionUtils.isEmpty(param)) {
			return emptyResult.get();
		}
		return action.apply(param);
	}


}

Optional orElseThrow如何记录错误日志

我们传统的orElseThrow可能就是抛一个异常:

// 可以将() -> new RuntimeException("error msg") 封装,参考封装if的方式
Object o = Optional.ofNullable(null).orElseThrow(() -> new RuntimeException("error msg"));

有时候为null就是严重的业务异常 需要事后通过日志排查,我们可能需要记录详细的日志信息 我们只需要在箭头后面加上大括号,并在抛异常前记录日志即可 对lambda熟悉的同学应该比较容易想到,如果lambda不熟练可能会不知道如何写。

Optional.ofNullable(null).orElseThrow(() -> {
            log.info("other error");
            return new RuntimeException("xxx");
        });
// 异常封装:
public static Supplier<RuntimeException> supplier(String bizMessage,String logMsg) {
        log.info(logMsg);
        return () -> new RuntimeException(bizMessage);
    }

如何避免字段名被修改无法感知问题 如何避免魔法值

某个类的字段名修改了 其它地方可能不知道 解决方法 : 使用Function转换 ,这样不仅可以避免下游无法感知字段名变更的问题 也可以避免魔法值。

让我们用个例子来说明是什么意思: 比如我们的Oper实体类里面 有一个ukKey字段,我们在某个地方 将ukKey作为key 存放到map中,某一天 团队中其他人修改了这个字段名,比如改成了uniqueKey,这个时候map的key值 极大可能就会被疏忽。

转换代码:

Map<String,Integer> map = new HashMap<>();
   map.put(FunctionUtils.function2Str(Oper::getUkKey), 123456);

FunctionUtils 工具类
(这是博主多年前写的一段代码 也是博主非常为之骄傲的一段代码):

public class FunctionUtils <T>{
    /**
     * Description: 函数式参数转回String属性名
     * date: 2021/06/09 12:05
     * @param sFunctionField
     * @return
     * @author qkj
     */
     public static <T> String function2Str (SFunction<T, ?> sFunctionField) {
        Method writeReplace = null;
        try {
            // 函数式方法可以直接调用writeReplace
            writeReplace = sFunctionField.getClass().getDeclaredMethod("writeReplace");
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        writeReplace.setAccessible(true);
        String fieldName = "";
        try {
            // 序列化
            Object object = writeReplace.invoke(sFunctionField);
            // 反序列化
            SerializedLambda serializedLambda = (SerializedLambda) object;
            String methodName = serializedLambda.getImplMethodName();
            String temp = methodName.substring(3);
            fieldName = temp.replaceFirst(temp.substring(0,1),temp.substring(0,1).toLowerCase());
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

        return fieldName;

    }

}

java中使用groovy脚本时,上述方法不适用,groovy版:

/**
     * 在groovy里面使用mybatis plus 构造器
     * java标准包的lambda方式和groovy有差异
     * usage:  GroovyShell.parse方式调试得出本方法,不保证其它方式生成的 Script 也能使用; 其它方式需要重新调试具体源代码
     * @param function
     * @author git: qiuhuanhen  csdn: 孟秋与你
     */
    public static <T> String function2StrInGroovy(Function<T, ?> function) {

        InvocationHandler handler = Proxy.getInvocationHandler(function);

        // 从本类、父类中查找方法
        Method[] methods = handler.getClass().getMethods();
        for (Method method : methods) {
            if (Objects.equals("getDelegate",method.getName())) {
                // 拿到了getDelegate方法
                // 使用反射调用 getDelegate 方法
                MethodClosure delegate = null;
                try {
                    delegate = (MethodClosure) method.invoke(handler);
                } catch (IllegalAccessException | InvocationTargetException e) {
                    throw new RuntimeException(e);
                }
                String methodName = delegate.getMethod();
                String temp = methodName.substring(3);
                temp = temp.replaceFirst(temp.substring(0, 1), temp.substring(0, 1).toLowerCase());
                return temp;
            }
        }
        throw new RuntimeException("can't found method");
    }

list如何存取一个元素

这种场景通常会出现在我们之前已经写了一个方法,入参是List;
但后来我们调用方的参数 又是一个对象,所以需要将对象转成list.

// 通常写法 因为即使只有一个元素 但底层定义的是 E[] 
List<Integer> list = Arrays.asList(111);

// 更合适的写法 定义的是E 
List<Integer> list = Collections.singletonList(111);

但是需要注意的是 SingletonList是一个私有内部类;
所以Collections.singletonList返回的是父类List,即使是一个元素 我们获取还是要用 list.get(0) , 而不能直接使用 list.get()。
JDK中既然没有提供简便的方法,我们可以自己简单包装一下:

public class SingleElementList<E> {
    private final List<E> list;

    public SingleElementList(E element) {
        this.list = Collections.singletonList(element);
    }

    public E get() {
        return list.get(0);
    }
}

这样我们就可以使用自定义的SingleElementList类:

List<Integer> list = new SingleElementList(111);
Integer res = list.get();

如何简洁的往list里面添加有限个数元素

按照常规写法,我们可能是先new ArrayList() ; 接着往list里面逐个add,这样也会导致add的行数较多;我们可以通过更简洁的方式:

List<Integer> list = Arrays.asList(1, 2, 3);
// >= jdk9 还可以通过List.of的方式
List<Integer> list1 = List.of(1, 2, 3);

注意:上述方式的list都不能remove 是不可变的,我们一般都过这种方式添加元素 基本上都是作为返回结果的 业务上已经确保不会remove了。

Arrays.asList原因简述: Arrays.asList返回的是Arrays类的内部类ArrayList ,这个ArrayList和我们常用的java.util.ArrayList是同名,但它是实现AbstractList的,且没有重写remove方法。
我们可以看看AbstractList的源码:

vs使用java控制台_List

List.of 也是类似的,就不再分析了。

map如何存取一个键值对

只有一个键值对的时候 也使用hashmap来存取,就会导致虽然只有一个entry ,但也必须 get(key) 或遍历entrySet来取值,那有没有什么办法 我们不用传入任何参数 也可以直接getKey呢?

我们可以使用java.util下的AbstractMap.SimpleEntry来解决这个问题:

AbstractMap.SimpleEntry<String,Integer> simpleEntry = new AbstractMap.SimpleEntry("csdn:孟秋与你", 18);
String key = simpleEntry.getKey();
Integer value = simpleEntry.getValue();

注: AbstractMap.SimpleEntry 是 Map.Entry的一个实现类,
如果我们想通过fastjson来转型,需要指定类型为Map.Entry , 并不支持AbstractMap.SimpleEntry (且fastjson版本需要>= 1.2.24)
代码示例如下:

LinkedHashMap<String, AbstractMap.SimpleEntry<String, String>> map = new LinkedHashMap<>();
String str = "{\"甲方合同\":{\"contact\":123},\"客户\":{\"customerName\":222}}";
// 将json字符串转成map
map = JSON.parseObject(str , LinkedHashMap.class);
  for (Map.Entry<String, AbstractMap.SimpleEntry<String, String>> entry : map.entrySet()) {
      		// 将大map的value转成Map.Entry
            Map.Entry<String, String> value = JSON.parseObject(String.valueOf(entry.getValue()),Map.Entry.class);
        }

如何简洁的往map里面添加有限个数元素

//与List.of()类似,jdk >= 9的时候 也有Map.of()方法
Map<String,Integer> map = Map.of("length",666,"age",18);

// jdk < 9  可以使用 谷歌guava包 (注意k v都不能为null,且键值对需要 <= 5)
Map immutableMap =  ImmutableMap.of();

如何批量删除mysql中某前缀开头的表

SELECT CONCAT('DROP TABLE IF EXISTS `', table_name, '`;')
FROM information_schema.tables

WHERE table_schema = 'your db name' AND table_name LIKE 'table_prefix%';

用上述语句 就可以得到所有以 table_prefix开头表的drop删除语句,我们可以在navicat中全选复制并手动执行

vs使用java控制台_封装_02

待续… 关注博主的同学可能比较清楚 博主的待续是真的有后续的 会动态更新