開發框架搭建考量

本文梳理了在搭建開發框架時的一些考量及具體的處理方法。

框架搭建目標

儘量保證開發人員的核心關注點在業務邏輯。

儘量避免非業務問題影響開發進度。

整體代碼規範化

規範的作用不是爲了規範而規範,也不是對不按規範的人做出懲罰。是爲了方便溝通。

可以從下面幾個方面做出規範:

重複代碼自動化

在代碼規範化的前提下,基於代碼生成工具(比如 IDEA 的 EasyCode)構建一套完整的代碼模板,基於代碼模板快速的生成對應的代碼。提高開發效率。

對於庫表結構調整後對代碼的影響,可以基於代碼結構的調整來儘量減少對現有代碼的影響。假設使用 Mybatis 作爲持久化框架,有三個方案:

基於 MybatisProvider

// Mapper

@SelectProvider(type = IssueProvider.class, method = "list")

Page list(IssueVO issue);

// Provider

// Issue 是對應的 Bean,字段調整後,在 Issue 中添加對應的字段即可(可以生成,可以手動添加)

public String list(Issue issue) {

SQL sql = new SQL();

sql.SELECT("*");

sql.FROM("issue");

BeanMap beanMap = BeanMap.create(issue);

for (Object key : beanMap.keySet()) {

Object val = beanMap.get(key);

if (val != null) {

if (val instanceof String && ((String) val).contains("%")) {

sql.WHERE("" + FieldUtils.camelToLine(key + "") +""+"like #{"+ key +"}");

} else {

sql.WHERE("" + FieldUtils.camelToLine(key + "") +""+"=#{"+ key +"}");

}

}

}

return sql.toString();

}

基於 Mapper 接口繼承

複雜關係精簡化

一般項目中的庫表設計基本都是按照關聯表的方式進行設計的:

可以結合場景考慮,做一些結構簡化和結構化。簡化爲對單表的操作,可以基於上面的模板來自動生成代碼。

主表 + 子表的關聯關係

此種方式適用於主表和子表需要單獨修改的場景。如果主表和子表是聚合關係,即子表依賴於主表存在,且需要一起調整,甚至子表不需要調整,實際可以簡化此種關聯關係。

因爲此種關聯關係涉及到了聯表查詢,聯表查詢是無法基於工具生成的。通過簡化此種關係,可以基於工具來提高開發效率。

表中冗餘其它表的字段

有些情況下,可能會將兩個邏輯上分離的對象整合爲一個對象來處理,簡化操作負責度。例如訂單中可能直接就包含購買的應用信息。此種情況實際是爲了操作的便利性,同時兼顧數據庫特性,將結構化的數據扁平化了。

數據扁平化本身問題不大,不過弱化了代碼語義。在正常理解裏,訂單包含訂單明細,訂單明細中是商品信息。

可以通過下面的方法來解決上面提到的兩個問題。

解決方案

Mysql5.7 開始支持 Json。上述兩種情況,都可以基於 Json 的方式來處理。即數據庫字段可以使用 json 類型。

// 支持基於 json 的查詢,請自行 google

create table order

(

......

item_info_json json not null comment 'json',

);

public class Order {

private ItemInfo itemInfoJson;

}

在 Mybatis 層面,通過 TypeHandler 來處理 Json 與對象之間的自動轉換。

public interface OrderDao {

// 配置轉換 handler

@Results(id = "jsonResult", value = {

@Result(property = "itemInfoJson", column = "item_info_json", typeHandler = JsonTypeHandler.class)

})

@Select("select * from order where rec_id = #{recId}")

Order selectByPrimaryKey(@Param("recId") String recId);

@InsertProvider(type = OrderProvider.class, method = "insert")

void insert(Order model);

......

}

public class OrderProvider {

public String insert(Order order) {

SQL sql = new SQL();

sql.INSERT_INTO("order");

BeanMap beanMap = BeanMap.create(order);

for (Object key : beanMap.keySet()) {

Object val = beanMap.get(key);

if (val != null) {

if ((key + "").endsWith("Json")) { // 根據後綴判定是否需要轉換

sql.VALUES("" + FieldUtils.camelToLine(key + "") +"","#{"+ key +", typeHandler=com.iwhalecloud.common.mybatis.JsonTypeHandler}");

} else {

sql.VALUES("" + FieldUtils.camelToLine(key + "") +"","#{"+ key +"}");

}

}

}

return sql.toString();

}

轉換類處理邏輯:

public class JsonTypeHandler extends BaseTypeHandler {

private static final Gson gson = new Gson();

private Class clazz;

public JsonTypeHandler(Class clazz) {

if (clazz == null) {

throw new IllegalArgumentException("Type argument cannot be null");

} else {

this.clazz = clazz;

}

}

public void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {

ps.setString(i, this.toJson(parameter));

}

public T getNullableResult(ResultSet rs, String columnName) throws SQLException {

return this.toObject(rs.getString(columnName), this.clazz);

}

public T getNullableResult(ResultSet rs, int columnIndex) throws SQLException {

return this.toObject(rs.getString(columnIndex), this.clazz);

}

public T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {

return this.toObject(cs.getString(columnIndex), this.clazz);

}

private String toJson(T object) {

try {

return object instanceof String ? (String)object : gson.toJson(object);

} catch (Exception var3) {

throw new RuntimeException(var3);

}

}

private T toObject(String content, Class<?> clazz) {

if (content != null && !content.isEmpty()) {

try {

return clazz == String.class ? content : gson.fromJson(content, clazz);

} catch (Exception var4) {

throw new RuntimeException(var4);

}

} else {

return null;

}

}

}

公共代碼統一化

對於多個項目中使用到的代碼,需要根據場景進行公共化統一管理,避免公共代碼散落在各個服務中,難以維護。

根據場景的不同,可以有幾種管理方式:

基於公共 jar 包的管理

此方案是最常規的方案,如果幾個服務中使用到了公共的代碼,比如:一些工具類。這種情況下就可以將這些類獨立爲公共項目,通過 jar 包的方式來進行管理。

基於 git 源代碼的管理

如果公共的代碼修改頻率比較高,可以基於 git 的 subtree 來處理公共代碼的管理問題。

module 是取的別名

git remote add -f module ${上面的項目 git 地址}

將這個項目拉取到 src/module 目錄下

git subtree add --prefix=src/module module master --squash

git subtree pull --prefix=src/module module master

◆ 基於公共服務的管理

如果公用的邏輯是一個獨立的功能,後續可以作爲服務對外提供服務。那可以考慮將這些代碼獨立爲服務來對外提供服務。

 總結

本文從幾個維度來考量在搭建一個項目框架時需要考慮的問題,以及對應的解決方案。

來源:

https://www.toutiao.com/i6876823730878677516/

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/pbHkRrKwE873ZTrDDY_Dkw