Spring boot,MyBatis-Plus - 深入進階

 2023-10-06 阅读 38 评论 0

摘要:DROP TABLE IF EXISTS `user`; CREATE TABLE `user` (`id` bigint(20) NULL DEFAULT NULL COMMENT '主鍵',`name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '姓名',`age`

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (`id` bigint(20) NULL DEFAULT NULL COMMENT '主鍵',`name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '姓名',`age` int(11) NULL DEFAULT NULL COMMENT '年齡',`email` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '郵箱',`manager_id` bigint(20) NULL DEFAULT NULL COMMENT '直屬上級id',`create_time` datetime(0) NULL DEFAULT NULL COMMENT '創建時間',`update_time` datetime(0) NULL DEFAULT NULL COMMENT '修改時間',`version` int(11) NULL DEFAULT 1 COMMENT '版本',`deleted` int(1) NULL DEFAULT 0 COMMENT '邏輯刪除標識(0,未刪除;1,已刪除)'
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
INSERT INTO `user` VALUES (1234, '大boss', 40, 'boss@163.com', NULL, '2019-10-02 10:08:02', '2019-10-02 10:08:05', 1, 0);
INSERT INTO `user` VALUES (2345, '王天風', 25, 'wtf@163.com', 1234, '2019-10-02 10:09:07', '2019-10-02 10:09:10', 1, 0);
INSERT INTO `user` VALUES (2346, '李藝偉', 28, 'lyw@163.com', 2345, '2019-10-02 10:10:09', '2019-10-02 10:10:12', 1, 0);
INSERT INTO `user` VALUES (3456, '張雨綺', 31, 'zyq@163.com', 2345, '2019-10-02 10:10:54', '2019-10-02 10:10:58', 1, 0);
INSERT INTO `user` VALUES (4566, '劉雨紅', 32, 'lyh@163.com', 2345, '2019-10-02 10:11:51', '2019-10-02 10:11:55', 1, 0);
SET FOREIGN_KEY_CHECKS = 1;

邏輯刪除

設定邏輯刪除規則

在配置文件中配置邏輯刪除和邏輯未刪除的值

mybatis-plus:global-config:logic-not-delete-value: 0logic-delete-value: 1

在pojo類中在邏輯刪除的字段加注解@TableLogic

@Data
@EqualsAndHashCode(callSuper = false)
public class User extends Model<User> {@TableId(type = IdType.AUTO)private Long id;@TableField(condition = SqlCondition.LIKE)private String name;private Integer age;private String email;private Long managerId;private LocalDateTime createTime;private LocalDateTime updateTime;private Integer version;@TableLogicprivate Integer deleted;
}

測試

通過id邏輯刪除

@Test
public void deleteById(){userMapper.deleteById(4566L);
}

查詢中排除刪除標識字段及注意事項

邏輯刪除字段只是為了標識數據是否被邏輯刪除,在查詢的時候,并不想也將該字段查詢出來。

我們只需要在delete字段上增加@TableField(select = false)mybatisplus在查詢的時候就會自動忽略該字段。

@Test
public void selectIgnoreDeleteTest(){userMapper.selectById(3456L);
}

其他測試

package com.dsf.mp.fakeDelete.dao;import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.dsf.mp.fakeDelete.FakeDeleteApp;
import com.dsf.mp.fakeDelete.entity.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.time.format.DateTimeFormatter;
import java.util.List;@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@RunWith(SpringRunner.class)
@SpringBootTest(classes = FakeDeleteApp.class)
public class UserMapperTest {@AutowiredUserMapper userMapper;@Testpublic void insert() {User user = new User();user.setName("劉明強");user.setAge(31);user.setManagerId(1088248166370832385L);user.setEmail("lmq@baomidou.com");int row = userMapper.insert(user);System.out.println("影響記錄數:"+row);}@Testpublic void deleteById(){int rows = userMapper.deleteById(1171357729945505793L);System.out.println("影響行數:"+rows);}@Testpublic void selectById(){User user = userMapper.selectById(1171357729945505793L);System.out.println(user);}@Testpublic void updateById(){User user = new User();user.setAge(32);user.setId(1171357729945505793L);int rows = userMapper.updateById(user);System.out.println("影響行數:"+rows);}@Testpublic void mySelectList(){List<User> list = userMapper.mySelectList(Wrappers.<User>lambdaQuery()
//                .select(User.class,x->!x.getColumn().equals("deleted"))   //方式1.select(User.class,x->!x.isLogicDelete())   //方式2.gt(User::getAge, 25).eq(User::getDeleted,0));list.forEach(System.out::println);}
}
  • 自定義sql,MybatisPlus不會忽略deleted屬性,需要我們手動忽略。

自動填充

MybaitsPlus在我們插入數據或者更新數據的時候,為我們提供了自動填充功能。類似MySQL提供的默認值一樣。

如果我們需要使用自動填充功能,我們需要在實體類的相應屬性上加@TableField注解,并指定什么時候進行自動填充。mybatisPlus為我們提供了三種填充時機,在FieldFill枚舉中。

public enum FieldFill {/*** 默認不處理*/DEFAULT,/*** 插入時填充字段*/INSERT,/*** 更新時填充字段*/UPDATE,/*** 插入和更新時填充字段*/INSERT_UPDATE
}

設置好之后,我們還需要編寫具體的填充規則,具體是編寫一個填充類并交給Spring管理,然后實現MetaObjectHandler接口中的insertFillupdateFill方法。

Eg

  1. 插入User對象的時候自動填充插入時間,更新User對象的時候自動填充更新時間。
  • 指定實體類中需要自動填充的字段,并設置填充時機。
@Data
@EqualsAndHashCode(callSuper = false)
public class User extends Model<User> {...@TableField(fill = INSERT)private LocalDateTime createTime;@TableField(fill = UPDATE)private LocalDateTime updateTime;...
}
  • 編寫填充規則
@Component
public class MyMetaObjHandler implements MetaObjectHandler {@Overridepublic void insertFill(MetaObject metaObject) {if(metaObject.hasSetter("createTime")){setInsertFieldValByName("createTime", LocalDateTime.now(),metaObject);}}@Overridepublic void updateFill(MetaObject metaObject) {if(metaObject.hasSetter("updateTime")){setUpdateFieldValByName("updateTime",LocalDateTime.now(),metaObject);}}
}

解釋一下為什么要用if判斷是否有對應的屬性

mybatisPlus在執行插入或者更新操作的時候,每次都會執行該方法,有些表中是沒有設置自動填充字段的,而且有些自動填充字段的值的獲取比較消耗系統性能,所以為了不必要的消耗,進行if判斷,決定是否需要填充。

有些時候我們已經設置了屬性的值。不想讓mybatisPlus再自動填充,也就是說我們沒有設置屬性的值,mybatisPlus進行填充,如果設置了那么就用我們設置的值。這種情況我們只需要在填充類中提前獲取默認值,然后使用該默認值就可以了。

@Override
public void updateFill(MetaObject metaObject) {if(metaObject.hasSetter("updateTime")){Object updateTime = getFieldValByName("updateTime", metaObject);if(Objects.nonNull(updateTime)){setUpdateFieldValByName("updateTime",updateTime,metaObject);}else{setUpdateFieldValByName("updateTime",LocalDateTime.now(),metaObject);}}
}

對?@TableField(fill = FieldFill.INSERT_UPDATE) 理解

并不是有了這個注解就能進到MyMetaObjHandler,更不是注解了1個字段進去1次,注解了2個字段進去2次,并不是的,只要我們實現了MyMetaObjHandler就都可以進去的,而且一次UPDATE OR INSERT操作只會進一次;只是是否對該字段擁有寫入權限的問題,只要被標注,就會在自定義的MyMetaObjHandler里進行對該字段有寫入權限,否則哪怕set了,也沒效果。

樂觀鎖

樂觀鎖適用于讀多寫少的情況,更新數據的時候不使用“鎖“而是使用版本號來判斷是否可以更新數據。通過不加鎖來減小數據更新時間和系統的性能消耗,進而提高數據庫的吞吐量。CAS機制就是一種典型的樂觀鎖的形式。

樂觀鎖是邏輯存在的一種概念,我們如果使用樂觀鎖需要手動在表的加上version字段。

mysql使用樂觀鎖偽代碼示例:

update user 
set balabala....
where balabala... and version = xxx

配置類中注入樂觀鎖插件

@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor(){return new OptimisticLockerInterceptor();
}

實體類中的版本字段增加@version注解

@Data
@EqualsAndHashCode(callSuper = false)
public class User extends Model<User> {...@Versionprivate Integer version;...
}

測試:更新王天風的年齡

@Test
public void testLock(){int version = 1;User user = new User();user.setEmail("wtf@163.com");user.setAge(34);user.setId(2345L);user.setManagerId(1234L);user.setVersion(1);userMapper.updateById(user);}

數據庫中的version已經變成2

/*** 在 update(entity, wrapper) 方法下, wrapper 不能復用!!!* 原因:*   每應用一次QueryWrapper,都會自動為where子句添加一次version=?,兩次應用就會添加兩次,*   出現形如“where ... and version=2 and version=3”的情況* 解決:*   重新創建一個QueryWrapper對象*/
@Test
public void update() throws InterruptedException {User user = userMapper.selectById(1171357729945505793L);user.setAge(34);LambdaQueryWrapper<User> query = Wrappers.<User>lambdaQuery().eq(User::getId, user.getId());int rows1 = userMapper.update(user,query);//第一次用querySystem.out.println("影響行數:"+rows1);user = userMapper.selectById(1171357729945505793L);user.setAge(35);int rows2 = userMapper.update(user,query);//復用querySystem.out.println("影響行數:"+rows2);
}

注意事項

  1. 支持的類型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime
  2. 整數類型下 newVerison = oldVersion+1
  3. newVersion會寫到entity中
  4. 僅支持updateById(id)與update(entity,wrapper)方法
  5. 在update(entiry,wrapper)方法下,wrapper不能復用

性能分析

配置類中注入性能分析插件

@Bean
// @Profile({"dev,test"})
public PerformanceInterceptor performanceInterceptor() {PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();// 格式化sql輸出performanceInterceptor.setFormat(true);// 設置sql執行最大時間,單位(ms)performanceInterceptor.setMaxTime(5L);return performanceInterceptor;
}

執行sql就可以打印sql執行的信息了

  • 開啟性能分析會消耗系統的性能,所以性能分析插件要配合@Profile注解執行使用的環境。

依靠第三方插件美化 SQL 輸出

https://mp.baomidou.com/guide/p6spy.html

第三方依賴

<dependency><groupId>p6spy</groupId><artifactId>p6spy</artifactId><version>3.8.5</version>
</dependency>

更改配置文件中的dirver和url

spring:datasource:
#    driver-class-name: com.mysql.cj.jdbc.Driverusername: rootpassword: root
#    url: jdbc:mysql://localhost:3306/test?serverTimezone=CTT&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=truedriver-class-name: com.p6spy.engine.spy.P6SpyDriverurl: jdbc:p6spy:mysql://localhost:3306/test?serverTimezone=CTT&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true

增加spy.properties配置文件

module.log=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory
# 自定義日志打印
logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger
#日志輸出到控制臺
appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger
# 使用日志系統記錄 sql
#appender=com.p6spy.engine.spy.appender.Slf4JLogger
# 設置 p6spy driver 代理
deregisterdrivers=true
# 取消JDBC URL前綴
useprefix=true
# 配置記錄 Log 例外,可去掉的結果集有error,info,batch,debug,statement,commit,rollback,result,resultset.
excludecategories=info,debug,result,batch,resultset
# 日期格式
dateformat=yyyy-MM-dd HH:mm:ss
# 實際驅動可多個
#driverlist=org.h2.Driver
# 是否開啟慢SQL記錄
outagedetection=true
# 慢SQL記錄標準 2 秒
outagedetectioninterval=2

測試

打印到文件

  • 開啟美化SQL插件會消耗系統的性能,所以插件要配合@Profile注解執行使用的環境。

多租戶

常用情景:一條記錄操作者的自動賦值!

過濾方式:傳統過濾、注解過濾。

過濾維度:表級過濾、SQL語句過濾。

package com.dsf.mp.tenant.dao;import com.baomidou.mybatisplus.annotation.SqlParser;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.dsf.mp.tenant.entity.User;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;public interface UserMapper extends BaseMapper<User> {//通過注解的方式過濾掉自定義sql方法的sql解析,false-不過濾sql解析,true-過濾sql解析@SqlParser(filter = true)@Select("select * from user ${ew.customSqlSegment}")List<User> mySelectList(@Param(Constants.WRAPPER)Wrapper<User> wrapper);
}
package com.dsf.mp.tenant.configuration;import com.baomidou.mybatisplus.core.parser.ISqlParser;
import com.baomidou.mybatisplus.core.parser.ISqlParserFilter;
import com.baomidou.mybatisplus.core.parser.SqlParserHelper;
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.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.ArrayList;@Configuration
public class MybatisPlusConf {@BeanPaginationInterceptor paginationInterceptor(){PaginationInterceptor paginationInterceptor = new PaginationInterceptor();ArrayList<ISqlParser> sqlParsersList = new ArrayList<>();TenantSqlParser tenantSqlParser = new TenantSqlParser();tenantSqlParser.setTenantHandler(new TenantHandler() {/*** 租戶ID,通常指公司代碼或ID,獲取途徑包括:session、配置文件、枚舉類、cookie、redis* 這里的ID是寫死的,實際項目中要想辦法獲取*/@Overridepublic Expression getTenantId() {return new LongValue(1088248166370832385L);}/*** 指定租戶ID的表列名*/@Overridepublic String getTenantIdColumn() {return "manager_id";}/*** 過濾掉(即不引入)哪些表的多租戶功能* false-過濾,默認; true-不過濾;*/@Overridepublic boolean doTableFilter(String tableName) {if("user".equals(tableName)){return false;}return true;}});sqlParsersList.add(tenantSqlParser);paginationInterceptor.setSqlParserList(sqlParsersList);/*** 過濾掉(即不引入)哪些方法的sql解析(即不進行多租戶功能)*/paginationInterceptor.setSqlParserFilter(new ISqlParserFilter() {@Overridepublic boolean doFilter(MetaObject metaObject) {MappedStatement ms = SqlParserHelper.getMappedStatement(metaObject);if("com.dsf.mp.tenant.dao.UserMapper.selectById".equals(ms.getId())){return true;}return false;}});return paginationInterceptor;}
}
package com.dsf.mp.tenant;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
@MapperScan("com.dsf.mp.tenant.dao")
public class TenantApp {public static void main(String[] args) {SpringApplication.run(TenantApp.class, args);}
}
package com.dsf.mp.tenant.dao;import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.dsf.mp.tenant.TenantApp;
import com.dsf.mp.tenant.entity.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
import static org.junit.Assert.*;@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@RunWith(SpringRunner.class)
@SpringBootTest(classes = TenantApp.class)
public class UserMapperTest {@AutowiredUserMapper userMapper;/*** 測試多租戶查詢方法* 效果:where ... and manager_id=1088248166370832385*/@Testpublic void selectList(){List<User> list = userMapper.selectList(null);list.forEach(System.out::println);}/*** 測試多租戶更新方法*/@Testpublic void updateById(){User user = userMapper.selectById(1094592041087729666L);user.setAge(34);int rows = userMapper.updateById(user);System.out.println("受影響的行數:"+rows);}/*** 測試多租戶插入方法*/@Testpublic void insert(){User user = new User();user.setName("李國民");user.setAge(29);user.setEmail("lgm@baomidou.com");int rows = userMapper.insert(user);System.out.println("受影響的行數:"+rows);}/*** 測試過濾掉內置方法的多租戶功能*/@Testpublic void selectById(){User user = userMapper.selectById(1094590409767661570L);System.out.println(user);}/*** 測試過濾掉自定義方法的多租戶功能*/@Testpublic void mySelectList(){List<User> list = userMapper.mySelectList(Wrappers.<User>lambdaQuery().gt(User::getAge,30));list.forEach(System.out::println);}
}
# 配置數據源
spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://testhost:3306/mp?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghaiusername: rootpassword: '#Dsf135246'# 配置日志
logging:level:root: warncom.dsf.mp.tenant.dao: tracepattern:console: '%p%m%n'mybatis-plus:mapper-locations: ['classpath:/mapper/*Mapper.xml']
#  global-config.sql-parser-cache: true  #3.1.1以上版本不需要配置該項

動態表名

應用場景:同樣的表分表時的操作!

package com.dsf.mp.dynamicTableNameParser.entity;import lombok.Data;
import java.time.LocalDateTime;@Data
public class User {private Long id;private String name;private Integer age;private String email;private Long managerId;private LocalDateTime createTime;private  LocalDateTime updateTime;private  Integer version;private  Integer deleted;}package com.dsf.mp.dynamicTableNameParser.dao;import com.baomidou.mybatisplus.annotation.SqlParser;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.dsf.mp.dynamicTableNameParser.entity.User;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;public interface UserMapper extends BaseMapper<User> {// 通過注解的方式過濾掉自定義sql方法的sql解析,false-不過濾sql解析,true-過濾sql解析@SqlParser(filter = false)@Select("select * from user ${ew.customSqlSegment}")List<User> mySelectList(@Param(Constants.WRAPPER) Wrapper<User> wrapper);
}
package com.dsf.mp.dynamicTableNameParser.configuration;import com.baomidou.mybatisplus.core.parser.ISqlParser;
import com.baomidou.mybatisplus.core.parser.ISqlParserFilter;
import com.baomidou.mybatisplus.core.parser.SqlParserHelper;
import com.baomidou.mybatisplus.extension.parsers.DynamicTableNameParser;
import com.baomidou.mybatisplus.extension.parsers.ITableNameHandler;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;@Configuration
public class MybatisPlusConf {//user動態表存放對象public static ThreadLocal<String> myTableName=new ThreadLocal<>();@BeanPaginationInterceptor paginationInterceptor(){PaginationInterceptor paginationInterceptor = new PaginationInterceptor();ArrayList<ISqlParser> sqlParsersList = new ArrayList<>();//動態表名解析器Map<String, ITableNameHandler> tableNameHandlerMap = new HashMap<>();tableNameHandlerMap.put("user", new ITableNameHandler() {@Overridepublic String dynamicTableName(MetaObject metaObject, String sql, String tableName) {return myTableName.get();}});DynamicTableNameParser dynamicTableNameParser = new DynamicTableNameParser();dynamicTableNameParser.setTableNameHandlerMap(tableNameHandlerMap);sqlParsersList.add(dynamicTableNameParser);paginationInterceptor.setSqlParserList(sqlParsersList);/*** 過濾掉(即不引入)哪些方法的sql解析功能(即不進行動態表名替換)*/paginationInterceptor.setSqlParserFilter(new ISqlParserFilter() {@Overridepublic boolean doFilter(MetaObject metaObject) {MappedStatement ms = SqlParserHelper.getMappedStatement(metaObject);if("com.dsf.mp.dynamicTableNameParser.dao.UserMapper.selectById".equals(ms.getId())){return true;}return false;}});return paginationInterceptor;}
}
# 配置數據源
spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://testhost:3306/mp?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghaiusername: rootpassword: '#Dsf135246'# 配置日志
logging:level:root: warncom.dsf.mp.dynamicTableNameParser.dao: tracepattern:console: '%p%m%n'mybatis-plus:mapper-locations: ['classpath:/mapper/*Mapper.xml']
package com.dsf.mp.dynamicTableNameParser.dao;import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.dsf.mp.dynamicTableNameParser.DynamicTableNameParserApp;
import com.dsf.mp.dynamicTableNameParser.configuration.MybatisPlusConf;
import com.dsf.mp.dynamicTableNameParser.entity.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@RunWith(SpringRunner.class)
@SpringBootTest(classes = DynamicTableNameParserApp.class)
public class UserMapperTest {@AutowiredUserMapper userMapper;/*** 測試動態表名查詢方法*/@Testpublic void selectList(){MybatisPlusConf.myTableName.set("user_2019");List<User> list = userMapper.selectList(null);list.forEach(System.out::println);}/*** 測試動態表名更新方法*/@Testpublic void updateById(){MybatisPlusConf.myTableName.set("user_2019");User user = userMapper.selectById(1094592041087729666L);user.setAge(34);int rows = userMapper.updateById(user);System.out.println("受影響的行數:"+rows);}/*** 測試動態表名插入方法*/@Testpublic void insert(){MybatisPlusConf.myTableName.set("user_2019");User user = new User();user.setName("李國民");user.setAge(29);user.setEmail("lgm@baomidou.com");int rows = userMapper.insert(user);System.out.println("受影響的行數:"+rows);}/*** 測試過濾掉內置方法的動態表名*/@Testpublic void selectById(){MybatisPlusConf.myTableName.set("user_2019");User user = userMapper.selectById(1094590409767661570L);System.out.println(user);}/*** 測試過濾掉自定義方法的動態表名*/@Testpublic void mySelectList(){MybatisPlusConf.myTableName.set("user_2019");List<User> list = userMapper.mySelectList(Wrappers.<User>lambdaQuery().gt(User::getAge,30));list.forEach(System.out::println);}
}

SQL注入器:封裝自定義通用SQL

實現步驟

  1. 創建定義方法的類
  2. 創建注入器
  3. 在mapper中加入自定義方法
package com.dsf.mp.sqlInjector.entity;import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableLogic;
import lombok.Data;
import java.time.LocalDateTime;@Data
public class User {private Long id;private String name;@TableField(fill= FieldFill.UPDATE)private Integer age;private String email;private Long managerId;private LocalDateTime createTime;private  LocalDateTime updateTime;private  Integer version;@TableLogic@TableField(select=false)private  Integer deleted;
}
package com.dsf.mp.sqlInjector.component.method;import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;public class DeleteAllMethod extends AbstractMethod {@Overridepublic MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {//執行的sqlString sql = "delete from "+tableInfo.getTableName();//mapper接口的方法名String method = "deleteAll";SqlSource sqlSource = languageDriver.createSqlSource(configuration,sql,modelClass);return addDeleteMappedStatement(mapperClass,method,sqlSource);}
}
package com.dsf.mp.sqlInjector.dao;import com.baomidou.mybatisplus.annotation.SqlParser;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.dsf.mp.sqlInjector.entity.User;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;public interface UserMapper extends MyBaseMapper<User> {@SqlParser(filter = true)@Select("select * from user ${ew.customSqlSegment}")List<User> mySelectList(@Param(Constants.WRAPPER) Wrapper<User> wrapper);//如果只想在本接口中調用自定義的method可以直接寫到這里
//    int deleteAll();
}
package com.dsf.mp.sqlInjector.dao;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import org.apache.ibatis.annotations.Param;
import java.util.List;public interface MyBaseMapper<T> extends BaseMapper<T> {int deleteAll();int insertBatchSomeColumn(List<T> list);int deleteByIdWithFill(T t);int alwaysUpdateSomeColumnById(@Param((Constants.ENTITY)) T entity);
}
package com.dsf.mp.sqlInjector.component;import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
import com.baomidou.mybatisplus.extension.injector.methods.additional.AlwaysUpdateSomeColumnById;
import com.baomidou.mybatisplus.extension.injector.methods.additional.InsertBatchSomeColumn;
import com.baomidou.mybatisplus.extension.injector.methods.additional.LogicDeleteByIdWithFill;
import com.dsf.mp.sqlInjector.component.method.DeleteAllMethod;
import org.springframework.stereotype.Component;
import java.util.List;@Component
public class MySqlInjector extends DefaultSqlInjector {@Overridepublic List<AbstractMethod> getMethodList(Class<?> mapperClass) {//一定要調用super.getMethodList,否則內置的方法全都不能用了List<AbstractMethod> methodList = super.getMethodList(mapperClass);//添加自定的方法組件:刪除全部methodList.add(new DeleteAllMethod());//添加選裝件:批量插入
//        methodList.add(new InsertBatchSomeColumn(x->!x.isLogicDelete()));methodList.add(new InsertBatchSomeColumn(x->!x.isLogicDelete()&&!x.getColumn().equals("age")));//構造器中指定哪些字段插入,哪些不插入//添加選裝件:邏輯刪除并填充methodList.add(new LogicDeleteByIdWithFill());//添加選裝件:根據id更新固定的字段methodList.add(new AlwaysUpdateSomeColumnById(x->!x.getColumn().equals("name")));//構造器中指定哪些字段更新,哪些不更新return methodList;}
}
  • 除了自定義的類,還有一些MP官方自帶的選裝件。?
package com.dsf.mp.sqlInjector.dao;import com.dsf.mp.sqlInjector.SqlInjectorApp;
import com.dsf.mp.sqlInjector.entity.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.Arrays;
import java.util.List;
import static org.junit.Assert.*;@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@RunWith(SpringRunner.class)
@SpringBootTest(classes = SqlInjectorApp.class)
public class UserMapperTest {@AutowiredUserMapper userMapper;/*** 測試自定義方法組件:刪除全部*/@Testpublic void deleteAll(){int rows = userMapper.deleteAll();System.out.println("受影響的行數:"+rows);}/*** 測試選裝件:批量刪除* 如果插入時字段為null,則不管數據庫是否設置默認值,插入的數據均是null*/@Testpublic void insertBatchSomeColumn(){User user1 = new User();user1.setName("李興華");user1.setAge(34);user1.setManagerId(1088248166370832385L);User user2 = new User();user2.setName("楊紅");user2.setAge(29);user2.setManagerId(1088248166370832385L);List<User> asList= Arrays.asList(user1,user2);int rows = userMapper.insertBatchSomeColumn(asList);System.out.println("受影響的行數"+rows);}/*** 測試選裝件:邏輯刪除并填充*/@Testpublic void deleteByIdWithFill(){User user = userMapper.selectById(1171846939462836226L);
//        user.setAge(23);int rows = userMapper.deleteByIdWithFill(user);System.out.println("受影響的行數"+rows);}/*** 測試選裝件:根據id更新固定的字段*/@Testpublic void alwaysUpdateSomeColumnById(){User user = userMapper.selectById(1088248166370832385L);user.setAge(26);user.setName("王地縫");int rows = userMapper.alwaysUpdateSomeColumnById(user);System.out.println("受影響的行數"+rows);}}

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://808629.com/126637.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 86后生记录生活 Inc. 保留所有权利。

底部版权信息