<返回更多

开发框架搭建考量

2020-11-16    
加入收藏

Version:0.9 Starthtml:0000000105 EndHTML:0000064633 StartFragment:0000000141 EndFragment:0000064597

 

本文梳理了在搭建开发框架时的一些考量及具体的处理方法。

 

框架搭建目标

尽量保证开发人员的核心关注点在业务逻辑。

尽量避免非业务问题影响开发进度。

整体代码规范化

规范的作用不是为了规范而规范,也不是对不按规范的人做出惩罚。是为了方便沟通

可以从下面几个方面做出规范:

重复代码自动化

在代码规范化的前提下,基于代码生成工具(比如IDEA的EasyCode)构建一套完整的代码模板,基于代码模板快速的生成对应的代码。提高开发效率。

对于库表结构调整后对代码的影响,可以基于代码结构的调整来尽量减少对现有代码的影响。假设使用Mybatis作为持久化框架,有三个方案:

基于MybatisProvider

// Mapper

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

Page<IssueVO> 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<T> extends BaseTypeHandler<T> {

private static final Gson gson = new Gson();

private Class<T> clazz;

public JsonTypeHandler(Class<T> 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

开发框架搭建考量

 

基于公共服务的管理

如果公用的逻辑是一个独立的功能,后续可以作为服务对外提供服务。那可以考虑将这些代码独立为服务来对外提供服务。

总结

本文从几个维度来考量在搭建一个项目框架时需要考虑的问题,以及对应的解决方案。

声明:本站部分内容来自互联网,如有版权侵犯或其他问题请与我们联系,我们将立即删除或处理。
▍相关推荐
更多资讯 >>>