本本分分做人,踏踏实实做事!!!
前言
- 过去一年,xx 技术方面有哪些进展?(本年度最新最前沿的技术或者是在商业化落地有重大进展的技术)
- 在生态方面有哪些变化?
- 在落地实践方面解决了哪些过去的问题?
- 未来展望
场景
-
客户需要实现敏感数据加密,至于哪些是敏感需要根据客户公司每年政策来定。
-
客户要求统一对每张表进行统一管理: 创建时间、修改时间、创建人、修改人、字段排序规则等
-
为应对不同客户,我们需要提高平台可配性前提外,还需要进行客户数据严格隔离,隔离的同时又要考虑到数据共享。
-
为了提高数据响应性能,大多都是采用的读写分离或者分库分表。
-
上面种种的场景就是本年度我自己经历的需求设计与开发, 单独将他们合并到一起主要是因为他们最终的解决方案都绕不过
Mybatis
这个基础组件。接下来我就Mybatis
生态谈谈在实践中的应用以及对它的未来展望
具体案例
数据加密
-
数据加密方案有很多
Controller
层返回数据前对数据字段进行加密- 前端通过页面手动进行数据加密 (伪加密)
- 实体映射的时候统一加密 (类似
Controller
加密) JDBC
映射的时候加密Mybatis
拦截加密 (类似字段映射加密)- 数据源头加密入库,查询则直接查询即可完成加密。
-
首先谈谈
Controller
层加密太片面了,换句话说需要我们手动的每个接口针对加密字段进行加密,工程量大而且容易有漏网之鱼。 -
其次就是前端自己加密,这种稍微懂技术的破解它都是分分钟的事情,或者说这种根本就不叫加密,而且前端也是手动进行处理,同样存在工程量大和遗漏的场景。
-
我比较青睐的是通过实体映射来处理字段加密,在实体中重写
get/set
相当于控制了写入和查询两条路,想怎么控制就怎么控制,但是如果是这样的相当于加密的字段不能或者尽量不要参与到业务逻辑,否则将会无法准备的获取到。 -
JDBC
映射是最底层的实现方式,这种方式能够真正做到数据的加密,因为它就是在从数据库获取到数据之后立马进行加密,不存在手动处理的问题,它只需要配置好,根据字段检测是否应该进行加密。如果我们想使用原始字段内容,我们可以借助ThreadLocal
进行控制加密的开关。 -
mybatis
拦截加密其实和JDBC
映射加密是一个作用,都不需要我们手动的去改,只需要在通用的地方拦截,凡事使用到该字段的都会被拦截加密、解密。 -
数据源头加密意思就是我在入库的时候就存储一个加密的内容进去,这样不管是谁都看不到,这种做法更加的暴力、直接。
通用字段维护
- 有些属性属于通用的,比如一条数据的创建时间、更新时间、创建人、修改人以及排序属性等等。这些东西放在各自功能里维护显然显得比较臃肿,这个时候我想到了
mybatis-plus
提供的MetaObjectHandler
功能。这个接口官方的注释明确的说明就是为了解决通用字段维护的。
java
@Component
public class DateMetaObjectHandler implements MetaObjectHandler {
@Autowired
FillDataHandler fillDataHandler;
@Override
public void insertFill(MetaObject metaObject) {
List<FillDataClassMapModel> list = fillDataHandler.insertPerial();
if (CollectionUtils.isNotEmpty(list)) {
for (FillDataClassMapModel model : list) {
this.strictInsertFill(metaObject, model.getName(), model.getCt(), model.getE());
}
}
}
@Override
public void updateFill(MetaObject metaObject) {
List<FillDataClassMapModel> list = fillDataHandler.updatePerial();
if (CollectionUtils.isNotEmpty(list)) {
for (FillDataClassMapModel model : list) {
this.strictUpdateFill(metaObject, model.getName(), model.getCt(), model.getE());
}
}
}
}
- 这里为了兼容到其他的类似处理,我在这里借助
Spring
处理器进行兼容处理
java
@Component
@Slf4j
public class DataMetaBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if ("sqlSessionFactory".equals(beanName) || bean instanceof SqlSessionFactory) {
try {
Configuration configuration = ((SqlSessionFactory) bean).getConfiguration();
//configuration.getTypeHandlerRegistry().register(EntryTypeHandler.class);
GlobalConfig globalConfig = GlobalConfigUtils.getGlobalConfig(configuration);
globalConfig.setMetaObjectHandler(SpringUtils.getBean(DateMetaObjectHandler.class));
} catch (Exception e) {
log.warn("统一处理sqlsessionfacy出错了...");
}
}
return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
}
}
- 有了这个功能我们就可以在实体上添加对应的注解,这样就可以自动维护了,甚至我们可以将这些字段抽离成一个父类,这样对代码的侵入降到最低。
SAAS 改造
- Saas 是一种趋势,软件即服务 (SaaS) 指一种基于云技术的软件交付模式,SaaS 就是平台化,用户在平台上进行自己的定制功能,通过不同的权限拥有不同程度的平台使用。
- 实现
SaaS
的原理和通用字段中创建人一样,每一条数据都存在这么一个字段用来表示所属租户。如果是老系统我们需要写个脚本进行批量添加租户字段。至于实现我还是选择使用Mybatis-plus
提供的TenantLineInnerInterceptor
java
public class CustomTenantLineInnerInterceptor extends TenantLineInnerInterceptor {
public CustomTenantLineInnerInterceptor(TenantLineHandler tenantLineHandler) {
super(tenantLineHandler);
}
@Override
public Expression buildTableExpression(Table table, Expression where, String whereSegment) {
Expression tenantId = super.getTenantLineHandler().getTenantId();
if (null == tenantId) {
return null;
}
SaasPlusControlHandler saasPlusControlHandler = DynicApplicationUtils.getApplicationContext().getBean(SaasPlusControlHandler.class);
boolean disinterPlus = saasPlusControlHandler.getDisinterPlus();
if (disinterPlus) {
//如果手动设置了拒绝租户拦截,则返回null
return null;
}
return super.buildTableExpression(table, where, whereSegment);
}
}
- 具体的细节我们还需要进行不同程度的改造,
mybatis
只是帮我们实现了切换的功能,但是部分场景我们是不需要切换的,比如登录不可能每个租户对应一个登录的,所以登录部分的逻辑需要共享,这个时候就需要我们进行特殊处理,具体细节我们以后再聊主要是通过ThreadLocal
进行控制开关。
MATE 数据分库分表
mybatis
里也提供了分表的操作,我们只需要重写切换策略即可。
java
public class DynamicDataSource extends AbstractRoutingDataSource {
private Logger logger = LoggerFactory.getLogger(DynamicDataSource.class);
@Override
protected Object determineCurrentLookupKey() {
logger.info("切换数据源:{}","db" + DBContextHolder.getDBKey());
return "db" + DBContextHolder.getDBKey();
}
}
- 我们在具体如何制定切换策略时,可以根据不同的场景进行定制开发,或者将常用的策略进行开发好有下游自己选择切换。
- 当然
mybatis-plus
升级版中提供了更多丰富的功能供我们使用。
java
@Component
public class MyShardingStrategy extends RandomShardingStrategy {
/**
* 决定切换数据源 key {@link ShardingDatasource}
*
* @param group 动态数据库组
* @param invocation {@link Invocation}
* @param sqlCommandType {@link SqlCommandType}
*/
@Override
public void determineDatasourceKey(String group, Invocation invocation, SqlCommandType sqlCommandType) {
// 数据源组 group 自定义选择即可, keys 为数据源组内主从多节点,可随机选择或者自己控制
this.changeDatabaseKey(group, sqlCommandType, keys -> chooseKey(keys, invocation));
}
}
生态未来展望
- 我们常规开发中是离不开数据库的,而目前比较主流的数据库框架非
mybatis
莫属,基本上我们跟数据相关的业务mybatis
或多或少的帮我们实现了,我们站在巨人的肩膀上已经很高了。 - 但是我还是希望
Mybatis
生态能够继续步步高升,开发出类似liquibase
的数据库增量、全量维护功能。目前mybatis
提供的家在sql
文件虽然也可以实现,但是并没有想liquibase
那么具有完备性。
数据冷热备份
- 在日常开发中基本满足需求了,但是对于上线的项目或者客户量很大的项目,数据要不了多久就会被撑满,这个时候我们会提出历史数据思想,就是按照时间将数据分为冷热数据,mybatis 如何能够开发出我们只需要指定时间范围就可以自动利用分库分表策略进行数据隔离。
- 当我们需要查询历史数据时,mybatis 再自动帮忙恢复成热数据,至于何时恢复的策略右开发者自己指定。
- 如果查询数据横跨冷热数据,mybatis 也能自动帮忙将冷数据恢复过来。
- 如果具备这个功能,我相信 mybatis 生态将会更加的健全。
数据库迁移
- 不同的项目使用的数据库不一样,不同的数据库迁移转换也是很麻烦的一件事,如果 mybatis 提供这样一个功能,我们就可以无缝切换数据库了。
总结
- 框架的另一个特性就是易于扩展,在使用
mybatis
的过程中最大的好处就是我可以灵活的扩展其提供的功能。在mybatis
生态不断完善的前提下,我也将不断的提高对mybatis
生态的学习。
放松一刻
I begin with an idea and then it becomes something else. --- Pablo Picasso