Springboot集成Mybatis-Flex的示例詳解
1 Mybatis-Flex 介紹
1.1簡介
Mybatis-Flex 是一個優(yōu)雅的 Mybatis 增強框架,它非常輕量、同時擁有極高的性能與靈活性。我們可以輕松的使用 Mybaits-Flex 鏈接任何數(shù)據(jù)庫,其內(nèi)置的 QueryWrapper 亮點幫助我們極大的減少了 SQL 編寫的工作的同時,減少出錯的可能性。
1.2特征
1.輕量:除了 MyBatis,沒有任何第三方依賴、沒有任何攔截器,在執(zhí)行的過程中,沒有任何的 Sql 解析(Parse)。 這帶來了幾個好處:極高的性能、極易對代碼進行跟蹤和調(diào)試、把控性更高。
2.靈活:支持 Entity 的增刪改查、以及分頁查詢的同時,MyBatis-Flex 提供了 Db + Row 工具,可以無需實體類對數(shù)據(jù)庫進行增刪改查以及分頁查詢。 與此同時,MyBatis-Flex 內(nèi)置的 QueryWrapper 可以輕易的幫助我們實現(xiàn) 多表查詢、鏈接查詢、子查詢 等等常見的 SQL 場景。
3.強大:支持任意關(guān)系型數(shù)據(jù)庫,還可以通過方言持續(xù)擴展,同時支持 多(復(fù)合)主鍵、邏輯刪除、樂觀鎖配置、數(shù)據(jù)脫敏、數(shù)據(jù)審計、 數(shù)據(jù)填充 等等功能。
簡單來說,Mybatis-Flex 相比 Mybatis-Plus 等框架 速度更快、功能更多、代碼更簡潔~
1.3Mybatis-Flex和同類框架對比
1)功能對比:
2)性能對比:
這里直接貼測試結(jié)果:
MyBatis-Flex 的查詢單條數(shù)據(jù)的速度,大概是 MyBatis-Plus 的 5 ~ 10+ 倍。
MyBatis-Flex 的查詢 10 條數(shù)據(jù)的速度,大概是 MyBatis-Plus 的 5~10 倍左右。
Mybatis-Flex 的分頁查詢速度,大概是 Mybatis-Plus 的 5~10 倍左右。
Mybatis-Flex 的數(shù)據(jù)更新速度,大概是 Mybatis-Plus 的 5~10+ 倍。
2 準備工作
官方文檔:快速開始 - MyBatis-Flex
以 Spring Boot + Maven + Mysql 項目做演示
2.1 數(shù)據(jù)庫中創(chuàng)建表及插入數(shù)據(jù)
此處省略~
2.2 Spring Boot 項目初始化
此時需要創(chuàng)建 Spring Boot 項目,并添加 Maven 依賴;此處我通過 IDEA 使用 Spring Initializer 快速初始化一個 Spring Boot 工程。
項目創(chuàng)建省略~
2.3 添加 Maven 主要依賴
往 pom.xml 文件中添加以下依賴。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>com.mybatis-flex</groupId> <artifactId>mybatis-flex-spring-boot-starter</artifactId> <version>1.7.3</version> </dependency> <dependency> <groupId>com.mybatis-flex</groupId> <artifactId>mybatis-flex-processor</artifactId> <version>1.7.3</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!--++++++++++++++++++++++++++++++++++++++++++++++++++++--> <!-- MyBatis分頁插件 --> <!--++++++++++++++++++++++++++++++++++++++++++++++++++++--> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.3.0</version> <optional>true</optional> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.12.0</version> </dependency>
2.4 配置數(shù)據(jù)源
在 application.properties 或 application.yml 中配置數(shù)據(jù)源:
server.port=8999 spring.application.name=mybatisPlus spring.datasource.username=root spring.datasource.password=root3306 spring.datasource.url=jdbc:mysql://127.0.0.1:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
3 Mybatis-Flex 實踐
3.1 編寫實體類和 Mapper 接口
?? User 實體類
- 使用 @Table("flex_user") 設(shè)置實體類與表名的映射關(guān)系
- 使用 @Id(keyType = KeyType.Auto) 標識主鍵為自增
package com.mybatisflex.flex.domain; import com.mybatisflex.annotation.Column; import com.mybatisflex.annotation.Table; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import java.io.Serializable; import java.util.Date; /** */ @Data @Builder @NoArgsConstructor @AllArgsConstructor //使用 @Table("tb_account") 設(shè)置實體類與表名的映射關(guān)系 @Table("user") public class User implements Serializable { private Integer id; @Column(value = "name") private String name; @Column(value = "age") private Integer age; @Column(value = "email") private String email; @Column(value = "create_time", onInsertValue = "now()") private Date createTime; @Column(value = "update_time", onUpdateValue = "now()") private Date updateTime; @Column(value = "del_flag") private int delFlag; @Column(value = "dept_code") private String deptCode; }
?? Mapper 接口繼承 BaseMapper 接口
package com.mybatisflex.flex.mapper; import com.mybatisflex.core.BaseMapper; import com.mybatisflex.flex.domain.User; /** * */ public interface UserMapper extends BaseMapper<User> { }
3.2 在主啟動類添加 @MapperScan 注解
用于掃描 Mapper 文件夾:
package com.mybatisflex.flex; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @MapperScan("com.mybatisflex.flex.mapper") public class FlexApplication { public static void main(String[] args) { SpringApplication.run(FlexApplication.class, args); } }
3.3 創(chuàng)建service
package com.mybatisflex.flex.service; import com.mybatisflex.core.query.QueryWrapper; import com.mybatisflex.core.update.UpdateChain; import com.mybatisflex.flex.domain.User; import com.mybatisflex.flex.domain.UserDto; import com.mybatisflex.flex.domain.table.SysDeptTableDef; import com.mybatisflex.flex.domain.table.UserTableDef; import com.mybatisflex.flex.mapper.UserMapper; import com.mybatisflex.spring.service.impl.ServiceImpl; import org.springframework.stereotype.Service; import java.util.List; /** * @Description: * @Date Create in 10:39 2023/11/22 * @Modified By: */ @Service public class UserService extends ServiceImpl<UserMapper, User> { /** * 查詢?nèi)? * @return */ public List<User> selectAll(){ return this.getMapper().selectAll(); } public List<User> selectList(){ QueryWrapper wrapper = QueryWrapper.create() // 這里可以指定查詢字段 .select() // sql from表名 .from(User.class) .where(User::getName).like("徐") .or(UserTableDef.USER.ID.in(2,3).and(UserTableDef.USER.NAME.like("o"))); return this.getMapper().selectListByQuery(wrapper); } /** * 根據(jù)userId獲取User數(shù)據(jù) * @param userId * @return */ public User listById(Integer userId){ QueryWrapper wrapper = QueryWrapper.create() // 這里可以指定查詢字段 .select() // sql from表名 .from(User.class) .where(User::getId).eq(userId).and(User::getName).like("徐"); return this.getMapper().selectOneByQuery(wrapper); } /** * 關(guān)聯(lián)查詢--鏈式查詢 */ public List<UserDto> getInfo(Integer userId){ QueryWrapper query = QueryWrapper.create() .select(UserTableDef.USER.ALL_COLUMNS) .select(SysDeptTableDef.SYS_DEPT.DEPT_NAME) .from(UserTableDef.USER).as("u") .leftJoin(SysDeptTableDef.SYS_DEPT).as("d").on(UserTableDef.USER.DEPT_CODE.eq(SysDeptTableDef.SYS_DEPT.DEPT_CODE)) .where(UserTableDef.USER.ID.eq(userId)); return this.getMapper().selectListByQueryAs(query,UserDto.class); } /** * 新增 * @param user */ public void insert(User user){ this.getMapper().insert(user); } /** * 更新User * @param user */ public void updateEntity(User user){ this.getMapper().update(user); } /** * 局部更新 * @param userId * @param userName */ public void updateRow(Integer userId, String userName){ UpdateChain.of(User.class) .set(User::getName, userName) .where(User::getId).eq(userId).update(); } /** * 刪除 * @param userName */ public void deleteByWrapper(String userName){ QueryWrapper queryWrapper = QueryWrapper.create().where(User::getName).eq(userName); this.getMapper().deleteByQuery(queryWrapper); } }
3.4創(chuàng)建Controller接口測試
package com.mybatisflex.flex.controller; import com.mybatisflex.flex.domain.User; import com.mybatisflex.flex.domain.UserDto; import com.mybatisflex.flex.page.TableDataInfo; import com.mybatisflex.flex.service.UserService; import com.mybatisflex.flex.utils.ResponseUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; import java.util.List; /** * @Author: best_liu * @Description: * @Date Create in 10:33 2023/11/22 * @Modified By: */ @RestController @RequestMapping("/user") public class SysUserController { @Resource private UserService userService; /** * 查詢?nèi)? * @return */ @GetMapping("listall") public List<User> listall(){ return userService.selectAll(); } /** * 分頁查詢 * @return **/ @GetMapping("/page") public TableDataInfo findPage() { ResponseUtils.startPage(); return ResponseUtils.getDataTable(userService.selectAll()); } /** * 按條件查詢 * @return */ @GetMapping("getList") public List<User> selectList(){ return userService.selectList(); } /** * 按userId查詢 * @return */ @GetMapping("listById") public User listById(){ return userService.listById(0); } /** * 按userId關(guān)聯(lián)查詢部門 * @return */ @GetMapping("getInfo") public List<UserDto> getInfo(){ return userService.getInfo(0); } /** * 新增 * @return */ @GetMapping("insert") public Boolean insert(){ User user = User.builder().id(10).name("張三").age(100).email("zhangsan@163.com").build(); userService.insert(user); return Boolean.TRUE; } /** * 更新 * @return */ @GetMapping("update") public Boolean update(){ userService.updateRow(10, "張三三"); return Boolean.TRUE; } /** * 刪除 * @return */ @GetMapping("delete") public Boolean delete(){ userService.deleteByWrapper("張三三"); return Boolean.TRUE; } }
4 鏈式查詢
若想使用鏈式查詢還得需要 APT 配置,MyBatis-Flex 使用了 APT(Annotation Processing Tool)技術(shù),在項目編譯的時候,會自動根據(jù) Entity/pojo 類定義的字段幫你生成 "USER" 類(可用于鏈式查詢)
通過開發(fā)工具構(gòu)建項目(如下圖),或者執(zhí)行 maven 編譯命令: mvn clean package 都可以自動生成。
正常情況下,會在 target 包下生成如下資源
若生成該資源并導(dǎo)入成功,那么此時,可使用鏈式查詢
/** * 關(guān)聯(lián)查詢 */ public List<UserDto> getInfo(Integer userId){ QueryWrapper query = QueryWrapper.create() .select(UserTableDef.USER.ALL_COLUMNS) .select(SysDeptTableDef.SYS_DEPT.DEPT_NAME) .from(UserTableDef.USER).as("u") .leftJoin(SysDeptTableDef.SYS_DEPT).as("d").on(UserTableDef.USER.DEPT_CODE.eq(SysDeptTableDef.SYS_DEPT.DEPT_CODE)) .where(UserTableDef.USER.ID.eq(userId)); return this.getMapper().selectListByQueryAs(query,UserDto.class); }
總的來說,MyBatis-Flex 的鏈式查詢相比 MyBatis-Plus 多了一步配置環(huán)節(jié),目前來看其他步驟類似。
5 MyBatis-Flex/Plus 代碼對比
接下來看一下MyBatis-Flex 和 MyBatis-Plus 各部分功能代碼的差別,Employee、Account、Article 都是實體類。
5.1 基礎(chǔ)查詢
MyBatis-Flex:
QueryWrapper query = QueryWrapper.create() .where(EMPLOYEE.LAST_NAME.like(searchWord)) //條件為null時自動忽略 .and(EMPLOYEE.GENDER.eq(1)) .and(EMPLOYEE.AGE.gt(24)); List<Employee> employees = employeeMapper.selectListByQuery(query);
MyBatis-Plus:
QueryWrapper<Employee> queryWrapper = Wrappers.query() .like(searchWord != null, "last_name", searchWord) .eq("gender", 1) .gt("age", 24); List<Employee> employees = employeeMapper.selectList(queryWrapper); //lambda 寫法: LambdaQueryWrapper<Employee> queryWrapper = Wrappers.<Employee>lambdaQuery() .like(StringUtils.isNotEmpty(searchWord), Employee::getUserName,"B") .eq(Employee::getGender, 1) .gt(Employee::getAge, 24); List<Employee> employees = employeeMapper.selectList(queryWrapper);
5.2 集合查詢
MyBatis-Flex:
QueryWrapper query = QueryWrapper.create() .select( ACCOUNT.ID, ACCOUNT.USER_NAME, max(ACCOUNT.BIRTHDAY), avg(ACCOUNT.SEX).as("sex_avg") ); List<Employee> employees = employeeMapper.selectListByQuery(query);
MyBatis-Plus:
QueryWrapper<Employee> queryWrapper = Wrappers.query() .select( "id", "user_name", "max(birthday)", "avg(birthday) as sex_avg" ); List<Employee> employees = employeeMapper.selectList(queryWrapper);
缺點:字段硬編碼,容易拼錯。無法使用 IDE 的字段進行重構(gòu),無法使用 IDE 自動提示,發(fā)生錯誤不能及時發(fā)現(xiàn),不過MyBatis-Plus的 lambdawrapper 也是能解決這個問題。
5.3 and(...) 和 or(...)
假設(shè)我們要構(gòu)建如下的 SQL 進行查詢(需要在 SQL 中添加括號)。
SELECT * FROM tb_account WHERE id >= 100 AND (sex = 1 OR sex = 2) OR (age IN (18,19,20) AND user_name LIKE "%michael%" )
MyBatis-Flex:
QueryWrapper query = QueryWrapper.create() .where(ACCOUNT.ID.ge(100)) .and(ACCOUNT.SEX.eq(1).or(ACCOUNT.SEX.eq(2))) .or(ACCOUNT.AGE.in(18, 19, 20).and(ACCOUNT.USER_NAME.like("michael")));
MyBatis-Plus:
QueryWrapper<Employee> query = Wrappers.query() .ge("id", 100) .and(i -> i.eq("sex", 1).or(x -> x.eq("sex", 2))) .or(i -> i.in("age", 18, 19, 20).like("user_name", "michael")); // or lambda LambdaQueryWrapper<Employee> query = Wrappers.<Employee>lambdaQuery() .ge(Employee::getId, 100) .and(i -> i.eq(Employee::getSex, 1).or(x -> x.eq(Employee::getSex, 2))) .or(i -> i.in(Employee::getAge, 18, 19, 20).like(Employee::getUserName, "michael"));
5.4 多表查詢
MyBatis-Flex:
QueryWrapper query = QueryWrapper.create() .select().from(ACCOUNT) .leftJoin(ARTICLE).on(ACCOUNT.ID.eq(ARTICLE.ACCOUNT_ID)) .where(ACCOUNT.AGE.ge(10)); List<Account> accounts = mapper.selectListByQuery(query);
QueryWrapper query = new QueryWrapper() .select( ACCOUNT.ID , ACCOUNT.USER_NAME , ARTICLE.ID.as("articleId") , ARTICLE.TITLE) .from(ACCOUNT.as("a"), ARTICLE.as("b")) .where(ACCOUNT.ID.eq(ARTICLE.ACCOUNT_ID));
MyBatis-Plus:不支持
5.5 部分字段更新
假設(shè)一個實體類 Account 中,我們要更新其內(nèi)容如下:
- userName 為 "michael"
- age 為 "18"
- birthday 為 null
其他字段保持數(shù)據(jù)庫原有內(nèi)容不變,要求執(zhí)行的 SQL 如下:
update tb_account set user_name = "michael", age = 18, birthday = null where id = 100
MyBatis-Flex 代碼如下:
Account account = UpdateEntity.of(Account.class); account.setId(100); //設(shè)置主鍵 account.setUserName("michael"); account.setAge(18); account.setBirthday(null); accountMapper.update(account);
MyBatis-Plus 代碼如下(或可使用 MyBatis-Plus 的 LambdaUpdateWrapper,但性能沒有 UpdateWrapper 好):
UpdateWrapper<Account> updateWrapper = new UpdateWrapper<>(); updateWrapper.eq("id", 100); updateWrapper.set("user_name", "michael"); updateWrapper.set("age", 18); updateWrapper.set("birthday", null); accountMapper.update(null, updateWrapper);
如上,MyBatis-Flex 在代碼編寫來說更加靈活,編寫方式更多一些,還是有些優(yōu)勢。
6 mybatis-flex使用多租戶
MyBatis-Flex 使用多租戶需要 2 個步驟:
- step 1:通過
@Column(tenantId = true)
標識租戶列。 - step 2:為
TenantManager
配置TenantFactory
。
TenantFactory 是用于生產(chǎn)租戶ID的,或者說是用于獲取當前租戶ID的。
以下是代碼示例:
@Table("tb_article") public class Article { @Id(keyType = KeyType.Auto) private Long id; @Column(tenantId = true) private Long tenantId; }
通過 @Column(tenantId = true)
表示租戶ID。
6.1 創(chuàng)建MyTenantFactory類
import com.mybatisflex.core.tenant.TenantFactory; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; /** * @Author: * @Description: 當然,MyTenantFactory 需要正常工作,我們需要在 Spring 攔截器里,需要通過 request 去獲取當前的租戶 ID,并設(shè)置到 request 的 attribute * @Date Create in 15:24 2023/11/24 * @Modified By: */ public class MyTenantFactory implements TenantFactory { @Override public Object[] getTenantIds(){ RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); // Long tenantId = (Long) attributes.getAttribute("tenantId", RequestAttributes.SCOPE_REQUEST); Long tenantId = 1L; return new Object[]{tenantId}; } }
6.2 創(chuàng)建MyConfiguration類,注入MyTenantFactory
import com.mybatisflex.core.tenant.TenantFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @Author: best_liu * @Description: * @Date Create in 15:25 2023/11/24 * @Modified By: */ @Configuration public class MyConfiguration { @Bean public TenantFactory tenantFactory(){ TenantFactory tenantFactory = new MyTenantFactory(); return tenantFactory; } }
6.3 實現(xiàn)ConfigurationCustomizer接口的customize()自定義方法
import com.mybatisflex.core.FlexGlobalConfig; import com.mybatisflex.core.mybatis.FlexConfiguration; import com.mybatisflex.spring.boot.ConfigurationCustomizer; import org.apache.ibatis.logging.stdout.StdOutImpl; import org.springframework.context.annotation.Configuration; /** * @Author: best_liu * @Description: Mybatis-flex sql日志打印 * @Date Create in 15:30 2023/11/24 * @Modified By: */ @Configuration public class MyConfigurationCustomizer implements ConfigurationCustomizer { /** * 自定義的FlexConfiguration配置 * @param configuration */ @Override public void customize(FlexConfiguration configuration) { //在 MyBatis-Flex 中,可以使用 FlexGlobalConfig 在 MyBatis-Flex 啟動之前,指定項目中的多租戶列的列名。 FlexGlobalConfig.getDefaultConfig().setTenantColumn("tenant_id"); //全局配置主鍵生成策略 FlexGlobalConfig.KeyConfig keyConfig = new FlexGlobalConfig.KeyConfig(); keyConfig.setKeyType(KeyType.Generator); keyConfig.setValue(KeyGenerators.snowFlakeId); keyConfig.setBefore(true); //sql日志打印 configuration.setLogImpl(StdOutImpl.class); } }
到此這篇關(guān)于Springboot集成Mybatis-Flex的示例詳解的文章就介紹到這了,更多相關(guān)Springboot集成Mybatis-Flex內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- MyBatis-Flex實現(xiàn)分頁查詢的示例代碼
- Mybatis-flex整合達夢數(shù)據(jù)庫的實現(xiàn)示例
- Spring Boot整合MyBatis-Flex全過程
- SpringBoot使用MyBatis-Flex實現(xiàn)靈活的數(shù)據(jù)庫訪問
- mybatis-flex實現(xiàn)鏈式操作的示例代碼
- mybatis-flex實現(xiàn)多數(shù)據(jù)源操作
- MyBatis-Flex實現(xiàn)多表聯(lián)查(自動映射)
- mybatis-flex與springBoot整合的實現(xiàn)示例
- MyBatis-Flex 邏輯刪除的用法小結(jié)
相關(guān)文章
關(guān)于Spring多數(shù)據(jù)源TransactionManager沖突的解決方案
這篇文章主要介紹了關(guān)于Spring多數(shù)據(jù)源TransactionManager沖突的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-07-07SpringBoot讀取properties文件配置項過程解析
這篇文章主要介紹了SpringBoot讀取properties文件配置項過程解析,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-06-06