引言
很多人在写代码刚开始可能会和我一样,不会去考虑重构和复用,因为一个需求给到你,也有相应的计划时间,我们更多的考虑是如何最快的实现业务,产品在催、PM在催、客户也在催。不过渐渐的会在完成一个业务时,用更优的方案来进行优化。因为你会发现,自己写的代码越简洁,自己越舒服。(PS:虽然网络一直在传言代码写的好,离职少不了,哈哈)
接下来我用一些案例,来阐明一些简单的优化方案,仅供大家参考,大神勿喷。
案例
1. SQL复用
背景描述:
虽然现在大家都已经用了Mybaits,或者是Mybaits-plus,再加上sql生成器,很多时候写SQL非常方便,只需要调用生成器生成出的方法,.insert()
/ .save()
,就可以实现新增或修改操作,但还是有些情况避免不了的使用原生SQL,例如JdbcTemplate。此案例在调用第三方接口时,用于存储大表数据。
当我们遇到需要新增一个表时,当字段太多,往往要写一大段SQL(因为正常写INERT
假设不加列名,会导致新增列时出现异常),后期如果维护也非常麻烦,假设再加一个表,又是一段SQL,因此我选择抽象出一个生成INSERT
的SQL方法。
思路:
简单的抽象工具方法,可以让一些验证或其他重复性工作的维护成本大大降低,增加代码可读性。
第一步是发现这个问题,你的逻辑中可能存在大量的重复,或重复复杂逻辑难以维护,这时需要考虑能否抽象成工具方法。以本案例为例,即使使用生成器,接口返回字段与实际存储字段不符,那么就导致我一个实体类要写几十个set(PS:这里也可以抽象构造方法,将Map传入,为另一种思路,但相比较差,因为我有多个表多个接口,可能要抽象N个构造方法),因此我需要一个生成INSERT
的SQL方法。
第二步要明确你需要什么,有哪些是可变参数。以本案例为例,我需要的是他拼接出的最终SQL,拿到之后直接执行即可。思考可变参数,每个表的表名列名不同,实际数据也不同,接口返回的实际Key也不同,因此我们需要:列名,实际Map的key,表名,数据。
第三步就可以抽象方法进行测试了。以本案例为例,拼接SQL时选择将头先行拼接INERT INTO
。然后将Sql分为两部分进行For循环拼接,一部分拼接列名,一部分拼接插入值。插入值的地方有两种额外情况,一种是默认的可用不可用状态,例如valid_status
,默认的插入时间input_time
,这种类型的数据我会在数据库中设置默认值,因为实际返回Key并没有。还有一种是空值,拼接时不能直接用map.get(key)
,应该拼接上NULL,
。最终对拼接完成的sql去掉最后一位,
进行合并即可。(PS:示例如下,实际情况和场景可自由发挥)
demo:
/**
* @description: 拼接新增Sql
* @author: zeus
* @createDate: 2019/8/2 10:28
* @param: data: 数据源
* colKey:配置列,key代表data所对应的实际key,value代表插入表的列名
* tableName:需要插入的表名
* @version: 1.0
*/
public static String concatInsertSql(Map<String, Object> data, Map<String, String> colKey, String tableName){
StringBuilder sqlHeader = new StringBuilder(" INSERT INTO ");// 拼接SQL
sqlHeader.append(tableName).append(" (");
StringBuilder sql = new StringBuilder(" (");
for(String key : colKey.keySet()) {
sqlHeader.append(colKey.get(key)).append(",");
if(data.get(key) != null){
String str = getConcatStr(data.get(key).toString());
sql.append("'").append(str).append("',");
}else{
sql.append("NULL,");
}
}
sqlHeader.deleteCharAt(sqlHeader.length()-1).append(") VALUES ");
sql.deleteCharAt(sql.length()-1).append(")");
sqlHeader.append(sql);
return sqlHeader.toString();
}