iis服务器助手广告广告
返回顶部
首页 > 资讯 > 精选 >Mybatis Plus框架源码分析
  • 462
分享到

Mybatis Plus框架源码分析

2023-07-05 07:07:24 462人浏览 八月长安
摘要

这篇文章主要介绍了mybatis Plus框架源码分析的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Mybatis Plus框架源码分析文章都会有所收获,下面我们一起来看看吧。基础设计Bas

这篇文章主要介绍了mybatis Plus框架源码分析的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Mybatis Plus框架源码分析文章都会有所收获,下面我们一起来看看吧。

基础设计

BaseEntity

对于数据库中表中的公共字段我们可以抽取出来做成基类继承。避免表映射的数据库实体类字段太过繁杂。
例如常用的创建时间、创建者、更新时间、更新者、逻辑删除字段。

@EqualsAndHashCode(callSuper = true)@Datapublic class BaseEntity<T extends Model<?>> extends Model<T> {    @apiModelProperty("创建者ID")    @TableField(value = "creator_id", fill = FieldFill.INSERT)    private Long creatorId;    @ApiModelProperty("创建时间")    @TableField(value = "create_time", fill = FieldFill.INSERT)    private Date createTime;    @ApiModelProperty("更新者ID")    @TableField(value = "updater_id", fill = FieldFill.UPDATE, updateStrategy = FieldStrategy.NOT_NULL)    private Long updaterId;    @ApiModelProperty("更新时间")    @TableField(value = "update_time", fill = FieldFill.UPDATE)    private Date updateTime;        @ApiModelProperty("删除标志(0代表存在 1代表删除)")    @TableField("deleted")    @TableLogic    private Boolean deleted;}

通过继承了基类,实体类看起来就简洁了许多。

@Getter@Setter@TableName("sys_notice")@ApiModel(value = "SysNoticeEntity对象", description = "通知公告表")public class SysNoticeEntity extends BaseEntity<SysNoticeEntity> {    private static final long serialVersionUID = 1L;    @ApiModelProperty("公告ID")    @TableId(value = "notice_id", type = IdType.AUTO)    private Integer noticeId;    @ApiModelProperty("公告标题")    @TableField("notice_title")    private String noticeTitle;    @ApiModelProperty("公告类型(1通知 2公告)")    @TableField("notice_type")    private Integer noticeType;    @ApiModelProperty("公告内容")    @TableField("notice_content")    private String noticeContent;    @ApiModelProperty("公告状态(1正常 0关闭)")    @TableField("`status`")    private Integer status;    @ApiModelProperty("备注")    @TableField("remark")    private String remark;    @Override    public Serializable pkVal() {        return this.noticeId;    }}

既然抽取出了公共字段,我们可以更进一步将这些公共字段进行自动填值处理。
Mybatis Plus提供了字段自动填充的插件

自动填充字段

@Component@Slf4jpublic class CustomMetaObjectHandler implements MetaObjectHandler {    public static final String CREATE_TIME_FIELD = "createTime";    public static final String CREATOR_ID_FIELD = "creatorId";    public static final String UPDATE_TIME_FIELD = "updateTime";    public static final String UPDATER_ID_FIELD = "updaterId";    @Override    public void insertFill(MetaObject metaObject) {        if (metaObject.hasSetter(CREATE_TIME_FIELD)) {            this.setFieldValByName(CREATE_TIME_FIELD, new Date(), metaObject);        }        if (metaObject.hasSetter(CREATOR_ID_FIELD)) {            this.strictInsertFill(metaObject, CREATOR_ID_FIELD, Long.class, getUserIdSafely());        }    }    @Override    public void updateFill(MetaObject metaObject) {        if (metaObject.hasSetter(UPDATE_TIME_FIELD)) {            this.setFieldValByName(UPDATE_TIME_FIELD, new Date(), metaObject);        }        if (metaObject.hasSetter(UPDATER_ID_FIELD)) {            this.strictUpdateFill(metaObject, UPDATER_ID_FIELD, Long.class, getUserIdSafely());        }    }    public Long getUserIdSafely() {        Long userId = null;        try {            LoginUser loginUser = AuthenticationUtils.getLoginUser();            userId = loginUser.getUserId();        } catch (Exception e) {            log.info("can not find user in current thread.");        }        return userId;    }}

使用自定义填充值时,需要在生成实体的时候加上配置。FieldFill.INSERTFieldFill.INSERT_UPDATE

private void entityConfig(StrategyConfig.Builder builder) {    Entity.Builder entityBuilder = builder.entityBuilder();    entityBuilder        .enableLombok()        .addTableFills(new Column("create_time", FieldFill.INSERT))        .addTableFills(new Column("creator_id", FieldFill.INSERT))        .addTableFills(new Property("updateTime", FieldFill.INSERT_UPDATE))        .addTableFills(new Property("updaterId", FieldFill.INSERT_UPDATE))        // ID strategy AUTO, NONE, INPUT, ASSIGN_ID, ASSIGN_UUID;        .idType(IdType.AUTO)        .fORMatFileName("%sEntity");    if (isExtendsFromBaseEntity) {        entityBuilder            .superClass(BaseEntity.class)            .addSuperEntityColumns("creator_id", "create_time", "creator_name", "updater_id", "update_time",                "updater_name", "deleted");    }    entityBuilder.build();}

逻辑删除

数据库一般不进行真实删除操作。但是如果让我们手工处理这些逻辑删除的话,也是非常麻烦。Mybatis Plus有提供这样的插件。仅需要在EntityConfig中设置逻辑删除的字段是哪个即可。

entityBuilder    // deleted的字段设置成tinyint  长度为1    .logicDeleteColumnName("deleted")    .formatFileName("%sEntity");

代码生成类

Mybatis plus支持生成entity,mapper,service,controller这四层类。 但是笔者认为生成类的时候还是不要直接覆盖原本的类比较好。
我将生成的类,固定放在一个目录让使用者自己copy类到指定的目录。
以下是我自己封装的CodeGenerator的代码片段。
需要填入的字段主要是:

  • 作者名

  • 包名

  • 表名

  • 是否需要继承基类(因为不是所有表都需要继承基类)

public static void main(String[] args) {    // 默认读取application-dev yml中的master数据库配置    JSON ymljson = JSONUtil.parse(new Yaml().load(ResourceUtil.getStream("application-dev.yml")));    CodeGenerator generator = CodeGenerator.builder()        .databaseUrl(JSONUtil.getByPath(ymlJson, URL_PATH).toString())        .username(JSONUtil.getByPath(ymlJson, USERNAME_PATH).toString())        .password(JSONUtil.getByPath(ymlJson, PASSWord_PATH).toString())        .author("valarchie")        //生成的类 放在orm子模块下的/target/generated-code目录底下        .module("/agileboot-orm/target/generated-code")        .parentPackage("com.agileboot")        .tableName("sys_config")        // 决定是否继承基类        .isExtendsFromBaseEntity(true)        .build();    generator.generateCode();}

查询操作

Query基类

系统内的查询大部分有共用的逻辑。比如时间范围的查询、排序。我们可以抽取这部分逻辑放在基类。 然后把具体查询条件的构造,放到子类去实现。

AbstractQuery

@Datapublic abstract class AbstractQuery<T> {    protected String orderByColumn;    protected String isAsc;    @JsonFormat(shape = Shape.STRING, pattern = "yyyy-MM-dd")    private Date beginTime;    @JsonFormat(shape = Shape.STRING, pattern = "yyyy-MM-dd")    private Date endTime;    private static final String ASC = "ascending";    private static final String DESC = "descending";        public abstract QueryWrapper<T> toQueryWrapper();    public void addSortCondition(QueryWrapper<T> queryWrapper) {        if(queryWrapper != null) {            boolean sortDirection = convertSortDirection();            queryWrapper.orderBy(StrUtil.isNotBlank(orderByColumn), sortDirection,                StrUtil.toUnderlineCase(orderByColumn));        }    }    public void addTimeCondition(QueryWrapper<T> queryWrapper, String fieldName) {        if (queryWrapper != null) {            queryWrapper                .ge(beginTime != null, fieldName, DatePickUtil.getBeginOfTheDay(beginTime))                .le(endTime != null, fieldName, DatePickUtil.getEndOfTheDay(endTime));        }    }    public boolean convertSortDirection() {        boolean orderDirection = true;        if (StrUtil.isNotEmpty(isAsc)) {            if (ASC.equals(isAsc)) {                orderDirection = true;            } else if (DESC.equals(isAsc)) {                orderDirection = false;            }        }        return orderDirection;    }}

PageQuery

分页是非常常见的查询条件,我们可以基于AbstractQuery再做一层封装。

@Datapublic abstract class AbstractPageQuery<T> extends AbstractQuery<T> {    public static final int MAX_PAGE_NUM = 200;    public static final int MAX_PAGE_SIZE = 500;    @Max(MAX_PAGE_NUM)    protected Integer pageNum = 1;    @Max(MAX_PAGE_SIZE)    protected Integer pageSize = 10;    public Page<T> toPage() {        return new Page<>(pageNum, pageSize);    }}

普通Query

比如我们有个菜单查询列表,我们可以新建一个MenuQuery继承AbstractQuery。然后实现 toQueryWrapper方法去构造查询条件。

@Datapublic class MenuQuery extends AbstractQuery<SysMenuEntity> {    private String menuName;    private Boolean isVisible;    private Integer status;    @Override    public QueryWrapper<SysMenuEntity> toQueryWrapper() {        QueryWrapper<SysMenuEntity> queryWrapper = new QueryWrapper<SysMenuEntity>()            .like(StrUtil.isNotEmpty(menuName), "menu_name", menuName)            .eq(isVisible != null, "is_visible", isVisible)            .eq(status != null, "status", status);        queryWrapper.orderBy(true, true, Arrays.asList("parent_id", "order_num"));        return queryWrapper;    }}

如果有另外一个不同的菜单查询列表,查询的参数一样,但是查询条件的构造不一样。我们可以新建一个DifferentMenuQuery类继承MenuQuery类,再覆写toQueryWrapper方法即可。

Lambda Query

如果在项目中的查询明确是单表操作的话,我们可以使用LambdaQuery来构造查询。

LambdaQueryWrapper<SysMenuEntity> menuQuery = Wrappers.lambdaQuery();menuQuery.select(SysMenuEntity::getMenuId);List<SysMenuEntity> menuList = menuService.list(menuQuery);

复杂多表查询

Mybatis Plus支持@Select注解,遇到简单的多表join查询的话,我们可以直接在代码中写sql语句。

以下是Mapper中的实现。${ew.customSqlSegment} 会渲染出QueryWrapper类生成的查询条件。

@Select("SELECT u.*, d.dept_name, d.leader_name "    + "FROM sys_user u "    + " LEFT JOIN sys_dept d ON u.dept_id = d.dept_id "    + "${ew.customSqlSegment}")Page<SearchUserDO> getUserList(Page<SearchUserDO> page,    @Param(Constants.WRAPPER) Wrapper<SearchUserDO> queryWrapper);

Service层中的实现。

@Overridepublic Page<SearchUserDO> getUserList(AbstractPageQuery<SearchUserDO> query) {    return baseMapper.getUserList(query.toPage(), query.toQueryWrapper());}

报表型查询

如果遇到复杂的报表型查询,利用@Select注解的话,可能SQL看起来还是非常的复杂。此时推荐使用XML的形式。

Mybatis Plus框架源码分析

保存操作

模型利用JPA保存

Mybatis Plus支持activeRecord特性,我们可以直接在Entity上执行save\update\delete等操作。框架会自动帮我们落库。 activeRecord需要在EntityConfig配置。

        entityBuilder            // operate entity like JPA.            .enableActiveRecord()            // deleted的字段设置成tinyint  长度为1            // ID strategy AUTO, NONE, INPUT, ASSIGN_ID, ASSIGN_UUID;            .idType(IdType.AUTO)            .formatFileName("%sEntity");

因为Entity都是生成的,我们不方便将业务逻辑直接放在Entity中。这样会和数据库实体过于耦合。
推荐新建一个模型类继承XxxxEntity,然后将逻辑填充在模型类中。

public class DeptModel extends SysDeptEntity {    private ISysDeptService deptService;    public DeptModel(ISysDeptService deptService) {        this.deptService = deptService;    }    public DeptModel(SysDeptEntity entity, ISysDeptService deptService) {        if (entity != null) {            // 如果大数据量的话  可以用MapStruct优化            BeanUtil.copyProperties(entity, this);        }        this.deptService = deptService;    }    public void loadAddCommand(ADDDeptCommand addCommand) {        this.setParentId(addCommand.getParentId());        this.setAncestors(addCommand.getAncestors());        this.setDeptName(addCommand.getDeptName());        this.setOrderNum(addCommand.getOrderNum());        this.setLeaderName(addCommand.getLeaderName());        this.setPhone(addCommand.getPhone());        this.setEmail(addCommand.getEmail());    }    public void checkDeptNameUnique() {        if (deptService.isDeptNameDuplicated(getDeptName(), getDeptId(), getParentId())) {            throw new ApiException(ErrorCode.Business.DEPT_NAME_IS_NOT_UNIQUE, getDeptName());        }    }    public void generateAncestors() {        if (getParentId() == 0) {            setAncestors(getParentId().toString());            return;        }        SysDeptEntity parentDept = deptService.getById(getParentId());        if (parentDept == null || StatusEnum.DISABLE.equals(            BasicEnumUtil.fromValue(StatusEnum.class, parentDept.getStatus()))) {            throw new ApiException(ErrorCode.Business.DEPT_PARENT_DEPT_NO_EXIST_OR_DISABLED);        }        setAncestors(parentDept.getAncestors() + "," + getParentId());    }}

在应用层我们就可以直接调用模型类来完成逻辑操作。整个代码的语义性非常强。

public void addDept(AddDeptCommand addCommand) {    DeptModel deptModel = deptModelFactory.create();    deptModel.loadAddCommand(addCommand);    deptModel.checkDeptNameUnique();    deptModel.generateAncestors();    deptModel.insert();}

批量保存数据

以上是单条数据的落库操作,那么多条数据循环去insert的话,显然不是一个明智之举。
Mybatis Plus提供了批量落库操作。

private boolean saveMenus() {    List<SysRoleMenuEntity> list = new ArrayList<>();    if (getMenuIds() != null) {        for (Long menuId : getMenuIds()) {            SysRoleMenuEntity rm = new SysRoleMenuEntity();            rm.setRoleId(getRoleId());            rm.setMenuId(menuId);            list.add(rm);        }        return roleMenuService.saveBatch(list);    }    return false;}

按条件更新数据

JPA的方式有一个弊端就是需要先拿到数据实体类,才能调用save等操作。

还有一种情况,我们需要按照某些条件去更新数据,而不想先一条条获取数据再Save。

此时可以使用LambdaUpdate类。

LambdaUpdateWrapper<SysUserEntity> updateWrapper = new LambdaUpdateWrapper<>();updateWrapper.set(SysUserEntity::getRoleId, null).eq(SysUserEntity::getUserId, userId);userService.update(updateWrapper);

扩展

阻止全表操作

Mybatis Plus提供了安全方面的插件,比如阻止全标更新删除的插件。

仅需要声明MybatisPlusInterceptor Bean,依次添加拦截插件即可。

@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();    interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());    return interceptor;}

动态数据源

Mybatis Plus提供@DS注解去动态选择从库还是主库来执行SQL.

@DS("slave")@PreAuthorize("@permission.has('system:notice:list')")@GetMapping("/listFromSlave")public ResponseDTO<PageDTO<NoticeDTO>> listFromSlave(NoticeQuery query) {    PageDTO<NoticeDTO> pageDTO = noticeApplicationService.getNoticeList(query);    return ResponseDTO.ok(pageDTO);}

比如打上了@DS("slave")的接口,就会去找slave这个从库进行操作。

多租户

    @Bean    public MybatisPlusInterceptor mybatisPlusInterceptor() {        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();        interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(new TenantLineHandler() {            @Override            public Expression getTenantId() {                // 获取租户ID 实际应该从用户信息中获取                return new LongValue(1);            }            // 这是 default 方法,默认返回 false 表示所有表都需要拼多租户条件            @Override            public boolean ignoreTable(String tableName) {                return !"sys_user".equalsIgnoreCase(tableName);            }        }));        // 如果用了分页插件注意先 add TenantLineInnerInterceptor 再 add PaginationInnerInterceptor        // 用了分页插件必须设置 MybatisConfiguration#useDeprecatedExecutor = false//        interceptor.addInnerInterceptor(new PaginationInnerInterceptor());        return interceptor;    }

关于“Mybatis Plus框架源码分析”这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对“Mybatis Plus框架源码分析”知识都有一定的了解,大家如果还想学习更多知识,欢迎关注编程网精选频道。

--结束END--

本文标题: Mybatis Plus框架源码分析

本文链接: https://www.lsjlt.com/news/350436.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Mybatis Plus框架源码分析
    这篇文章主要介绍了Mybatis Plus框架源码分析的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Mybatis Plus框架源码分析文章都会有所收获,下面我们一起来看看吧。基础设计Bas...
    99+
    2023-07-05
  • mybatis-plus查询源码的示例分析
    这篇文章主要介绍mybatis-plus查询源码的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!配置详情pom.xmldependency>     &...
    99+
    2023-06-29
  • Java Log框架源码分析
    这篇文章主要讲解了“Java Log框架源码分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java Log框架源码分析”吧!Log4J、Log4J2和LogBack的...
    99+
    2023-07-05
  • 微前端框架qiankun源码分析
    本文小编为大家详细介绍“微前端框架qiankun源码分析”,内容详细,步骤清晰,细节处理妥当,希望这篇“微前端框架qiankun源码分析”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。一、single-spa简介要...
    99+
    2023-07-05
  • Mybatis-plus框架使用配置
    Mybatis-plus框架使用配置 MyBatis-Plus(简称 MP)是一个基于 MyBatis 的增强工具,它对 Mybatis 的基础功能进行了增强,但未做任何改变。使得我们可以可以在 My...
    99+
    2023-09-08
    java spring boot mybatis
  • 若依框架整合mybatis-plus
    在使用若依(RuoYi-vue)时,发现若依用的是mybatis而不是mybatis-plus,所以为了保留原有代码生成器生成的方法,外加入mybaits-plus,故有了下面的内容: ruoyi-admin的prom.xml中添加myba...
    99+
    2023-08-17
    mybatis java mysql
  • MyBatis框架底层的执行原理源码解析
    目录1.前言2.案例项目源码3.MyBatis源码解析底层执行原理3.1 读取mybatis配置文件创建出SqlSeesionFactory对象3.2 通过SqlSeesionFac...
    99+
    2024-04-02
  • MyBatis的SqlSession.getMapper()源码分析
    今天小编给大家分享一下MyBatis的SqlSession.getMapper()源码分析的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来...
    99+
    2023-07-05
  • MyBatis底层源码分析
    🎄欢迎来到@边境矢梦°的csdn博文🎄 🎄本文主要梳理MyBatis底层源码分析 🎄 🌈我是边境矢梦°,一个正在为秋招和算法竞赛做准备的学生ἰ...
    99+
    2023-10-20
    mybatis java 数据库
  • 解析MyBatis源码实现自定义持久层框架
    目录自定义框架设计自定义框架实现使用端框架端自定义框架设计 使用端 : 提供核⼼配置⽂件: sqlMapConfig.xml : 存放数据源信息,引⼊mapper.xml Mappe...
    99+
    2024-04-02
  • Android框架之Volley源码的示例分析
    这篇文章主要介绍Android框架之Volley源码的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!Volley简单使用我这里是以依赖架包的形式 ,大家也可以以gradle的形式进行依赖。好了,接下来上代码了...
    99+
    2023-06-15
  • mybatis-plus查询源码详解
    目录配置详情测试类debug流程总结配置详情 pom.xml dependency> <groupId>com.baomidou</groupId&...
    99+
    2024-04-02
  • Mybatis Plus案例分析
    这篇文章主要介绍了Mybatis Plus案例分析的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Mybatis Plus案例分析文章都会有所收获,下面我们一起来看看吧。1 初识Mybatis-Plus  MyB...
    99+
    2023-06-29
  • Android框架之OkHttp3源码的示例分析
    这篇文章将为大家详细讲解有关Android框架之OkHttp3源码的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。OkHttp流程图OkHttp基本使用gradle依赖implementation...
    99+
    2023-06-15
  • Mybatis-Spring源码分析图解
    Mybatis-Spring 当我们使用mybatis和spring整合后为什么下面的代码可以运行? 一个问题: 我就写了个mapper接口为什么能用? 首先来看...
    99+
    2024-04-02
  • Java集合框架概览之ArrayList源码分析
    今天小编给大家分享一下Java集合框架概览之ArrayList源码分析的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。一、从一...
    99+
    2023-07-05
  • MyBatis-Plus框架整合详细方法
    目录MyBatis-Plus其特性有:引入依赖yml配置主启动类加上包扫码自动生产代码接口util扩展MyBatis-Plus MyBatis-Plus(简称 MP)是一个 MyBa...
    99+
    2024-04-02
  • Mybatis-Plus的示例分析
    这篇文章主要介绍了Mybatis-Plus的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。Mybatis-Plus1.快速入门地址:安装 | MyBatis-Plus...
    99+
    2023-06-20
  • 解析Android框架之OkHttp3源码
    目录OkHttp流程图OkHttp基本使用OkHttp源码分析OkHttp流程图 OkHttp基本使用 gradle依赖 implementation 'com.squareup...
    99+
    2024-04-02
  • 怎么解析jQuery框架源码
    怎么解析jQuery框架源码,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。对于jQuery只停留在应用是可悲的,在做项目的过程,jquer...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作