Mybatis Plus自定义IService与BaseMapper
作者:互联网
Mybatis Plus自定义IService与BaseMapper
一、为什么研究起了这个东西
最近在公司独立负责了一个创新业务的java模块的服务研发,在搭建项目的时候,选择了Mybatis Plus 做数据库Dao层的工作。从工作以来,虽然不是第一次接触Mybatis Plus了,但是之前的接触都是在组里的前辈们搭建好框架后直接使用,这次是完完全全自己亲自提刀上阵了。想要自己去自定义IService 和 BaseMapper 实际上是来自一个问题:当一个entity类的某个字段被@TableLogic(逻辑删除)标识后 ,IService 中提供的 boolean updateById(T entity) 方法,无法再对改注释字段进行编辑。 hhh,因为这个原因,盲写的所有业务逻辑删除的接口都没有生效。“也许你会说,可以直接使用IService里提供的remove** 系列的接口,但是这些接口不能在逻辑删除的时候同时更新业务中的 [当前操作人姓名、操作人ID、操作时间…]等信息。” 总而言之就是因为懒的原因,并不想每一个数据库模型,都去对应的mapper里自定义SQL,还有一方面如果调整数据库字段还需要去做对应的调整,作为技术狗来说也觉得挨个调整不够炫酷。
二、实现步骤
2.1 自定义一个注入的方法类
新建一个类MyUpdateById.class,继承com.baomidou.mybatisplus.core.injector包下的抽象类AbstractMethod.class。
public class MyUpdateById extends AbstractMethod {
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
SqlMethod sqlMethod = SqlMethod.UPDATE_BY_ID;
//方法名称 需要与mapper中的名称保持一致
String method = "updateByIdWithLogicDelete";
final String additional = optlockVersion() + tableInfo.getLogicDeleteSql(true, true);
//因为是基于updateById(T entity)进行改造,这里只是把sqlSet里的logic更改为false,并未调整其他的
String sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(),
sqlSet(false, false, tableInfo, false, ENTITY, ENTITY_DOT),
tableInfo.getKeyColumn(), ENTITY_DOT + tableInfo.getKeyProperty(), additional);
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
return addUpdateMappedStatement(mapperClass, modelClass, method, sqlSource);
}
}
一些额外的说明,其实当你用IDEA打开AbstractMethod时,你会发现该抽象类的实现类,就是BaseMapper中提供的那些方法。如下图:
我在寻找updateById(T entity) 为什么在set 语句部分会自动移除@LogicDelete 注释的字段时,发现AbstractMethod的实现类DeleteById.class,习惯性的在给DeleteById.class 的injectMappedStatement方法打上断点后,去启动服务,就会看到,每一个entity都会先执行这个方法,去初始化该entity的执行SQL模板,如下图所示:
sql变量的数据如下(隐去了工作表的名称与一些其他信息):
<script>
UPDATE table_activity <set>
<if test="et['code'] != null">code=#{et.code},</if>
<if test="et['name'] != null">name=#{et.name},</if>
<if test="et['scopeType'] != null">scope_type=#{et.scopeType},</if>
<if test="et['scopeCode'] != null">scope_code=#{et.scopeCode},</if>
<if test="et['itemCode'] != null">item_code=#{et.itemCode},</if>
<if test="et['sector'] != null">sector=#{et.sector},</if>
<if test="et['helpFileUrls'] != null">help_file_urls=#{et.helpFileUrls},</if>
<if test="et['status'] != null">status=#{et.status},</if>
<if test="et['comment'] != null">comment=#{et.comment},</if>
<if test="et['updater'] != null">updater=#{et.updater},</if>
<if test="et['updateBy'] != null">update_by=#{et.updateBy},</if>
<if test="et['createdAt'] != null">create_at=#{et.createdAt},</if>
<if test="et['updatedAt'] != null">update_at=#{et.updatedAt},</if>
</set> WHERE id=#{et.id} <if test="et instanceof java.util.Map"> AND ${et.MP_OPTLOCK_VERSION_COLUMN}=#{et.MP_OPTLOCK_VERSION_ORIGINAL}</if> AND is_delete=0
</script>
当我们自定义的MyUpdateById配置成功后,将会得到与updateById十分相似,并且多了isDelete字段的set部分信息,详情参考如下信息
2.2 自定义一个SQL注入器
定义一个MySqlInjector.class继承com.baomidou.mybatisplus.core.injector包下的DefaultSqlInjector.class,重写获取方法列表接口,在该接口中添加上我们第一步配置的方法。
public class MySqlInjector extends DefaultSqlInjector {
@Override
public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
//拿到父类的getMethodList方法
List<AbstractMethod> methodList = super.getMethodList(mapperClass);
//自定义包含逻辑删除的更新
methodList.add(new MyUpdateById());
return methodList;
}
}
2.3 将自定义的SQL注入器添加到配置中
@Bean
public MySqlInjector myLogicSqlInjector() {
return new MySqlInjector();
}
2.4 自定义一个通用的BaseMapper接口,添加上updateByIdWithLogicDelete(@Param(Constants.ENTITY)T entity) 方法
public interface MyBaseMapper<T> extends BaseMapper<T> {
int updateByIdWithLogicDelete(@Param(Constants.ENTITY) T entity);
}
其实到这里,我们已经可以独立使用mapper模块的功能了
可以直接通过注入的baseMapper,调用我们定义的updateByIdWithLogicDelete方法。
但其实这样做的话,我们还是需要在每个service中自己定义一个方法,再通过mapper来调用该方法,既然是如此通用的方法,也许70%的业务模型都会涉及到逻辑删除时并更新保存一些业务信息,那干脆就直接用自己的IMyService来替代MyBatis Plus 提供给我们IService,在包含IService的基础上,扩充自己项目里通用的一写方法,不就可以简单的实现了吗~
2.5 定义自定义的IMyService接口,提供一些自定义的通用方法
public interface IMyBaseService<T> extends IService<T> {
/**
* 对updateById的升级,包含逻辑删除字段的更新
* @param entity
* @return
*/
boolean updateByIdWithLogicDelete(T entity);
}
2.6 添加IMyService的实现
@SuppressWarnings("unchecked")
public class MyBaseServiceImpl <M extends MyBaseMapper<T>, T> extends ServiceImpl<M, T> implements IMyBaseService<T>{
@Override
public boolean updateByIdWithLogicDelete(T entity) {
return retBool(baseMapper.updateByIdWithLogicDelete(entity));
}
}
2.7 剩下的只有用起来!
1.在service中继承IMyService
public interface IDictService extends IMyBaseService<Dict>
2.在service的实现中继承IMyBaseService的实现
@Service
public class DictServiceImpl extends MyBaseServiceImpl<DictMapper, Dict> implements IDictService {...}
3在mapper中继承MyBaseMapper替代IBaseMapper
public interface DictMapper extends MyBaseMapper<Dict> {
}
总结
最近的工作好无聊,好无力啊,什么时候才能拥有正规化的研发流程。当你速度太快,开完评审的东西都已经通过大家的评审了,当你接口研发好了以后,你就会接到产品的需求变更、领导侧另一服务对接的流程变更… 还要处理模糊没有边界的数据,最近的工作好烦。打工不易,为了这个项目搞上线,可真难,还没有同事陪我一起加班…
标签:IService,自定义,class,entity,Mybatis,et,public 来源: https://blog.csdn.net/zhangsuhua0702/article/details/121658448