写点什么

springboot2.3 手册:多租户及自动创建数据,这样做

发布于: 2020 年 09 月 21 日
springboot2.3手册:多租户及自动创建数据,这样做

现在很多项目都用 mybaits 来做,这里不讨论 hibernate 与 mybaits 的谁好用的问题,因地制宜去做,今天我们来聊一下一个国产的框架 Mybatis-Plus。



以前小编经常用 mybatis 来做开发,然后自己再写一个单表的自动化生成工作,虽然也是很简便,但是代码还是挺多,后面用 TK,到现在用 MP,都是挺好的东西,重复造轮子的活小编是不干的,没必要。


今天咱们就来讲一下 Mybatis-Plus 3.4.0 版本如何使用多租户及自动新增创建人,创建时间等。在开始之前,针对前后端分离的架构来说,先说说小编是怎么设计的。


1:系统登陆,获取基础的用户信息并把租户 ID(tenant),用户 ID(userId),用户名称(userName)放到一个地方去,如 localstorage 等,具体你自己定义。


2:每次访问后端服务的从缓存的地方获取这些信息,并放到 Header 里面,要注意一点,由于 Header 不能存放中文,此时你需要对中文进行 URL 编码。


3:后端每次进行操作的时候,根据需要从 Header 里面获取信息进行属性填充或者租户的判断。



基础包引用

目前 mybaits-plus 的最新版已经达到了 3.4.0,今天我们就用最新版来操作

<!-- mybatisplus配置 --><dependency>  <groupId>com.baomidou</groupId>  <artifactId>mybatis-plus-boot-starter</artifactId>  <version>3.4.0</version> </dependency>
复制代码

多租户判断

多租户的判断的原理说起来非常简单,就是基于 mybatis 的拦截器来操作,在进行新增,修改,删除的时候,从某一个固定的地方获取租户 ID,并插入到指定的 SQL 里面去。在 Mybatis-Plus 里面用的是分页拦截器进行填充,参考一下我这边的写法,并且已经标注注意的事项。


备注:在进行插件的增加的时候,一定要注意如果分页插件与多租户插件同时应用,一定要先写多租户拦截器。


/**     * 添加分页插件支持          * @author 互联网应用架构        * @return PaginationInterceptor          */    @Bean    public MybatisPlusInterceptor paginationInterceptor() {        final MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();        // 新增多租户拦截器        interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(new TenantLineHandler() {            /**             * 获取租户ID                          * @author 林溪                          * @return 租户ID                          */            @Override            public Expression getTenantId() {                return new LongValue(HeaderUtils.getTenantId());            }            /**             * 判断哪些表不需要尽心多租户判断,返回false表示都需要进行多租户判断             * @author 林溪                          * @param tableName                          * @return                           */            @Override            public boolean ignoreTable(String tableName) {                return false;            }        }));        // 新增MYSQL分页拦截器        // 一定要先设置租户判断后才进行分页拦截设置        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));        return interceptor;    }
复制代码


这样就完成了一个分页插件及多租户的插件的新增,有时候有些表不需要进行多租户的判断的时候,自己在 ignoreTable 这个方法里面新增判断即可。



自动填充数据

自动填充数据来源于小编的表中有创建人 ID,创建人姓名,创建时间,更新人 ID,更新人姓名,更新时间,删除状态,先给大伙看看我的这些字段。


// 创建人id@TableField(value = "created_id", fill = FieldFill.INSERT)    private Long createdId;// 创建人名称    @TableField(value = "created_name", fill = FieldFill.INSERT)    private String createdName;// 创建时间    @TableField(value = "created_date", fill = FieldFill.INSERT)    private Date createdDate;// 更新人id    @TableField(value = "updated_id", fill = FieldFill.UPDATE)    private Long updatedId;// 更新人名称    @TableField(value = "updated_name", fill = FieldFill.UPDATE)    private String updatedName;// 更新时间    @TableField(value = "updated_date", fill = FieldFill.UPDATE)    private Date updatedDate;// 0:未删除;1已删除    @TableField(value = "deleted_status", fill = FieldFill.INSERT)    private int deletedStatus;
复制代码


这些字段在新增或者更新的时候,如果每次都要去手动操作,那就比较繁琐,写起来太辛苦了,Mybatis-Plus 目前提供了一个解决方案给我们,一个字段填充的接口 MetaObjectHandler,我们只需要新建一个类来继承这个接口,并处理即可。


/** * All rights Reserved, Designed By 林溪 * Copyright:    Copyright(C) 2016-2020 * Company       林溪开源 */
package com.modules.boots.mp.mysql;
import java.util.Date;
import org.apache.ibatis.reflection.MetaObject;import org.springframework.stereotype.Component;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;import com.bases.common.constant.BaseConstant;import com.bases.common.utils.HeaderUtils;
import lombok.extern.slf4j.Slf4j;
/** * 字段自动填充 * @author:林溪 * @date:2020年9月9日 */@Slf4j@Componentpublic class FiledsFillingHandler implements MetaObjectHandler {
/** * 新增数据的时候,自动进行数据填充 * @author 林溪 * @param metaObject * @return */ @Override public void insertFill(MetaObject metaObject) { log.info("开始进行新增字段数据填充"); final Object createdId = getFieldValByName("createdId", metaObject); final Object createdName = getFieldValByName("createdName", metaObject); final Object createdDate = getFieldValByName("createdDate", metaObject); final Object deletedStatus = getFieldValByName("deletedStatus", metaObject); if (createdId == null) { this.setFieldValByName("createdId", HeaderUtils.getUserId(), metaObject); } if (createdName == null) { this.setFieldValByName("createdName", HeaderUtils.getUserName(), metaObject); } if (createdDate == null) { this.setFieldValByName("createdDate", new Date(), metaObject); } if (deletedStatus == null) { this.setFieldValByName("deletedStatus", BaseConstant.UNDELETE, metaObject); } }
/** * 更新数据的时候,自动进行数据填充 * @author 林溪 * @param metaObject * @return */ @Override public void updateFill(MetaObject metaObject) { log.info("开始进行更新字段数据填充"); final Object updatedId = getFieldValByName("updatedId", metaObject); final Object updatedName = getFieldValByName("updatedName", metaObject); final Object updatedDate = getFieldValByName("updatedDate", metaObject); if (updatedId == null) { this.setFieldValByName("updatedId", HeaderUtils.getUserId(), metaObject); } if (updatedName == null) { this.setFieldValByName("updatedName", HeaderUtils.getUserName(), metaObject); } if (updatedDate == null) { this.setFieldValByName("updatedDate", new Date(), metaObject); } }
}
复制代码


这里有一点可能要注意一下,就是针对实体类中那些需要进行新增或者更新的字段,需要手动加入一个注解,这样就可以实现在新增或者更新的时候,进行数据的自动填充了

// 新增的时候进行填充fill = FieldFill.INSERT// 更新的时候进行填充fill = FieldFill.UPDATE// 新增/更新的时候进行填充fill = FieldFill.INSERT_UPDATE
复制代码


总结

这里还是要说一下,不重复造轮子,对我们 IT 的开发者来说,很多时候都喜欢自己写,这样子比较有成就感,但是小编认为,花时间去读懂别的思想及设计还是非常重要的,对于已经存在的东西,小编认为三步走:读懂设计,用起来,按需改造。


发布于: 2020 年 09 月 21 日阅读数: 153
用户头像

喜欢奋战在一线的架构师 2020.09.21 加入

专注架构,微服务,机器学习,JAVA,Python等领域教程,欢迎关注

评论

发布
暂无评论
springboot2.3手册:多租户及自动创建数据,这样做