文章目录
- 如何封装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的源码:
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中全选复制并手动执行
待续… 关注博主的同学可能比较清楚 博主的待续是真的有后续的 会动态更新