1. MyBatisPlus概述
為什么要學(xué)習(xí)它呢?MyBatisPlus可以節(jié)省我們大量工作時(shí)間,所有的CRUD代碼它都可以自動(dòng)化完成!
JPA 旁壮、 tk-mapper、MyBatisPlus
1.1 簡介
是什么谐檀? MyBatis 本來就是簡化 JDBC 操作的抡谐!
官網(wǎng):https://mp.baomidou.com/ MyBatis Plus,簡化 MyBatis 桐猬!
1.2 特性
- 無侵入:只做增強(qiáng)不做改變麦撵,引入它不會(huì)對現(xiàn)有工程產(chǎn)生影響,如絲般順滑
-
損耗小:啟動(dòng)即會(huì)自動(dòng)注入基本 CURD溃肪,性能基本無損耗免胃,直接面向?qū)ο蟛僮鳎?BaseMapper
強(qiáng)大的 CRUD 操作:內(nèi)置通用 Mapper、通用 Service惫撰,僅僅通過少量配置即可實(shí)現(xiàn)單表大部分
CRUD 操作羔沙,更有強(qiáng)大的條件構(gòu)造器,滿足各類使用需求, 以后簡單的CRUD操作厨钻,它不用自己編寫
了撬碟! - 支持 Lambda 形式調(diào)用:通過 Lambda 表達(dá)式,方便的編寫各類查詢條件莉撇,無需再擔(dān)心字段寫錯(cuò)
-
支持主鍵自動(dòng)生成:支持多達(dá) 4 種主鍵策略(內(nèi)含分布式唯一 ID 生成器 - Sequence),可自由配
置惶傻,完美解決主鍵問題 -
支持 ActiveRecord 模式:支持 ActiveRecord 形式調(diào)用棍郎,實(shí)體類只需繼承 Model 類即可進(jìn)行強(qiáng)大
的 CRUD 操作 - 支持自定義全局通用操作:支持全局通用方法注入( Write once, use anywhere )
-
內(nèi)置代碼生成器:采用代碼或者 Maven 插件可快速生成 Mapper 、 Model 银室、 Service 涂佃、
Controller 層代碼,支持模板引擎蜈敢,更有超多自定義配置等您來使用(自動(dòng)幫你生成代碼) -
內(nèi)置分頁插件:基于 MyBatis 物理分頁辜荠,開發(fā)者無需關(guān)心具體操作,配置好插件之后抓狭,寫分頁等同
于普通 List 查詢 -
分頁插件支持多種數(shù)據(jù)庫:支持 MySQL伯病、MariaDB、Oracle否过、DB2午笛、H2惭蟋、HSQL、SQLite药磺、
Postgre告组、SQLServer 等多種數(shù)據(jù)庫 -
內(nèi)置性能分析插件:可輸出 Sql 語句以及其執(zhí)行時(shí)間,建議開發(fā)測試時(shí)啟用該功能癌佩,能快速揪出慢
查詢 -
內(nèi)置全局?jǐn)r截插件:提供全表 delete 木缝、 update 操作智能分析阻斷,也可自定義攔截規(guī)則围辙,預(yù)防誤
操作
2. 快速入門
地址:https://mp.baomidou.com/guide/quick-start.html#初始化工程
使用第三方組件:
- 導(dǎo)入對應(yīng)的依賴
- 研究依賴如何配置
- 代碼如何編寫
- 提高擴(kuò)展技術(shù)能力我碟!
2.1 步驟
創(chuàng)建數(shù)據(jù)庫
mybatis_plus
-
創(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, 'test1@baomidou.com'), (2, 'Jack', 20, 'test2@baomidou.com'), (3, 'Tom', 28, 'test3@baomidou.com'), (4, 'Sandy', 21, 'test4@baomidou.com'), (5, 'Billie', 24, 'test5@baomidou.com'); -- 真實(shí)開發(fā)中,version(樂觀鎖)酌畜、deleted(邏輯刪除)怎囚、gmt_create、gmt_modified
編寫項(xiàng)目桥胞,初始化項(xiàng)目恳守!使用SpringBoot初始化!
-
導(dǎo)入依賴
<!-- 數(shù)據(jù)庫驅(qū)動(dòng) --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!-- lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!-- mybatis-plus --> <!-- mybatis-plus 是自己開發(fā)贩虾,并非官方的催烘! --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.0.5</version> </dependency>
說明:我們使用 mybatis-plus 可以節(jié)省我們大量的代碼,盡量不要同時(shí)導(dǎo)入 mybatis 和 mybatis-
plus缎罢!版本的差異伊群! -
連接數(shù)據(jù)庫!這一步和 mybatis 相同策精!
# mysql 5 驅(qū)動(dòng)不同 com.mysql.jdbc.Driver # mysql 8 驅(qū)動(dòng)不同com.mysql.cj.jdbc.Driver舰始、需要增加時(shí)區(qū)的配置 serverTimezone=GMT%2B8 spring.datasource.username=root spring.datasource.password=123456 spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus? useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
-
傳統(tǒng)方式pojo-dao(連接mybatis,配置mapper.xml文件)-service-controller
使用了mybatis-plus 之后
-
pojo
@Data @AllArgsConstructor @NoArgsConstructor public class User { private Long id; private String name; private Integer age; private String email; }
-
mapper接口
package com.kuang.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.kuang.pojo.User; import org.springframework.stereotype.Repository; // 在對應(yīng)的Mapper上面繼承基本的類 BaseMapper @Repository // 代表持久層 public interface UserMapper extends BaseMapper<User> { // 所有的CRUD操作都已經(jīng)編寫完成了 // 你不需要像以前的配置一大堆文件了咽袜! }
-
注意點(diǎn)丸卷,我們需要在主啟動(dòng)類上去掃描我們的mapper包下的所有接口
@MapperScan("com.kuang.mapper")
-
測試類中測試
@SpringBootTest class MybatisPlusApplicationTests { // 繼承了BaseMapper,所有的方法都來自己父類 // 我們也可以編寫自己的擴(kuò)展方法询刹! @Autowired private UserMapper userMapper; @Test void contextLoads() { // 參數(shù)是一個(gè) Wrapper 谜嫉,條件構(gòu)造器,這里我們先不用 null // 查詢?nèi)坑脩? List<User> users = userMapper.selectList(null); users.forEach(System.out::println); } }
-
結(jié)果
-
3. 配置日志
我們所有的sql現(xiàn)在是不可見的凹联,我們希望知道它是怎么執(zhí)行的沐兰,所以我們必須要看日志!
# 配置日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
配置完畢日志之后蔽挠,后面的學(xué)習(xí)就需要注意這個(gè)自動(dòng)生成的SQL住闯,你們就會(huì)喜歡上 MyBatis-Plus!
4. CRUD操作
4.1 插入操作
// 測試插入
@Test
public void testInsert(){
User user = new User();
user.setName("狂神說Java");
user.setAge(3);
user.setEmail("24736743@qq.com");
int result = userMapper.insert(user); // 幫我們自動(dòng)生成id
System.out.println(result); // 受影響的行數(shù)
System.out.println(user); // 發(fā)現(xiàn),id會(huì)自動(dòng)回填
}
數(shù)據(jù)庫插入的id的默認(rèn)值為:全局的唯一id
4.2 主鍵生成策略
默認(rèn) ID_WORKER 全局唯一id
分布式系統(tǒng)唯一id生成:https://www.cnblogs.com/haoxinyue/p/5208136.html
雪花算法:
snowflake是Twitter開源的分布式ID生成算法寞秃,結(jié)果是一個(gè)long型的ID斟叼。其核心思想是:使用41bit作為
毫秒數(shù),10bit作為機(jī)器的ID(5個(gè)bit是數(shù)據(jù)中心春寿,5個(gè)bit的機(jī)器ID)朗涩,12bit作為毫秒內(nèi)的流水號(意味
著每個(gè)節(jié)點(diǎn)在每毫秒可以產(chǎn)生 4096 個(gè) ID),最后還有一個(gè)符號位绑改,永遠(yuǎn)是0谢床。可以保證幾乎全球唯
一厘线!
主鍵自增
我們需要配置主鍵自增:
實(shí)體類字段上
@TableId(type = IdType.AUTO)
-
數(shù)據(jù)庫字段一定要是自增识腿!
- 再次測試插入即可!
其與的源碼解釋
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 字符串表示法
}
4.3 更新操作
// 測試更新
@Test
public void testUpdate(){
User user = new User();
// 通過條件自動(dòng)拼接動(dòng)態(tài)sql
user.setId(6L);
user.setName("關(guān)注公眾號:狂神說");
user.setAge(18);
// 注意:updateById 但是參數(shù)是一個(gè) 對象造壮!
int i = userMapper.updateById(user);
System.out.println(i);
}
所有的sql都是自動(dòng)幫你動(dòng)態(tài)配置的渡讼!
4.4 自動(dòng)填充
創(chuàng)建時(shí)間、修改時(shí)間耳璧!這些個(gè)操作一遍都是自動(dòng)化完成的成箫,我們不希望手動(dòng)更新!
阿里巴巴開發(fā)手冊:所有的數(shù)據(jù)庫表:gmt_create
旨枯、gmt_modified
幾乎所有的表都要配置上蹬昌!而且需
要自動(dòng)化!
方式一:數(shù)據(jù)庫級別(工作中不允許你修改數(shù)據(jù)庫)
-
在表中新增字段
create_time
,update_time
-
再次測試插入方法攀隔,我們需要先把實(shí)體類同步皂贩!
private Date createTime; private Date updateTime;
-
再次更新查看結(jié)果即可
方式二:代碼級別
-
刪除數(shù)據(jù)庫的默認(rèn)值、更新操作昆汹!
-
實(shí)體類字段屬性上需要增加注解
// 字段添加填充內(nèi)容 @TableField(fill = FieldFill.INSERT) private Date createTime; @TableField(fill = FieldFill.INSERT_UPDATE) private Date updateTime;
-
編寫處理器來處理這個(gè)注解即可明刷!
package com.kuang.handler; @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("createTime",new Date(),metaObject); this.setFieldValByName("updateTime",new Date(),metaObject); } // 更新時(shí)的填充策略 @Override public void updateFill(MetaObject metaObject) { log.info("start update fill....."); this.setFieldValByName("updateTime",new Date(),metaObject); } }
測試插入
測試更新满粗、觀察時(shí)間即可遮精!
4.5 樂觀鎖
樂觀鎖 : 故名思意十分樂觀,它總是認(rèn)為不會(huì)出現(xiàn)問題败潦,無論干什么不去上鎖!如果出現(xiàn)了問題准脂,
再次更新值測試
悲觀鎖:故名思意十分悲觀劫扒,它總是認(rèn)為總是出現(xiàn)問題,無論干什么都會(huì)上鎖狸膏!再去操作沟饥!
樂觀鎖實(shí)現(xiàn)方式:
- 取出記錄時(shí),獲取當(dāng)前 version
- 更新時(shí),帶上這個(gè)version
- 執(zhí)行更新時(shí)贤旷, set version = newVersion where version = oldVersion
- 如果version不對广料,就更新失敗
-- 樂觀鎖:1、先查詢幼驶,獲得版本號 version = 1
-- A
update user set name = "kuangshen", version = version + 1
where id = 2 and version = 1
-- B 線程搶先完成艾杏,這個(gè)時(shí)候 version = 2,會(huì)導(dǎo)致 A 修改失斨言濉购桑!
update user set name = "kuangshen", version = version + 1
where id = 2 and version = 1
測試一下MybatisPlus的樂觀鎖插件
-
給數(shù)據(jù)庫中增加version字段!
-
我們實(shí)體類加對應(yīng)的字段
@Version //樂觀鎖Version注解 private Integer version;
-
注冊組件
package com.kuang.config; // 掃描我們的 mapper 文件夾 @MapperScan("com.kuang.mapper") @EnableTransactionManagement @Configuration // 配置類 public class MyBatisPlusConfig { // 注冊樂觀鎖插件 @Bean public OptimisticLockerInterceptor optimisticLockerInterceptor() { return new OptimisticLockerInterceptor(); } }
-
測試一下氏淑!
// 測試樂觀鎖成功勃蜘! @Test public void testOptimisticLocker(){ // 1、查詢用戶信息 User user = userMapper.selectById(1L); // 2假残、修改用戶信息 user.setName("kuangshen"); user.setEmail("24736743@qq.com"); // 3缭贡、執(zhí)行更新操作 userMapper.updateById(user); } // 測試樂觀鎖失敗辉懒!多線程下 @Test public void testOptimisticLocker2(){ // 線程 1 User user = userMapper.selectById(1L); user.setName("kuangshen111"); user.setEmail("24736743@qq.com"); // 模擬另外一個(gè)線程執(zhí)行了插隊(duì)操作 User user2 = userMapper.selectById(1L); user2.setName("kuangshen222"); user2.setEmail("24736743@qq.com"); userMapper.updateById(user2); // 自旋鎖來多次嘗試提交阳惹! userMapper.updateById(user); // 如果沒有樂觀鎖就會(huì)覆蓋插隊(duì)線程的值! }
4.6 查詢操作
// 測試查詢
@Test
public void testSelectById(){
User user = userMapper.selectById(1L);
System.out.println(user);
}
// 測試批量查詢耗帕!
@Test
public void testSelectByBatchId(){
List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
users.forEach(System.out::println);
}
// 按條件查詢之一使用map操作
@Test
public void testSelectByBatchIds(){
HashMap<String, Object> map = new HashMap<>();
// 自定義要查詢
map.put("name","狂神說Java");
map.put("age",3);
List<User> users = userMapper.selectByMap(map);
users.forEach(System.out::println);
}
4.7 分頁查詢
如何使用穆端!
-
配置攔截器組件即可
package com.kuang.config; @MapperScan("com.kuang.mapper") @EnableTransactionManagement @Configuration // 配置類 public class MyBatisPlusConfig { // 分頁插件 @Bean public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); } }
-
直接使用Page對象即可!
// 測試分頁查詢 @Test public void testPage(){ // 參數(shù)一:當(dāng)前頁 // 參數(shù)二:頁面大小 // 使用了分頁插件之后仿便,所有的分頁操作也變得簡單的体啰! Page<User> page = new Page<>(2,5); userMapper.selectPage(page,null); page.getRecords().forEach(System.out::println); System.out.println(page.getTotal()); }
4.8 刪除操作
-
根據(jù) id 刪除記錄
// 測試刪除 @Test public void testDeleteById(){ userMapper.deleteById(1240620674645544965L); } // 通過id批量刪除 @Test public void testDeleteBatchId(){ userMapper.deleteBatchIds(Arrays.asList(1240620674645544961L,124062067464554496 2L)); } // 通過map刪除 @Test public void testDeleteMap(){ HashMap<String, Object> map = new HashMap<>(); map.put("name","狂神說Java"); userMapper.deleteByMap(map); }
4.9 邏輯刪除
物理刪除 :從數(shù)據(jù)庫中直接移除
邏輯刪除 :再數(shù)據(jù)庫中沒有被移除,而是通過一個(gè)變量來讓他失效嗽仪! deleted = 0 => deleted = 1
管理員可以查看被刪除的記錄荒勇!防止數(shù)據(jù)的丟失,類似于回收站闻坚!
測試一下:
-
在數(shù)據(jù)表中增加一個(gè) deleted 字段
-
實(shí)體類中增加屬性
@TableLogic //邏輯刪除 private Integer deleted;
-
配置沽翔!
package com.kuang.config; @MapperScan("com.kuang.mapper") @EnableTransactionManagement @Configuration // 配置類 public class MyBatisPlusConfig { // 邏輯刪除組件! @Bean public ISqlInjector sqlInjector() { return new LogicSqlInjector(); } }
# 配置邏輯刪除 mybatis-plus.global-config.db-config.logic-delete-value=1 mybatis-plus.global-config.db-config.logic-not-delete-value=0
-
測試一下刪除窿凤!
記錄依舊在數(shù)據(jù)庫仅偎,但是值確已經(jīng)變化了!
5. 性能分析插件
我們在平時(shí)的開發(fā)中雳殊,會(huì)遇到一些慢sql橘沥。測試! druid,,,,,
作用:性能分析攔截器夯秃,用于輸出每條 SQL 語句及其執(zhí)行時(shí)間
MP也提供性能分析插件座咆,如果超過這個(gè)時(shí)間就停止運(yùn)行痢艺!
-
導(dǎo)入插件
package com.kuang.config; @MapperScan("com.kuang.mapper") @EnableTransactionManagement @Configuration // 配置類 public class MyBatisPlusConfig { /** * 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; } }
記住堤舒,要在SpringBoot中配置環(huán)境為dev或者 test 環(huán)境!
spring.profiles.active=dev
-
測試使用哺呜!
@Test void contextLoads() { // 參數(shù)是一個(gè) Wrapper 舌缤,條件構(gòu)造器,這里我們先不用 null // 查詢?nèi)坑脩? List<User> users = userMapper.selectList(null); users.forEach(System.out::println); }
6. 條件構(gòu)造器
-
測試一弦牡,查詢name不為空的用戶友驮,并且郵箱不為空的用戶,年齡大于等于12
@Test void contextLoads() { // 查詢name不為空的用戶驾锰,并且郵箱不為空的用戶卸留,年齡大于等于12 QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper .isNotNull("name") .isNotNull("email") .ge("age",12); userMapper.selectList(wrapper).forEach(System.out::println); // 和我們剛才學(xué)習(xí) 的map對比一下 }
-
測試二,查詢指定名字的User
@Test void test2(){ // 查詢名字狂神說 QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.eq("name","狂神說"); User user = userMapper.selectOne(wrapper); // 查詢一個(gè)數(shù)據(jù),出現(xiàn)多個(gè)結(jié)果使用List 或者 Map System.out.println(user); }
-
測試三,查詢年齡在 20 ~ 30 歲之間的用戶數(shù)
@Test void test3(){ // 查詢年齡在 20 ~ 30 歲之間的用戶 QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.between("age",20,30); // 區(qū)間 Integer count = userMapper.selectCount(wrapper);// 查詢結(jié)果數(shù) System.out.println(count); }
-
測試四椭豫,模糊查詢
// 模糊查詢 @Test void test4(){ // 查詢年齡在 20 ~ 30 歲之間的用戶 QueryWrapper<User> wrapper = new QueryWrapper<>(); // 左和右 t% wrapper .notLike("name","e") .likeRight("email","t"); List<Map<String, Object>> maps = userMapper.selectMaps(wrapper); maps.forEach(System.out::println); }
-
測試五,子查詢
@Test void test5(){ QueryWrapper<User> wrapper = new QueryWrapper<>(); // id 在子查詢中查出來 wrapper.inSql("id","select id from user where id<3"); List<Object> objects = userMapper.selectObjs(wrapper); objects.forEach(System.out::println); }
-
測試六,通過id進(jìn)行排序
//測試六 @Test void test6(){ QueryWrapper<User> wrapper = new QueryWrapper<>(); // 通過id進(jìn)行排序 wrapper.orderByAsc("id"); List<User> users = userMapper.selectList(wrapper); users.forEach(System.out::println); }
7. 代碼自動(dòng)生成器
dao耻瑟、pojo、service赏酥、controller都是我自己去編寫完成喳整!
AutoGenerator 是 MyBatis-Plus 的代碼生成器,通過 AutoGenerator 可以快速生成 Entity裸扶、Mapper框都、Mapper XML、Service呵晨、Controller 等各個(gè)模塊的代碼魏保,極大的提升了開發(fā)效率。
測試:
package com.kuang;
// 代碼自動(dòng)生成器
public class KuangCode {
public static void main(String[] args) {
// 需要構(gòu)建一個(gè) 代碼自動(dòng)生成器 對象
AutoGenerator mpg = new AutoGenerator();
// 配置策略
// 1摸屠、全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath+"/src/main/java");
gc.setAuthor("狂神說");
gc.setOpen(false);
gc.setFileOverride(false); // 是否覆蓋
gc.setServiceName("%sService"); // 去Service的I前綴
gc.setIdType(IdType.ID_WORKER);
gc.setDateType(DateType.ONLY_DATE);
gc.setSwagger2(true);
mpg.setGlobalConfig(gc);
//2谓罗、設(shè)置數(shù)據(jù)源
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/kuang_community?
useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("123456");
dsc.setDbType(DbType.MYSQL);
mpg.setDataSource(dsc);
//3、包的配置
PackageConfig pc = new PackageConfig();
pc.setModuleName("blog");
pc.setParent("com.kuang");
pc.setEntity("entity");
pc.setMapper("mapper");
pc.setService("service");
pc.setController("controller");
mpg.setPackageInfo(pc);
//4季二、策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setInclude("blog_tags","course","links","sys_settings","user_record","
user_say"); // 設(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<TableFill> tableFills = new ArrayList<>();
tableFills.add(gmtCreate);
tableFills.add(gmtModified);
strategy.setTableFillList(tableFills);
// 樂觀鎖
strategy.setVersionFieldName("version");
strategy.setRestControllerStyle(true);
strategy.setControllerMappingHyphenStyle(true); //
localhost:8080/hello_id_2
mpg.setStrategy(strategy);
mpg.execute(); //執(zhí)行
}
}
參考:B站狂神說Java