<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                # 一、什么是多租戶 多租戶技術或稱多重租賃技術,簡稱多租戶。是一種軟件架構技術,是實現如何在多用戶環境下(此處的多用戶一般是面向企業用戶)共用相同的系統或程序組件,并且可確保各用戶間數據的隔離性。 簡單講:在一臺服務器上運行單個應用實例,它為多個租戶(客戶)提供服務。從定義中我們可以理解:多租戶是一種架構,目的是為了讓多用戶環境下使用同一套程序,且保證用戶間數據隔離。那么重點就很淺顯易懂了,多租戶的重點就是同一套程序下實現多用戶數據的隔離。 # 二、數據隔離有三種方案 1. 獨立數據庫:簡單來說就是一個租戶使用一個數據庫,這種數據隔離級別最高,安全性最好,但是提高成本。 2. 共享數據庫、隔離數據架構:多租戶使用同一個數據庫,但是每個租戶對應一個Schema(數據庫user)。 3. 共享數據庫、共享數據架構:使用同一個數據庫,同一個Schema,但是在表中增加了`租戶ID`的字段,這種共享數據程度最高,隔離級別最低。 這里采用方案三,即共享數據庫,共享數據架構,因為這種方案服務器成本最低,但是提高了開發成本。 # 三、Mybatis-plus實現多租戶方案 > 為什么選擇MyBatisPlus? > 除了一些系統共用的表以外,其他租戶相關的表,我們都需要在sql不厭其煩的加上`AND t.tenant_id = ?`查詢條件,稍不注意就會導致數據越界,數據安全問題讓人擔憂。好在有了MybatisPlus這個神器,可以極為方便的實現多租戶SQL解析器。 Mybatis-plus就提供了一種多租戶的解決方案,實現方式是基于分頁插件(攔截器)進行實現的。 ## 3.1 第一步: * 在數據庫中添加維護一張sys\_tenant(租戶管理表), * 在需要進行租戶數據隔離的數據表上新增租戶id; ## 3.2 第二步: 創建表: ~~~ CREATE TABLE `orders_1`.`tenant` ( `id` int(0) NOT NULL AUTO_INCREMENT COMMENT '自增主鍵', `expire_date` datetime(0) COMMENT '協議到期時間', `amount` decimal(8, 2) COMMENT '金額', `tenant_id` int(0) COMMENT '租戶ID', PRIMARY KEY (`id`) ); ~~~ 自定義系統的上下文,存儲從cookie等方式獲取的租戶ID,在后續的getTenantId()使用。 ~~~ package com.erbadagang.mybatis.plus.tenant.config; import org.springframework.stereotype.Component; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * @description 系統的上下文幫助類。ConcurrentHashMap設置租戶ID,供后續的MP的getTenantId()取出 * @ClassName: ApiContext * @author: 郭秀志 jbcode@126.com * @date: 2020/7/12 21:50 * @Copyright: */ @Component public class ApiContext { private static final String KEY_CURRENT_TENANT_ID = "KEY_CURRENT_TENANT_ID"; private static final Map<String, Object> mContext = new ConcurrentHashMap<>(); public void setCurrentTenantId(Long providerId) { mContext.put(KEY_CURRENT_TENANT_ID, providerId); } public Long getCurrentTenantId() { return (Long) mContext.get(KEY_CURRENT_TENANT_ID); } } ~~~ 核心類——`MyBatisPlusConfig`通過分頁插件配置MP多租戶。 ~~~ package com.erbadagang.mybatis.plus.tenant.config; import com.baomidou.mybatisplus.core.parser.ISqlParser; import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor; import com.baomidou.mybatisplus.extension.plugins.tenant.TenantHandler; import com.baomidou.mybatisplus.extension.plugins.tenant.TenantSqlParser; import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.LongValue; import org.mybatis.spring.annotation.MapperScan; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.ArrayList; import java.util.List; /** * @description MyBatisPlus配置類,分頁插件,多租戶也是使用的分頁插件進行的配置。 * @ClassName: MyBatisPlusConfig * @author: 郭秀志 jbcode@126.com * @date: 2020/7/12 21:34 * @Copyright: */ @Configuration @MapperScan("com.erbadagang.mybatis.plus.tenant.mapper")//配置掃描的mapper包 public class MyBatisPlusConfig { @Autowired private ApiContext apiContext; /** * 分頁插件 * * @return */ @Bean public PaginationInterceptor paginationInterceptor() { PaginationInterceptor paginationInterceptor = new PaginationInterceptor(); // 創建SQL解析器集合 List<ISqlParser> sqlParserList = new ArrayList<>(); // 創建租戶SQL解析器 TenantSqlParser tenantSqlParser = new TenantSqlParser(); // 設置租戶處理器 tenantSqlParser.setTenantHandler(new TenantHandler() { // 設置當前租戶ID,實際情況你可以從cookie、或者緩存中拿都行 @Override public Expression getTenantId(boolean select) { // 從當前系統上下文中取出當前請求的服務商ID,通過解析器注入到SQL中。 Long currentProviderId = apiContext.getCurrentTenantId(); if (null == currentProviderId) { throw new RuntimeException("Get CurrentProviderId error."); } return new LongValue(currentProviderId); } @Override public String getTenantIdColumn() { // 對應數據庫中租戶ID的列名 return "tenant_id"; } @Override public boolean doTableFilter(String tableName) { // 是否需要需要過濾某一張表 /* List<String> tableNameList = Arrays.asList("sys_user"); if (tableNameList.contains(tableName)){ return true; }*/ return false; } }); sqlParserList.add(tenantSqlParser); paginationInterceptor.setSqlParserList(sqlParserList); return paginationInterceptor; } } ~~~ # 四、測試 配置好之后,不管是查詢、新增、修改刪除方法,MP都會自動加上租戶ID的標識,測試如下: ~~~ package com.erbadagang.mybatis.plus.tenant; import com.erbadagang.mybatis.plus.tenant.config.ApiContext; import com.erbadagang.mybatis.plus.tenant.entity.Tenant; import com.erbadagang.mybatis.plus.tenant.mapper.TenantMapper; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import java.util.List; /** * @description 多租戶測試用例 * @ClassName: MultiTanentApplicationTests * @author: 郭秀志 jbcode@126.com * @date: 2020/7/12 22:06 * @Copyright: */ @SpringBootTest class MultiTanentApplicationTests { @Autowired private ApiContext apiContext; @Autowired private TenantMapper tenantMapper; @Test public void before() { // 在上下文中設置當前服務商的ID apiContext.setCurrentTenantId(1L); } @Test public void select() { List<Tenant> tenants = tenantMapper.selectList(null); tenants.forEach(System.out::println); } } ~~~ 輸出的SQL自動包括`WHERE tenant_id = 1`: ~~~ ==> Preparing: SELECT id, expire_date, amount, tenant_id FROM t_tenant WHERE tenant_id = 1 ==> Parameters: <== Total: 0 ~~~ # 五、特定SQL過濾 如果在程序中,有部分SQL不需要加上租戶ID的表示,需要過濾特定的sql,可以通過如下兩種方式: ## 5.1 方式一: 在配置分頁插件中加上配置ISqlParserFilter解析器,如果配置SQL很多,比較麻煩,不建議。 ~~~ //有部分SQL不需要加上租戶ID的表示,需要過濾特定的sql。如果比較多不建議這里配置。 /*paginationInterceptor.setSqlParserFilter(new ISqlParserFilter() { @Override public boolean doFilter(MetaObject metaObject) { MappedStatement ms = SqlParserHelper.getMappedStatement(metaObject); // 對應Mapper或者dao中的方法 if("com.erbadagang.mybatis.plus.tenant.mapper.UserMapper.selectList".equals(ms.getId())){ return true; } return false; } });*/ ~~~ ## 5.2 方式二: 通過租戶注解的形式,目前只能作用于Mapper的方法上。特定sql過濾 過濾特定的方法 也可以在userMapper需要排除的方法上加入注解SqlParser(filter=true) 排除 SQL 解析。 ~~~ package com.erbadagang.mybatis.plus.tenant.mapper; import com.baomidou.mybatisplus.annotation.SqlParser; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.erbadagang.mybatis.plus.tenant.entity.Tenant; import org.apache.ibatis.annotations.Select; /** * <p> * Mapper 接口 * </p> * * @author 郭秀志 jbcode@126.com * @since 2020-07-12 */ public interface TenantMapper extends BaseMapper<Tenant> { /** * 自定Wrapper, @SqlParser(filter = true)注解代表不進行SQL解析也就沒有租戶的附加條件。 * * @return */ @SqlParser(filter = true) @Select("SELECT count(5) FROM t_tenant ") public Integer myCount(); } ~~~ 測試 ~~~ @Test public void myCount() { Integer count = tenantMapper.myCount(); System.out.println(count); } ~~~ SQL輸出 ~~~ ==> Preparing: SELECT count(5) FROM t_tenant ==> Parameters: <== Columns: count(5) <== Row: 0 <== Total: 1 ~~~ 開啟 SQL 解析緩存注解生效,如果你的MP版本在3.1.1及以上則不需要配置 ~~~ # 開啟 SQL 解析緩存注解生效,如果你的MP版本在3.1.1及以上則不需要配置 mybatis-plus: global-config: sql-parser-cache: true ~~~
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看