MyBatis-Plus常用 API 全套教程
創(chuàng)建表
創(chuàng)建user表
DROP?TABLE?IF?EXISTS?user;
CREATE?TABLE?user
(
id?BIGINT(20) NOT?NULL?COMMENT?'主鍵ID',
name?VARCHAR(30) NULL?DEFAULT?NULL?COMMENT?'姓名',
age INT(11) NULL?DEFAULT?NULL?COMMENT?'年齡',
email VARCHAR(50) NULL?DEFAULT?NULL?COMMENT?'郵箱',
PRIMARY KEY?(id)
);
INSERT?INTO?user?(id, name, age, email) VALUES
(1, 'Jone', 18, '[email protected]'),
(2, 'Jack', 20, '[email protected]'),
(3, 'Tom', 28, '[email protected]'),
(4, 'Sandy', 21, '[email protected]'),
(5, 'Billie', 24, '[email protected]');注意:-- 真實(shí)開發(fā)中往往都會(huì)有這四個(gè)字段,version(樂觀鎖)、deleted(邏輯刪除)、gmt_create(創(chuàng)建時(shí)間)、gmt_modified(修改時(shí)間)
導(dǎo)入依賴
<dependency>
??<groupId>mysqlgroupId>
??<artifactId>mysql-connector-javaartifactId>
dependency>
<dependency>
??<groupId>org.projectlombokgroupId>
??<artifactId>lombokartifactId>
dependency>
<dependency>
??<groupId>com.baomidougroupId>
??<artifactId>mybatis-plus-boot-starterartifactId>
??<version>3.0.5version>
dependency>注意:盡量不要同時(shí)導(dǎo)入 mybatis 和 mybatis-plus!避免版本的差異造成無法預(yù)知的問題。
連接數(shù)據(jù)庫
創(chuàng)建application.yml
spring:
??profiles:
????active: dev
??datasource:
# 驅(qū)動(dòng)不同 mysql 5??com.mysql.jdbc.Driver
# mysql 8??com.mysql.cj.jdbc.Driver、需要增加時(shí)區(qū)的配置serverTimezone=GMT%2B8
????url:?jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
????driver-class-name: com.mysql.cj.jdbc.Driver
????username: root
????password: root業(yè)務(wù)代碼
實(shí)體類
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
??private?Long?id;
??private?String?name;
??private?Integer?age;
??private?String?email;
}mapper接口
import?com.baomidou.mybatisplus.core.mapper.BaseMapper;
import?com.kuang.pojo.User;
import?org.springframework.stereotype.Repository;
// 在對(duì)應(yīng)的Mapper上面繼承基本的類 BaseMapper
@Repository?// 代表持久層
public?interface?UserMapper?extends?BaseMapper<User> {
??// 所有的CRUD操作都已經(jīng)編寫完成了
}注意點(diǎn),我們需要在主啟動(dòng)類上去掃描我們的mapper包下的所有接口 @MapperScan(“com.kwhua.mapper”)
測(cè)試
@SpringBootTest
class?MybatisPlusApplicationTests?{
??// 繼承了BaseMapper,所有的方法都來自己父類
??// 我們也可以編寫自己的擴(kuò)展方法!
??@Autowired
??private?UserMapper userMapper;
??@Test
??void?contextLoads()?{
????// 參數(shù)是一個(gè) Wrapper ,條件構(gòu)造器,這里我們先設(shè)置條件為空,查詢所有。
????List users = userMapper.selectList(null);
????users.forEach(System.out::println);
?}
} 所有數(shù)據(jù)輸出

配置日志
我們所有的sql現(xiàn)在是不可見的,我們希望知道它是怎么執(zhí)行的,所有我們要配置日志的輸出 application.yml文件添加日志配置
#配置日志
mybatis-plus:
??configuration:
????log-impl: org.apache.ibatis.logging.stdout.StdOutImpl查看執(zhí)行sql的日志信息

插入操作
// 測(cè)試插入
????@Test
????public?void?testInsert(){
????????User user = new?User();
????????user.setName("kwhua_mybatis-plus_insertTest");
????????user.setAge(15);
????????user.setEmail("[email protected]");
????????int?result = userMapper.insert(user); // 幫我們自動(dòng)生成id
????????System.out.println(result); // 受影響的行數(shù)
????????System.out.println(user); // 看到id會(huì)自動(dòng)填充。}
看到id會(huì)自動(dòng)填充。數(shù)據(jù)庫插入的id的默認(rèn)值為:全局的唯一id
主鍵生成策略
1)主鍵自增 1、實(shí)體類字段上 @TableId(type = IdType.AUTO)
2、數(shù)據(jù)庫id字段設(shè)置為自增!

3、再次測(cè)試(可以看到id值比上次插入的大1)id的生成策略源碼解釋

public?enum?IdType?{
??AUTO(0), // 數(shù)據(jù)庫id自增
??NONE(1), // 未設(shè)置主鍵
??INPUT(2), // 手動(dòng)輸入
??ID_WORKER(3), // 默認(rèn)的方式,全局唯一id
??UUID(4), // 全局唯一id uuid
??ID_WORKER_STR(5); //ID_WORKER 字符串表示法
}以上不再逐一測(cè)試。
更新操作
@Test
public?void?testUpdate(){
????????User user = new?User();
????????// 通過條件自動(dòng)拼接動(dòng)態(tài)sql
????????user.setId(1302223874217295874L);
????????user.setName("kwhua_mybatis-plus_updateTest");
????????user.setAge(20);
????????// 注意:updateById 但是參數(shù)是一個(gè)對(duì)象!
????????int?i = userMapper.updateById(user);
????????System.out.println(i);
????}
自動(dòng)填充
創(chuàng)建時(shí)間、修改時(shí)間!這兩個(gè)字段操作都是自動(dòng)化完成的,我們不希望手動(dòng)更新!阿里巴巴開發(fā)手冊(cè):所有的數(shù)據(jù)庫表都要配置上gmt_create、gmt_modified!而且需要自動(dòng)化!
方式一:數(shù)據(jù)庫級(jí)別(工作中一般不用)
1、在表中新增字段 gmt_create, gmt_modified
2、把實(shí)體類同步
private?Date?gmtCreate;
private?Date?gmtModified;3、再次查看

方式二:代碼級(jí)別 1、刪除數(shù)據(jù)庫的默認(rèn)值、更新操作!
2、實(shí)體類字段屬性上需要增加注解
// 字段添加填充內(nèi)容
@TableField(fill = FieldFill.INSERT)
private?Date?gmt_create;
@TableField(fill = FieldFill.INSERT_UPDATE)
private?Date?gmt_modified;3、編寫處理器來處理這個(gè)注解即可!
@Slf4j
@Component?// 一定不要忘記把處理器加到IOC容器中!
public?class?MyMetaObjectHandler?implements?MetaObjectHandler?{
????// 插入時(shí)的填充策略
????@Override
????public?void?insertFill(MetaObject metaObject)?{
????????log.info("start insert fill.....");
????????// setFieldValByName(String fieldName, Object fieldVal, MetaObject metaObject
????????this.setFieldValByName("gmt_create",new?Date(),metaObject);
????????this.setFieldValByName("gmt_modified",new?Date(),metaObject);
????}
????// 更新時(shí)的填充策略
????@Override
????public?void?updateFill(MetaObject metaObject)?{
????????log.info("start update fill.....");
????????this.setFieldValByName("gmt_modified",new?Date(),metaObject);
????}
}4、測(cè)試插入和更新,檢查時(shí)間變化。
樂觀鎖
樂觀鎖 : 顧名思義,十分樂觀,它總是認(rèn)為不會(huì)出現(xiàn)問題,無論干什么不去上鎖!如果出現(xiàn)了問題, 再次更新值測(cè)試 悲觀鎖:顧名思義,十分悲觀,它總是認(rèn)為總是出現(xiàn)問題,無論干什么都會(huì)上鎖!再去操作!
樂觀鎖實(shí)現(xiàn)方式:
取出記錄時(shí),獲取當(dāng)前version 更新時(shí),帶上這個(gè)version 執(zhí)行更新時(shí), set version = newVersion where version = oldVersion 如果version不對(duì),就更新失敗
樂觀鎖:1、先查詢,獲得版本號(hào) version = 1
-- A
update?user?set?name?= "kwhua", version?= version?+ 1
where?id?= 2?and?version?= 1
-- B 線程搶先完成,這個(gè)時(shí)候 version = 2,會(huì)導(dǎo)致 A 修改失敗!
update?user?set?name?= "kwhua", version?= version?+ 1
where?id?= 2?and?version?= 1樂觀鎖測(cè)試
1、給數(shù)據(jù)庫中增加version字段!

2、實(shí)體類加對(duì)應(yīng)的字段
@Version?//樂觀鎖Version注解
private?Integer version;3、注冊(cè)組件
// 掃描我們的 mapper 文件夾
@MapperScan("com.kwhua.mapper")
@EnableTransactionManagement
@Configuration?// 配置類
public class MyBatisPlusConfig {
????// 注冊(cè)樂觀鎖插件
????@Bean
????public OptimisticLockerInterceptor optimisticLockerInterceptor() {
????????return?new?OptimisticLockerInterceptor();
????}
???}4、測(cè)試
// 測(cè)試樂觀鎖成功!
@Test
public?void?testOptimisticLocker(){
????????// 1、查詢用戶信息
????????User user = userMapper.selectById(1L);
????????// 2、修改用戶信息
????????user.setName("kwhua");
????????user.setEmail("[email protected]");
????????// 3、執(zhí)行更新操作
????????userMapper.updateById(user);
????}version字段已經(jīng)由1變成了2
// 測(cè)試樂觀鎖失敗!多線程下
????@Test
????public?void?testOptimisticLocker2(){
????????// 線程 1
????????User user = userMapper.selectById(1L);
????????user.setName("kwhua111");
????????user.setEmail("[email protected]");
????????// 模擬另外一個(gè)線程執(zhí)行了插隊(duì)操作
????????User user2 = userMapper.selectById(1L);
????????user2.setName("kwhua222");
????????user2.setEmail("[email protected]");
????????userMapper.updateById(user2);
????????// 自旋鎖來多次嘗試提交!
????????userMapper.updateById(user); // 如果沒有樂觀鎖就會(huì)覆蓋插隊(duì)線程的值!
????}
可以看到線程1執(zhí)行更新失敗
查詢操作
// 測(cè)試查詢
????@Test
????public?void?testSelectById(){
????????User user = userMapper.selectById(1L);
????????System.out.println(user);
????}
????// 測(cè)試批量查詢!
????@Test
????public?void?testSelectByBatchId(){
????????List users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
????????users.forEach(System.out::println);
????}
????// 按條件查詢之一使用map操作
????@Test
????public?void?testSelectByBatchIds(){
????????HashMap map?= new?HashMap<>();
????????// 自定義要查詢
????????map.put("name","kwhua");
????????map.put("age",15);
????????List users = userMapper.selectByMap(map);
????????users.forEach(System.out::println);
????} 1、配置攔截器組件
// 分頁插件
@Bean
public?PaginationInterceptor paginationInterceptor()?{
??return??new?PaginationInterceptor();
}2、直接使用Page對(duì)象即可!
// 測(cè)試分頁查詢
@Test
public?void?testPage(){
??// 參數(shù)一:當(dāng)前頁
??// 參數(shù)二:頁面大小
??Page page = new?Page<>(2,5);
??userMapper.selectPage(page,null);
??page.getRecords().forEach(System.out::println);
??System.out.println(page.getTotal());
} 物理刪除
// 測(cè)試刪除
????@Test
????public?void?testDeleteById(){
????????userMapper.deleteById(1L);
????}
????// 通過id批量刪除
????@Test
????public?void?testDeleteBatchId(){
????????userMapper.deleteBatchIds(Arrays.asList(2L,3L));
????}
????// 通過map刪除
????@Test
????public?void?testDeleteMap(){
????????HashMap map?= new?HashMap<>();
????????map.put("name","kwhua");
????????userMapper.deleteByMap(map);
????} 邏輯刪除
物理刪除 :從數(shù)據(jù)庫中直接移除 邏輯刪除 :在數(shù)據(jù)庫中沒有被移除,而是通過一個(gè)變量來讓它失效!deleted = 0 => deleted = 1 管理員可以查看被刪除的記錄!防止數(shù)據(jù)的丟失,類似于回收站!
1、在數(shù)據(jù)表中增加一個(gè) deleted 字段2、實(shí)體類中增加屬性
@TableLogic?//邏輯刪除
?private?Integer deleted;3、配置
// 邏輯刪除組件!
????@Bean
????public?ISqlInjector sqlInjector()?{
????????return?new?LogicSqlInjector();
????}配置文件配置
global-config:
????db-config:
??????logic-delete-value: 1
??????logic-not-delete-value: 04、測(cè)試 測(cè)試刪除

字段值也從0修改成了1測(cè)試查詢

性能分析插件
作用:性能分析攔截器,用于輸出每條 SQL 語句及其執(zhí)行時(shí)間 MP也提供性能分析插件,如果超過這個(gè)時(shí)間就停止運(yùn)行!
1、導(dǎo)入插件
/**
?????* SQL執(zhí)行效率插件
?????*/
????@Bean
????@Profile({"dev","test"})// 設(shè)置 dev test 環(huán)境開啟,保證我們的效率
????public?PerformanceInterceptor performanceInterceptor() {
????????PerformanceInterceptor performanceInterceptor = new?PerformanceInterceptor();
????????performanceInterceptor.setMaxTime(100); //ms 設(shè)置sql執(zhí)行的最大時(shí)間,如果超過了則不執(zhí)行
????????performanceInterceptor.setFormat(true);
????????return?performanceInterceptor;
????}
條件構(gòu)造器(Wrapper)
isNotNull .gt
@Test
void?contextLoads()?{
????????// 查詢name不為空的用戶,并且郵箱不為空的用戶,年齡大于等于12
????????QueryWrapper wrapper = new?QueryWrapper<>();
????????wrapper
????????????????.isNotNull("name") //不為空
????????????????.isNotNull("email")
????????????????.ge("age",18);
????????userMapper.selectList(wrapper).forEach(System.out::println); // 和我們剛才學(xué)習(xí)的map對(duì)比一下
????} 
.eq
@Test
void?test2(){
????????// 查詢名字kwhua
????????QueryWrapper wrapper = new?QueryWrapper<>();
????????wrapper.eq("name","kwhua");
????????User user = userMapper.selectOne(wrapper); // 查詢一個(gè)數(shù)據(jù)用selectOne,查詢多個(gè)結(jié)果使用List 或者 Map
????????System.out.println(user);
????} 其他方法可以自己測(cè)試...
代碼自動(dòng)生成器
// 代碼自動(dòng)生成器
public?class?generateCode?{
???public?static?void main(String[] args) {
????// 需要構(gòu)建一個(gè) 代碼自動(dòng)生成器 對(duì)象
????AutoGenerator mpg = new?AutoGenerator();
????// 配置策略
????// 1、全局配置
????GlobalConfig gc = new?GlobalConfig();
????String projectPath = System.getProperty("user.dir");
????gc.setOutputDir(projectPath+"/src/main/java");
????gc.setAuthor("kwhua");//作者名稱
????gc.setOpen(false);
????gc.setFileOverride(false); // 是否覆蓋
????gc.setIdType(IdType.ID_WORKER);
????gc.setDateType(DateType.ONLY_DATE);
????gc.setSwagger2(true);//實(shí)體屬性 Swagger2 注解
????// 自定義文件命名,注意 %s 會(huì)自動(dòng)填充表實(shí)體屬性!
????gc.setServiceName("%sService");
????gc.setControllerName("%sController");
????gc.setServiceName("%sService");
????gc.setServiceImplName("%sServiceImpl");
????gc.setMapperName("%sMapper");
????gc.setXmlName("%sMapper");
????mpg.setGlobalConfig(gc);
????//2、設(shè)置數(shù)據(jù)源
????DataSourceConfig dsc = new?DataSourceConfig();
????dsc.setUrl("jdbc:mysql://localhost:3306/kwhua_test?
useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8");
????dsc.setDriverName("com.mysql.cj.jdbc.Driver");
????// dsc.setDriverName("com.mysql.jdbc.Driver"); //mysql5.6以下的驅(qū)動(dòng)
????dsc.setUsername("root");
????dsc.setPassword("root");
????dsc.setDbType(DbType.MYSQL);
????mpg.setDataSource(dsc);
????//3、包的配置
????PackageConfig pc = new?PackageConfig();
????pc.setParent("com.kwhua"); //包名
????pc.setModuleName("model"); //模塊名
????pc.setEntity("entity");
????pc.setMapper("mapper");
????pc.setService("service");
????pc.setController("controller");
????mpg.setPackageInfo(pc);
????//4、策略配置
????StrategyConfig strategy = new?StrategyConfig();
????strategy.setInclude("user","course"); // 設(shè)置要映射的表名
????strategy.setNaming(NamingStrategy.underline_to_camel);
????strategy.setColumnNaming(NamingStrategy.underline_to_camel);
????strategy.setEntityLombokModel(true); // 自動(dòng)lombok;
????strategy.setLogicDeleteFieldName("deleted");
????// 自動(dòng)填充配置
????TableFill gmtCreate = new?TableFill("gmt_create", FieldFill.INSERT);
????TableFill gmtModified = new?TableFill("gmt_modified",FieldFill.INSERT_UPDATE);
????ArrayList tableFills = new?ArrayList<>();
????tableFills.add(gmtCreate);
????tableFills.add(gmtModified);
????strategy.setTableFillList(tableFills);
????// 樂觀鎖
????strategy.setVersionFieldName("version");
????//根據(jù)你的表名來建對(duì)應(yīng)的類名,如果你的表名沒有下劃線,比如test,那么你就可以取消這一步
????strategy.setTablePrefix("t_");
????strategy.setRestControllerStyle(true); //rest請(qǐng)求
????//自動(dòng)轉(zhuǎn)下劃線,比如localhost:8080/hello_id_2
????strategy.setControllerMappingHyphenStyle(true);
????mpg.setStrategy(strategy);
????mpg.execute(); //執(zhí)行
?}
} 執(zhí)行主方法即可生成對(duì)應(yīng)代碼
?
作者:java架構(gòu)師阿松
www.toutiao.com/i6869621037831717387
后臺(tái)回復(fù)?學(xué)習(xí)資料?領(lǐng)取學(xué)習(xí)視頻
如有收獲,點(diǎn)個(gè)在看,誠摯感謝
