日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術文章
文章詳情頁

Fluent Mybatis實現環境隔離和租戶隔離

瀏覽:41日期:2023-10-18 11:08:59
目錄什么是環境隔離和多租戶隔離環境隔離和多租戶隔離需要做的事情環境隔離和租戶隔離工具類隔離前準備工作增刪改查環境和租戶隔離演示新增數據查詢數據更新數據總結什么是環境隔離和多租戶隔離

我們在實際的業務開發中,經常會碰到環境邏輯隔離和租戶數據邏輯隔離的問題。

環境隔離

我們的開發系統過程中,經常會涉及到日常開發環境,測試環境,預發環境和線上環境,如何區隔這些環境,有些方案是采用獨立的數據庫,有些是采用同一套數據庫(比如線下多個測試環境使用同一個數據庫,預發環境和線上環境使用同一個數據庫),然后對數據進行打標的辦法,來區分不同環境的數據。

多租戶管理

在復雜的業務系統中,比如SaaS應用中,在多用戶環境下共用相同的系統或程序組件,如何確保各用戶間數據的隔離性。簡單講:在一臺服務器上運行單個應用實例,它為多個租戶(客戶)提供服務。從定義中我們可以理解:多租戶是一種架構,是為了讓多用戶環境下使用同一套程序,但要保證用戶間數據隔離。那如何進行多租戶的重點就是同一套程序下實現多用戶數據的隔離,做法其實和環境隔離是同一個道理。這里采用多環境多租戶共用數據表的場景,來探討下FluentMybatis是如何支持多環境和多租戶管理的。

環境隔離和多租戶隔離需要做的事情

比如我們有下面表

create table student( id bigint(21) unsigned auto_increment comment ’主鍵id’primary key, age int null comment ’年齡’, grade int null comment ’年級’, user_name varchar(45) null comment ’名字’, gender_man tinyint(2) default 0 null comment ’性別, 0:女; 1:男’, birthdaydatetime null comment ’生日’, phone varchar(20) null comment ’電話’, bonus_points bigint(21) default 0 null comment ’積分’, status varchar(32) null comment ’狀態(字典)’, home_county_id bigint(21) null comment ’家庭所在區縣’, home_address_id bigint(21) null comment ’home_address外鍵’, address varchar(200) null comment ’家庭詳細住址’, version varchar(200) null comment ’版本號’, env varchar(10) NULL comment ’數據隔離環境’, tenant bigint NOT NULL default 0 comment ’租戶標識’, gmt_created datetime null comment ’創建時間’, gmt_modified datetime null comment ’更新時間’, is_deleted tinyint(2) default 0 null comment ’是否邏輯刪除’) ENGINE = InnoDB DEFAULT CHARSET = utf8 COMMENT ’學生信息表’;

注意其中的2個字段

env, 表示應用部署的環境, 環境的區隔一般是采用應用部署的機器環境變量。 tenant, 表示數據所屬租戶,租戶的隔離一般是通過登錄用戶信息獲取的。

對環境和租戶的隔離,主要是CRUD過程中,需要帶上環境變量和租戶信息。如果沒有框架的支持,就需要在構造SQL的過程中,手動設置env和tenant。這就存在一個嚴重的弊端: 在編碼過程中,需要時刻注意sql語句中不要漏了這2個條件,否則就會產生邏輯錯誤和信息泄露。

為了減少錯誤,我們都會將邏輯進行收攏,下面我們演示fluent mybatis如何統一處理。

環境隔離和租戶隔離工具類

為了進行環境隔離和租戶隔離,我們一般會統一定義獲取環境變量和租戶信息的工具類。

環境隔離工具類

/** * 應用部署環境工具類 */public class EnvUtils { public static String currEnv() {// 應用啟動時, 讀取的機器部署環境變量, 這里簡化為返回固定值演示return 'test1'; }}

租戶隔離工具類

/** * 獲取用戶所屬租戶信息工具類 */public class TenantUtils { /** * 租戶A */ static final long A_TENANT = 111111L; /** * 租戶B */ static final long B_TENANT = 222222L; /** * 租戶信息一般根據登錄用戶身份來判斷, 這里簡化為偶數用戶屬于租戶A, 奇數用戶屬于租戶B * * @return */ public static long findUserTenant() {long userId = loginUserId();if (userId % 2 == 0) { return A_TENANT;} else { return B_TENANT;} } /** * 當前登錄的用戶id, 一般從Session中獲取 * * @return */ public static long loginUserId() {return 1L; }}隔離前準備工作

Entity隔離屬性基類

為了方便對所有需要隔離的Entity進行統一的環境和租戶信息的設置和讀取,我們把Entity的環境和租戶的屬性的getter和setter方法定義到一個接口上。

/** * Entity類隔離屬性基類 */public interface IsolateEntity { /** * 返回entity env屬性值 * * @return */ String getEnv(); /** * 設置entity env屬性值 * * @param env * @return */ IsolateEntity setEnv(String env); /** * 返回entity 租戶信息 * * @return */ Long getTenant(); /** * 設置entity 租戶信息 * * @param tenant * @return */ IsolateEntity setTenant(Long tenant);}

這樣所有需要隔離的Entity只要繼承這個接口就可以在需要隔離操作的地方把具體的entity當作IsolateEntity對象來操作。

隔離屬性和默認條件設置

有了統一的接口,我們還需要一個默認進行設置的操作,fluent mybatis提供了一個IDefaultSetter 接口,可以對Entity,Query和Update進行攔截操作。

/** * 增刪改查中,環境和租戶隔離設置 */public interface IsolateSetter extends IDefaultSetter { /** * 插入的entity,如果沒有顯式設置環境和租戶,根據工具類進行默認設置 * * @param entity */ @Override default void setInsertDefault(IEntity entity) {IsolateEntity isolateEntity = (IsolateEntity) entity;if (isolateEntity.getEnv() == null) { isolateEntity.setEnv(EnvUtils.currEnv());}if (isolateEntity.getTenant() == null) { isolateEntity.setTenant(TenantUtils.findUserTenant());} } /** * 查詢條件追加環境隔離和租戶隔離 * * @param query */ @Override default void setQueryDefault(IQuery query) {query.where() .apply('env', SqlOp.EQ, EnvUtils.currEnv()) .apply('tenant', SqlOp.EQ, TenantUtils.findUserTenant()); } /** * 更新條件追加環境隔離和租戶隔離 * * @param updater */ @Override default void setUpdateDefault(IUpdate updater) {updater.where() .apply('env', SqlOp.EQ, EnvUtils.currEnv()) .apply('tenant', SqlOp.EQ, TenantUtils.findUserTenant()); }}

為了避免使用不當導致線程安全問題(變量共享), fluent mybatis只允許在應用中定義接口(比如這里的IsolateSetter)繼承IDefaultSetter, 不允許定義成類。

代碼生成設置

怎么讓fluent mybatis識別到哪些Entity可以繼承IsolateEntity,哪些Entity操作需要進行IsolateSetter統一攔截呢?在@FluentMybatis上有個屬性defaults(), 我們把defaults值設置為 IsolateSetter.class就可以了。

public @interface FluentMybatis { /** * entity, query, updater默認值設置實現 * * @return */ Class<? extends IDefaultSetter> defaults() default IDefaultSetter.class;}

當然,我們并不需要手動去修改Entity類,只需要在代碼生成上設置。

public class FluentGenerateMain { static final String url = 'jdbc:mysql://localhost:3306/fluent_mybatis?useSSL=false&useUnicode=true&characterEncoding=utf-8'; /** * 生成代碼的package路徑 */ static final String basePackage = 'cn.org.fluent.mybatis.many2many.demo'; public static void main(String[] args) {FileGenerator.build(Noting.class); } @Tables(/** 數據庫連接信息 **/url = url, username = 'root', password = 'password',/** Entity類parent package路徑 **/basePack = basePackage,/** Entity代碼源目錄 **/srcDir = 'example/many2many_demo/src/main/java',/** 如果表定義記錄創建,記錄修改,邏輯刪除字段 **/gmtCreated = 'gmt_created', gmtModified = 'gmt_modified', logicDeleted = 'is_deleted',/** 需要生成文件的表 ( 表名稱:對應的Entity名稱 ) **/tables = @Table(value = {'student'}, entity = IsolateEntity.class, defaults = IsolateSetter.class) ) static class Noting { }}

注意,對比之前的代碼生成,@Table上多了2個屬性設置

// 標識對應的Entity類需要繼承的接口entity = IsolateEntity.class

// 標識對應的Entity類CRUD過程中需要進行的默認設置操作defaults = IsolateSetter.class

執行代碼生成,Entity代碼如下:

@FluentMybatis( table = 'student', defaults = IsolateSetter.class)public class StudentEntity extends RichEntity implements IsolateEntity { // ... 省略}

我們看到@FluentMybatis設置了defaults屬性,Entity類繼承了IsolateEntity接口。接下來,我們進行具體的增刪改查演示。

增刪改查環境和租戶隔離演示新增數據

@RunWith(SpringRunner.class)@SpringBootTest(classes = AppMain.class)public class InsertWithEnvDemo { @Autowired private StudentMapper mapper; @Test public void insertEntity() {mapper.delete(new StudentQuery());mapper.insert(new StudentEntity() .setAddress('宇宙深處') .setUserName('FluentMybatis'));StudentEntity student = mapper.findOne(StudentQuery.query() .where.userName().eq('FluentMybatis').end() .limit(1));System.out.println(student.getUserName() + ', env:' + student.getEnv() + ', tenant:' + student.getTenant()); }}

查看控制臺輸出log

DEBUG - ==> Preparing: INSERT INTO student(gmt_created, gmt_modified, is_deleted, address, env, tenant, user_name) VALUES (now(), now(), 0, ?, ?, ?, ?) DEBUG - ==> Parameters: 宇宙深處(String), test1(String), 222222(Long), FluentMybatis(String) DEBUG - <== Updates: 1 DEBUG - ==> Preparing: SELECT id, gmt_created, gmt_modified, is_deleted, address, age, birthday, bonus_points, env, gender_man, grade, home_address_id, home_county_id, phone, status, tenant, user_name, version FROM student WHERE user_name = ? LIMIT ?, ? DEBUG - ==> Parameters: FluentMybatis(String), 0(Integer), 1(Integer) DEBUG - <== Total: 1 FluentMybatis, env:test1, tenant:222222

在演示例子中,我們雖然只顯式設置了userName和address2個屬性,但插入數據中設置了7個屬性,其中包括env和tenant。注意,這里的查詢條件并沒有帶上環境變量

查詢數據

fluent mybatis提供了2種構造查詢器的方式

XyzQuery.query(): 全新的不帶任何條件的查詢。 XyzQuery.defaultQuery(): 按照@FluentMybatis defaults屬性指定的接口,設置好默認查詢條件。

上面默認插入的例子已經演示了不帶條件的query()查詢,我們現在演示下設置了默認條件的查詢。

@RunWith(SpringRunner.class)@SpringBootTest(classes = AppMain.class)public class QueryWithEnvDemo { @Autowired private StudentMapper mapper; @Test public void testQueryWithEnv(){mapper.delete(new StudentQuery());mapper.insert(new StudentEntity() .setAddress('宇宙深處') .setUserName('FluentMybatis'));StudentEntity student = mapper.findOne(mapper.defaultQuery() .where.userName().eq('FluentMybatis').end() .limit(1));System.out.println(student.getUserName() + ', env:' + student.getEnv() + ', tenant:' + student.getTenant()); }}

查看控制log輸出

DEBUG - ==> Preparing: SELECT id, gmt_created, ... , tenant, user_name, version FROM student WHERE env = ? AND tenant = ? AND user_name = ? LIMIT ?, ? DEBUG - ==> Parameters: test1(String), 222222(Long), FluentMybatis(String), 0(Integer), 1(Integer) DEBUG - <== Total: 1 FluentMybatis, env:test1, tenant:222222

我們看到,查詢條件中除了有我們設置好的user_name,還包括在IsolateSetter接口中設置好的env和tenant字段。

更新數據

和Query一樣,Updater同樣提供了2個方法來構造Updater

XyzUpdate.updater() : 不帶任何條件的更新。 XyzUpdate.defaultUpdater(): 根據IsolateSetter#setUpdateDefault方法設置好更新條件。

演示例子

@RunWith(SpringRunner.class)@SpringBootTest(classes = AppMain.class)public class UpdateWithEnvDemo { @Autowired private StudentMapper mapper; @Test public void testQueryWithEnv() {mapper.delete(new StudentQuery());mapper.insert(new StudentEntity() .setAddress('宇宙深處') .setUserName('FluentMybatis'));mapper.updateBy(StudentUpdate.defaultUpdater() .update.address().is('回到地球').end() .where.userName().eq('FluentMybatis').end()); }}

查看控制臺log輸出

DEBUG - ==> Preparing: UPDATE student SET gmt_modified = now(), address = ? WHERE env = ? AND tenant = ? AND user_name = ? DEBUG - ==> Parameters: 回到地球(String), test1(String), 222222(Long), FluentMybatis(String) DEBUG - <== Updates: 1

更新條件中自動帶上了設置好的默認條件 env 和 tenant。

總結

Fluent Mybatis通過自定義接口繼承IDefaultSetter,賦予了你進行數據隔離操作的強大功能。默認值的賦值是通過編譯生成的XyzDefaults類來進行的,大家可以具體查看編譯生成的代碼。文中示例代碼

到此這篇關于Fluent Mybatis實現環境隔離和租戶隔離的文章就介紹到這了,更多相關Fluent Mybatis環境隔離和租戶隔離內容請搜索好吧啦網以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持好吧啦網!

標簽: Mybatis 數據庫
相關文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
中文字幕一区二区三区在线视频| 另类综合日韩欧美亚洲| 精品一区二区三区免费看| 久久男人av资源站| 欧美日一区二区| 国产一区二区三区黄网站| 麻豆精品视频在线观看| 久久中文字幕一区二区| 国产调教精品| 精品一区二区三区在线观看视频 | 黄色在线一区| 蜜臀av国产精品久久久久| 97久久中文字幕| 日韩欧美一区二区三区在线视频| 午夜国产精品视频| 日本午夜精品久久久久| 精品入口麻豆88视频| 91精品一区二区三区综合| 日韩精品一区第一页| 国产精成人品2018| 欧美日韩亚洲在线观看| 亚洲精品麻豆| 久久精品亚洲欧美日韩精品中文字幕| 亚洲精品在线a| 99久久婷婷这里只有精品| 国产精品久久久免费| 一区三区视频| 精品国产aⅴ| 日本在线视频一区二区| 91精品国产成人观看| 欧美黄色精品| 中文字幕亚洲精品乱码| 成人影视亚洲图片在线| 日韩专区在线视频| 涩涩av在线| 国产麻豆一区| 国产精品日韩久久久| 高清av一区| 国产欧美欧美| 午夜国产精品视频| 久久亚洲成人| 日韩三区免费| 日韩一区三区| 国内自拍视频一区二区三区| 日本不卡一区二区| 石原莉奈在线亚洲三区| 欧美日韩国产亚洲一区| 日韩不卡视频在线观看| 国产一区二区亚洲| 国产精品最新| 久久国产三级精品| 国产一精品一av一免费爽爽| 日韩国产成人精品| 色婷婷成人网| 国产亚洲久久| 国产日韩在线观看视频| 欧美影院精品| 国产精品videossex久久发布| 国产麻豆精品久久| 久草免费在线视频| 婷婷六月综合| 亚洲91在线| 国产精品巨作av| 亚洲爱爱视频| 激情综合激情| 亚洲2区在线| 久久99高清| 亚洲精品在线观看91| 婷婷五月色综合香五月| 精品一区二区三区视频在线播放| 国产精品久久久久久久久妇女| 久久国产影院| 亚洲电影在线一区二区三区| 国产精品日韩欧美一区| 六月天综合网| 日本成人中文字幕| 久久精品免费看| 久久精品卡一| 好看的亚洲午夜视频在线| 99在线精品视频在线观看| 久久一二三区| 国产精品调教视频| 日韩在线精品| 亚洲丝袜美腿一区| 国产精品久久久久久久免费观看 | 亚洲视频国产精品| 日韩国产91| 在线天堂中文资源最新版| 亚洲一区二区三区高清不卡| 蜜臀久久99精品久久久久久9| 欧美日本二区| 国产一区二区三区不卡视频网站 | 中文字幕高清在线播放| 欧美1级日本1级| 鲁大师精品99久久久| 午夜在线精品| 午夜欧美巨大性欧美巨大| 国产精品亚洲成在人线| 综合一区二区三区| 欧美在线亚洲综合一区| 成人在线视频区| 视频一区免费在线观看| 日韩精品dvd| 国产一区二区精品久| 亚洲一区二区小说| 亚洲在线成人| 九九色在线视频| 欧美日韩免费看片| 亚洲免费毛片| 欧美韩日一区| 国产精品网址| 色综合视频一区二区三区日韩| 久久免费黄色| 日韩成人精品一区二区| 国产精品18| 蜜桃91丨九色丨蝌蚪91桃色| 国产精品99一区二区| 精品久久精品| 欧美精品国产白浆久久久久| 蜜桃视频一区二区三区在线观看| 久久国产尿小便嘘嘘| 欧美日韩一视频区二区| 日韩avvvv在线播放| 日本不卡中文字幕| 日本不卡中文字幕| 亚洲天堂免费| 香蕉久久久久久| 国产精品美女久久久| 首页国产欧美久久| 蜜桃久久av一区| 亚洲欧美日韩国产一区| 亚洲免费精品| 97成人在线| 国产精品久久久久久久久久齐齐 | 日本欧美国产| 成人免费网站www网站高清| 成人精品亚洲| 久久亚洲视频| 日韩精品亚洲专区| 你懂的国产精品永久在线| 亚洲精品成人图区| 亚洲一区二区网站| 国产福利资源一区| 美女性感视频久久| 久久久夜精品| 亚洲一区二区三区无吗| 国产精品乱战久久久| 亚洲风情在线资源| 免费精品视频最新在线| 国产欧美91| 女人天堂亚洲aⅴ在线观看| 精品一区欧美| 欧美日韩在线精品一区二区三区激情综合| 日韩精品第一| 亚洲作爱视频| 麻豆成全视频免费观看在线看| 人人精品人人爱| 久久人人精品| 国产精品99一区二区三区| 亚洲精品自拍| 国产精品麻豆久久| 亚洲美女久久| 午夜精品婷婷| 亚洲性视频h| 国产乱子精品一区二区在线观看| 亚洲成人日韩| 国产v综合v| 麻豆精品久久| 国产精品成人**免费视频| 鲁大师影院一区二区三区| 免费在线亚洲欧美| 日韩av资源网| 日韩高清欧美激情| 免费看欧美美女黄的网站| 伊人精品视频| 亚洲综合不卡| 亚洲欧美日韩国产综合精品二区 | 欧美一级二级三级视频| 日本欧美在线看| 亚洲日本久久| 中文字幕系列一区| 国产精品日本一区二区三区在线| 亚洲三级av| 青青草91久久久久久久久| 亚洲人妖在线| 亚洲一二三区视频| 日韩视频1区| 国产精品最新| 欧美国产专区| 亚洲综合电影| 精精国产xxxx视频在线播放 | 亚洲精品一区二区妖精| 精品中文一区| 少妇精品在线| 久久爱www成人| 免费在线播放第一区高清av| 国产一区二区三区免费在线| 高清av一区| 免费观看久久av| 日韩精品亚洲专区在线观看|