Version:0.9 Starthtml:0000000105 EndHTML:0000064633 StartFragment:0000000141 EndFragment:0000064597
本文梳理了在搭建开发框架时的一些考量及具体的处理方法。
尽量保证开发人员的核心关注点在业务逻辑。
尽量避免非业务问题影响开发进度。
规范的作用不是为了规范而规范,也不是对不按规范的人做出惩罚。是为了方便沟通。
可以从下面几个方面做出规范:
在代码规范化的前提下,基于代码生成工具(比如IDEA的EasyCode)构建一套完整的代码模板,基于代码模板快速的生成对应的代码。提高开发效率。
对于库表结构调整后对代码的影响,可以基于代码结构的调整来尽量减少对现有代码的影响。假设使用Mybatis作为持久化框架,有三个方案:
// 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();
}
一般项目中的库表设计基本都是按照关联表的方式进行设计的:
可以结合场景考虑,做一些结构简化和结构化。简化为对单表的操作,可以基于上面的模板来自动生成代码。
此种方式适用于主表和子表需要单独修改的场景。如果主表和子表是聚合关系,即子表依赖于主表存在,且需要一起调整,甚至子表不需要调整,实际可以简化此种关联关系。
因为此种关联关系涉及到了联表查询,联表查询是无法基于工具生成的。通过简化此种关系,可以基于工具来提高开发效率。
有些情况下,可能会将两个逻辑上分离的对象整合为一个对象来处理,简化操作负责度。例如订单中可能直接就包含购买的应用信息。此种情况实际是为了操作的便利性,同时兼顾数据库特性,将结构化的数据扁平化了。
数据扁平化本身问题不大,不过弱化了代码语义。在正常理解里,订单包含订单明细,订单明细中是商品信息。
可以通过下面的方法来解决上面提到的两个问题。
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包的方式来进行管理。
如果公共的代码修改频率比较高,可以基于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
如果公用的逻辑是一个独立的功能,后续可以作为服务对外提供服务。那可以考虑将这些代码独立为服务来对外提供服务。
本文从几个维度来考量在搭建一个项目框架时需要考虑的问题,以及对应的解决方案。