彻底搞清mybaties plus实现原理( 二 )


MapperRegistry
Mapper注册器,其实就是加入到一个内部数组中 。
MybatisParameterHandler
DefaultParameterHandler
用于处理 SQL 中的参数占位符,为参数占位符设置值
上面是mybaties plus扩展mybaties的核心类 。
答疑时刻
mybatis plus扩展了mybatis的很多功能,添加了很多实用功能 。比如最主要的基于对象的增删改查(不需要写sql),基于雪花算法的ID生成器,字段自动填充功能,逻辑删除功能等 。
mybatis plus中“字段自动填充功能”实现源码,包括id自动生成的原理 。字段自动填充功能可以干什么?
在开发过程中表中经常会建创建时间,创建人,更新时间,更新人字段,正常自己维护这几个字段的时候,插入数据的时候需要自己给创建时间创建人赋值 。更新数据的时候需要自己给更新时间,更新人字段赋值 。
mybaties plus “字段自动填充功能”就是来解决这个问题的,可以在插入数据的时候指定要更新哪些字段,更新数据的时候指定更新哪些字段 。用起来还是很方便的 。
下面是使用例子:
public class User {// 注意!这里需要标记为填充字段@TableField(fill = FieldFill.INSERT)private LocalDateTime createTime;@TableField(fill = FieldFill.INSERT)private String createUserName;@TableField(fill = FieldFill.UPDATE)private LocalDateTime updateTime;@TableField(fill = FieldFill.UPDATE)private String updateUserName;}@Slf4j@Componentpublic class MyMetaObjectHandler implements MetaObjectHandler {@Overridepublic void insertFill(MetaObject metaObject) {log.info("start insert fill ....");this.strictInsertFill(metaObject, "createTime", () -> LocalDateTime.now(), LocalDateTime.class);this.strictInsertFill(metaObject, "createUserName", () -> "wanglining", String.class);}@Overridepublic void updateFill(MetaObject metaObject) {log.info("start update fill ....");this.strictUpdateFill(metaObject, "updateTime", () -> LocalDateTime.now(), LocalDateTime.class);this.strictInsertFill(metaObject, "updateUserName", () -> "wanglining", String.class)}}主要两步:

  1. 在对象上标记要填充的字段 。
  2. 定义字段填充handler 。
经过上面两个步骤,自动填充功能就完成了,当你调用mybaties plus的BaseMapper中提供的insert方法的时候,会给createTime,createUserName两个字段填充你在handler中指定的值 。相应的调用BaseMapper提供的update方法的时候,会给updateTime,updateUserName两个字段填充你在handler中指定的值 。
注意:一定得是调用BaseMapper中提供的方法,如果你自己再xml中定义sql语句是不会有作用的 。
【彻底搞清mybaties plus实现原理】实现原理
下面开始讲解它的实现原理 。
在上面提到mybaties plus定制了参数处理器(ParameterHandler),mybaties plus中的实现为MybatisParameterHandler,它的作用是“用于处理 SQL 中的参数占位符,为参数占位符设置值”, mybaties plus就是用在jdbc真正执行sql前,通过MetaObjectHandler给对象相应字段赋值 。进而mybaties把对象解析到了sql中进行执行 。
MybatisParameterHandler
private void process(Object parameter) {if (parameter != null) {TableInfo tableInfo = null;Object entity = parameter;if (parameter instanceof Map) {// 处理单参数使用注解标记的时候,尝试提取et来获取实体参数Map<?, ?> map = (Map<?, ?>) parameter;if (map.containsKey(Constants.ENTITY)) {Object et = map.get(Constants.ENTITY);if (et != null) {entity = et;tableInfo = TableInfoHelper.getTableInfo(entity.getClass());}}} else {tableInfo = TableInfoHelper.getTableInfo(parameter.getClass());}if (tableInfo != null) {//到这里就应该转换到实体参数对象了,因为填充和ID处理都是针对实体对象处理的,不用传递原参数对象下去.MetaObject metaObject = this.configuration.newMetaObject(entity);if (SqlCommandType.INSERT == this.sqlCommandType) {// 这个方法实现了给主键ID赋值的,根据你指定的策略生成id值,并赋值给主键ID 。populateKeys(tableInfo, metaObject, entity);// 这里会进一步调用上面定义的handler里面的方法insertFill(metaObject, tableInfo);} else {// 这里会进一步调用上面定义的handler里面的方法updateFill(metaObject, tableInfo);}}}}protected void insertFill(MetaObject metaObject, TableInfo tableInfo) {// 这里会先获取到你上面定义的MyMetaObjectHandler 。GlobalConfigUtils.getMetaObjectHandler(this.configuration).ifPresent(metaObjectHandler -> {// 判断openInsertFill是否为true,模式的就是返回true// isWithInsertFill判断是否为true,这个方法返回的就是TableInfo类中的withInsertFill属性// TableInfo就是dao层对象以及字段注解解析出来的一个对应数据库表的对象,其中withInsertFill属性// 就是根据类的字段上是否有@TableField(fill = FieldFill.INSERT)注解,如果有那么withInsertFill// 最终会是trueif (metaObjectHandler.openInsertFill() && tableInfo.isWithInsertFill()) {// 调用MyMetaObjectHandler的insertFill方法metaObjectHandler.insertFill(metaObject);}});}// 这个方法就不写注释了,跟上面一样 。protected void updateFill(MetaObject metaObject, TableInfo tableInfo) {GlobalConfigUtils.getMetaObjectHandler(this.configuration).ifPresent(metaObjectHandler -> {if (metaObjectHandler.openUpdateFill() && tableInfo.isWithUpdateFill()) {metaObjectHandler.updateFill(metaObject);}});}


推荐阅读