范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

大型SaaS系统的数据范围权限设计与实现!

  前言
  多租户(Multi-Tenant)是SaaS中的一个重要概念,它是一种软件架构技术,在多个租户的环境下,共享同一套系统实例,并且租户之间的数据具有隔离性,也就是说一个租户不能去访问其他租户的数据。基于不同的隔离级别,通常具有下面三种实现方案:  每个租户使用独立DataBase,隔离级别高,性能好,但成本大  租户之间共享DataBase,使用独立的Schema  租户之间共享Schema,在表上添加租户字段,共享数据程度最高,隔离级别最低。  数据库设计
  Mybatis-plus在第3层隔离级别上,提供了基于分页插件的多租户的解决方案,我们对此来进行介绍。在正式开始前,首先做好准备工作创建两张表,在基础字段后都添加租户字段tenant_id:  CREATE TABLE `user` (   `id` bigint(20) NOT NULL,   `name` varchar(20) DEFAULT NULL,   `phone` varchar(11) DEFAULT NULL,   `address` varchar(64) DEFAULT NULL,   `tenant_id` bigint(20) DEFAULT NULL,   PRIMARY KEY (`id`) ) CREATE TABLE `dept` (   `id` bigint(20) NOT NULL,   `dept_name` varchar(64) DEFAULT NULL,   `comment` varchar(128) DEFAULT NULL,   `tenant_id` bigint(20) DEFAULT NULL,   PRIMARY KEY (`id`) ) 引入依赖
  在项目中导入需要的依赖:       com.baomidou     mybatis-plus-boot-starter     3.3.2       com.github.jsqlparser     jsqlparser     3.1  实现
  Mybatis-plus 配置类:  @EnableTransactionManagement(proxyTargetClass = true) @Configuration public class MybatisPlusConfig {     @Bean     public PaginationInterceptor paginationInterceptor() {         PaginationInterceptor paginationInterceptor = new PaginationInterceptor();          List sqlParserList=new ArrayList<>();         TenantSqlParser tenantSqlParser=new TenantSqlParser();         tenantSqlParser.setTenantHandler(new TenantHandler() {             @Override             public Expression getTenantId(boolean select) {                                String tenantId = "3";                 return new StringValue(tenantId);             }              @Override             public String getTenantIdColumn() {                 return "tenant_id";             }              @Override             public boolean doTableFilter(String tableName) {                 return false;             }         });          sqlParserList.add(tenantSqlParser);         paginationInterceptor.setSqlParserList(sqlParserList);         return paginationInterceptor;     } }
  这里主要实现的功能:  创建SQL解析器集合  创建租户SQL解析器  设置租户处理器,具体处理租户逻辑
  这里暂时把租户的id固定写成3,来进行测试。测试执行全表语句:  public List getUserList() {     return userMapper.selectList(new LambdaQueryWrapper().isNotNull(User::getId)); }
  使用插件解析执行的SQL语句,可以看到自动在查询条件后加上了租户过滤条件:
  那么在实际的项目中,怎么将租户信息传给租户处理器呢,根据情况我们可以从缓存或者请求头中获取,以从Request请求头获取为例:  @Override public Expression getTenantId(boolean select) {     ServletRequestAttributes attributes=(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();     HttpServletRequest request = attributes.getRequest();     String tenantId = request.getHeader("tenantId");     return new StringValue(tenantId); }
  前端在发起http请求时,在Header中加入tenantId字段,后端在处理器中获取后,设置为当前这次请求的租户过滤条件。
  如果是基于请求头携带租户信息的情况,那么在使用中可能会遇到一个坑,如果当使用多线程的时候,新开启的异步线程并不会自动携带当前线程的Request请求。  @Override public List getUserListByFuture() {     Callable getUser=()-> userMapper.selectList(new LambdaQueryWrapper().isNotNull(User::getId));     FutureTask> future=new FutureTask<>(getUser);     new Thread(future).start();     try {         return future.get();     } catch (Exception e) {         e.printStackTrace();     }     return null; }
  执行上面的方法,可以看出是获取不到当前的Request请求的,因此无法获得租户id,会导致后续报错空指针异常:
  修改的话也非常简单,开启RequestAttributes的子线程共享,修改上面的代码:  @Override public List getUserListByFuture() {     ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();     Callable getUser=()-> {         RequestContextHolder.setRequestAttributes(sra, true);         return userMapper.selectList(new LambdaQueryWrapper().isNotNull(User::getId));     };     FutureTask> future=new FutureTask<>(getUser);     new Thread(future).start();     try {         return future.get();     } catch (Exception e) {         e.printStackTrace();     }     return null; }
  这样修改后,在异步线程中也能正常的获取租户信息了。
  那么,有的小伙伴可能要问了,在业务中并不是所有的查询都需要过滤租户条件啊,针对这种情况,有两种方式来进行处理。
  1、如果整张表的所有SQL操作都不需要针对租户进行操作,那么就对表进行过滤,修改doTableFilter方法,添加表的名称:  @Override public boolean doTableFilter(String tableName) {     List IGNORE_TENANT_TABLES= Arrays.asList("dept");     return IGNORE_TENANT_TABLES.stream().anyMatch(e->e.equalsIgnoreCase(tableName)); }
  这样,在dept表的所有查询都不进行过滤:
  2、如果有一些特定的SQL语句不想被执行租户过滤,可以通过@SqlParser注解的形式开启,注意注解只能加在Mapper接口的方法上:  @SqlParser(filter = true) @Select("select * from user where name =#{name}") User selectUserByName(@Param(value="name") String name);
  或在分页拦截器中指定需要过滤的方法:  @Bean public PaginationInterceptor paginationInterceptor() {     PaginationInterceptor paginationInterceptor = new PaginationInterceptor();     paginationInterceptor.setSqlParserFilter(metaObject->{         MappedStatement ms = SqlParserHelper.getMappedStatement(metaObject);         // 对应Mapper、dao中的方法         if("com.cn.tenant.dao.UserMapper.selectUserByPhone".equals(ms.getId())){             return true;         }         return false;     });     ... }
  上面这两种方式实现的功能相同,但是如果需要过滤的SQL语句很多,那么第二种方式配置起来会比较麻烦,因此建议通过注解的方式进行过滤。
  除此之外,还有一个比较容易踩的坑就是在复制Bean时,不要复制租户id字段,否则会导致SQL语句报错:  public void createSnapshot(Long userId){     User user = userMapper.selectOne(new LambdaQueryWrapper().eq(User::getId, userId));     UserSnapshot userSnapshot=new UserSnapshot();     BeanUtil.copyProperties(user,userSnapshot);     userSnapshotMapper.insert(userSnapshot); }
  查看报错可以看出,本身Bean的租户字段不为空的情况下,SQL又自动添加一次租户查询条件,因此导致了报错:
  我们可以修改复制Bean语句,手动忽略租户id字段,这里使用的是hutool的BeanUtil工具类,可以添加忽略字段。  BeanUtil.copyProperties(user,userSnapshot,"tenantId");
  在忽略了租户id的拷贝后,查询可以正常执行。
  最后,再来看一下对联表查询的支持,首先看一下包含子查询的SQL:  @Select("select * from user where id in (select id from user_snapshot)") List selectSnapshot();
  查看执行结果,可以看见,在子查询的内部也自动添加的租户查询条件:
  再来看一下使用Join进行联表查询:  @Select("select u.* from user u left join user_snapshot us on u.id=us.id") List selectSnapshot();
  同样,会在左右两张表上都添加租户的过滤条件:
  再看一下不使用Join的普通联表查询:  @Select("select u.* from user u ,user_snapshot us,dept d where u.id=us.id and d.id is not null") List selectSnapshot();
  查看执行结果,可以看见在这种情况下,只在FROM关键字后面的第一张表上添加了租户的过滤条件,因此如果使用这种查询方式,需要额外注意,用户需要手动在SQL语句中添加租户过滤。
  来源:https://mp.weixin.qq.com/s/SREXWGeYvto_Cb-abBBWrg

韩国首尔旅记我们趁着过年到韩国首尔来逛逛,看看他们有没有历史传统的年味,我们是年初一到达首尔的。第二天一早我们就去光化门广场的景福宫,有点像北京天安门广场的故宫,但是规模和我们的故宫没法比,我用脚步丈量土地,感受宽实胸膛的温度(刚上涠洲岛时看见的蓝桥)涠洲岛,两天,约15个小时,大于280000米的步行路程。回到家中,我感到双腿被难以言状的酸痛缠绕着,可那些天的一切,我想,仍旧让自己觉得意犹未尽。我还是组图NASA新一代巨型月球火箭首亮相NASA太空发射系统火箭与猎户座飞船离开位于佛州卡纳维拉尔角的装配大楼,前往肯尼迪航天中心39B发射基地。美国国家航空航天局(NASA)的新一代巨型月球火箭首次亮相,这标志着美国再月球科学家和工程师设计月球洞穴探测器月球洞穴不仅是月球历史的地质原始记录,而且还可以为未来的人类探险者提供一个安全的家园。在欧空局发现号OSIPCALL和SysNova挑战的基础上,欧空局召集了来自许多不同科学和工程孩子流鼻血怎么办?仰头塞纸团都没用,正确的方法在这里快收藏相信很多人都有过流鼻血的经历,特别是对于小孩子而言,难免会因为一些外力原因,或者身体原因而出现流鼻血的情况。而对于很多新手家长而言,孩子在出现流鼻血的情况后,往往会表现得手足无措,美三大股指涨超1,热门中概股多数下跌,国际油价暴涨8澎湃新闻记者孙燕周三大涨后,美股三大股指周四再度收红。截至美东时间3月17日收盘,道指收涨417。66点,报34480。76点,涨幅为1。23纳指收涨178。23点,报13614。推荐几款降价比较快的热门机型当下手机市场的竞争力非常残酷,手机厂商为了获得极致热度都在疯狂进行堆料,同时市场中还诞生了非常多极具性价比的产品。因为如今手机的卖点都差不多,如果厂商本身没有带来特别新鲜的因素,那值得收藏的男士街拍穿搭技巧终于不用再P图了街拍一直都被很多人当成穿搭样板,对于不懂造型搭配的小白们来说,街拍具有很高的价值,里面的搭配技巧和造型都很适合普通人学习。和中国不同,在韩国街头我们能发现很多男生在穿衣打扮上面都有每天学一点黄帝内经秋冬两季养生篇!快快收藏吧在上篇文章给大家介绍了黄帝内经春夏两季的养生知识,今天接着给大家分享其中关于秋冬两季的养生方法喜欢的点赞加关注!秋季养生篇秋三月,此谓容平。天气以急,地气以明,早卧早起,与鸡俱兴,七夕会健康今年冬奥会期间,我兴致勃勃地每天收看滑雪比赛,运动员娴熟的技巧令我羡慕,其拼搏的精神更令我敬佩。我不由得想起多年前赴哈尔滨滑雪的刺激经历。从哈尔滨到亚布力滑雪场需要三个半小时的行程2022年菲律宾商务签办理条件以及资料全说明菲律宾的疫情已经持续两年时间了,随着政策的变动全面开放已经近在咫尺,相信在开放以后会有大量人员办理商务签证入境,还有很多小伙伴不清楚怎么办理,那么我们了解下2022年办理菲律宾商务
反击开始了!这次又砍单140亿颗芯片,外媒结果明显了点击关注,每天精彩不断!导读反击开始了!这次又砍单140亿颗芯片,外媒结果明显了!众所周知,半导体芯片产业的快速发展,也在不断地驱动着整个科技领域的发展作为科技领域发展的核心,半导油价9月6日迎来调整,倒计时3个工作日,预计95号汽油显著上涨自从国内成品油价实现五连跌之后,全国各地汽柴油价格确实达到了较低的价位,对应92号95号汽油以及0号柴油价格明显下调,车主加满一箱汽油的费用较少不小,尤其95号汽油0号柴油分别下调滕哈格称已说服C罗留在曼联,或首发出战莱斯特城!马夏尔仍缺阵曼联新帅滕哈格认为,他已经说服C罗认可自己的理念,后者将会留在队中继续效力。52岁的荷兰人证实,自己多次与C罗进行会晤,并且达成了共识。滕哈格说是的,我们谈了好几次,我们达成了一致20年疯狂夏窗截止日豪掷14亿,曼联2。1亿居首,卢卡库标王自从坎波成为史上第一位在夏窗截止日转投英超的球员以来,已经过去了20年。在这期间,马克莱莱鲁尼欧文和C罗都有过截止日转会的经历。那么,英超俱乐部们在夏窗截止日一共完成了多少笔引援呢勇士签204强援!一夜4笔签约猛龙捡漏2米03前锋,马刺补强后卫尽管在米切尔等球员的影响下,今夏暗流涌动的交易市场,一度吸引了不少球迷和媒体的关注。但好在伴随着休赛期的推进,自由市场上各支球队之间的抢人大战和军备竞赛,同样不乏看点!而仅仅一夜,创NBA历史纪录!恭喜詹姆斯将成为球员兼4支球队老板的球星北京时间8月31号,NBA交易市场正在火热进行中,联赛各队都在积极备战着,力争帮助球队在今夏完成阵容的升级补强,以在新赛季拿到更多场次的胜利,吸引了数以万计球迷的目光。与此同时,交媒体人塔克法尔近日入境开始隔离有望9月中旬与球队汇合直播吧9月1日讯今日据媒体人所罗门的臣仆报道,新疆大外援塔克法尔于近日入境开始隔离,9月中旬有望与球队汇合。小外援皮埃尔杰克逊已经与球队汇合并开始训练。所罗门的臣仆微博原文如下新疆扎卡本赛季我们心态改善了曼联或与我们三年前情况相似在今天凌晨结束的英超第5轮比赛中,阿森纳在主场21战胜维拉,取得了联赛的5连胜,开赛至今还未尝败绩。而在接受伦敦晚旗报的采访时,阿森纳中场扎卡谈到了球队本赛季的变化,他认为球队最重曼联华裔小将即将永久离队!委身英冠保级队,在英超4年仅出场5次在顶着超级天才新古利特等名号升入曼联一线队,以及凭借华裔血统格外受到中国球迷关注后,钟塔西(塔希提钟)的成长轨迹无疑是让人失望的。日前,根据英国媒体talkSPORT的最新报道,钟AiFA体育鲁尼指指点点别人球队,但他自己执教的球队却垫底糟糕鲁尼指指点点别人别的球队,但他自己执教的球队却垫底糟糕做球员和做教练完全不是一回事儿,杰拉德和兰帕德现在在英超的执教非常的糟糕,这2人随时都有可能下课,而另外一个英格兰名宿鲁尼,现爆大冷!乒乓世界冠军13惨败,19岁女将收大礼后,双线大捷8月31日,WTT保加利亚支线赛资格赛继续进行,在结束的男双第一轮比拼中,国乒1胜1负,周恺任浩面对世界冠军陈建安搭档黄毓仁组合,直接31掀翻,而牛冠凯全开源则23落败。13惨败!