前言
官網:
https://baomidou.com/
建立資料庫資料庫名為mybatis_plus
建立表建立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]');
注意:-- 真實開發中往往都會有這四個欄位,version(樂觀鎖)、deleted(邏輯刪除)、gmt_create(建立時間)、gmt_modified(修改時間)
初始化專案使用SpringBoot器 初始化!
連線資料庫建立application.yml
spring: profiles: active: dev datasource:# 驅動不同 mysql 5 com.mysql.jdbc.Driver# mysql 8 com.mysql.cj.jdbc.Driver、需要增加時區的配置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
業務程式碼實體類
@Data@AllArgsConstructor@NoArgsConstructorpublic 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;// 在對應的Mapper上面繼承基本的類 BaseMapper@Repository // 代表持久層public interface UserMapper extends BaseMapper<User> { // 所有的CRUD操作都已經編寫完成了}
注意點,我們需要在主啟動類上去掃描我們的mapper包下的所有介面 @MapperScan(“com.kwhua.mapper”)
測試@SpringBootTestclass MybatisPlusApplicationTests { // 繼承了BaseMapper,所有的方法都來自己父類 // 我們也可以編寫自己的擴充套件方法! @Autowired private UserMapper userMapper; @Test void contextLoads() { // 引數是一個 Wrapper ,條件構造器,這裡我們先設定條件為空,查詢所有。 List<User> users = userMapper.selectList(null); users.forEach(System.out::println); }}
所有資料輸出
配置日誌我們所有的sql現在是不可見的,我們希望知道它是怎麼執行的,所有我們要配置日誌的輸出 application.yml檔案新增日誌配置
#配置日誌mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
檢視執行sql的日誌資訊
三.Mybatis-plus的CRUD插入操作// 測試插入 @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); // 幫我們自動生成id System.out.println(result); // 受影響的行數 System.out.println(user); // 看到id會自動填充。 }
看到id會自動填充。資料庫插入的id的預設值為:全域性的唯一id
主鍵生成策略1)主鍵自增
1、實體類欄位上 @TableId(type = IdType.AUTO)
2、資料庫id欄位設定為自增!
3、再次測試(可以看到id值比上次插入的大1)id的生成策略原始碼解釋
public enum IdType { AUTO(0), // 資料庫id自增 NONE(1), // 未設定主鍵 INPUT(2), // 手動輸入 ID_WORKER(3), // 預設的方式,全域性唯一id UUID(4), // 全域性唯一id uuid ID_WORKER_STR(5); //ID_WORKER 字串表示法}
以上不再逐一測試。
更新操作 @Test public void testUpdate(){ User user = new User(); // 透過條件自動拼接動態sql user.setId(1302223874217295874L); user.setName("kwhua_mybatis-plus_updateTest"); user.setAge(20); // 注意:updateById 但是引數是一個物件! int i = userMapper.updateById(user); System.out.println(i); }
自動填充建立時間、修改時間!這兩個欄位操作都是自動化完成的,我們不希望手動更新!阿里巴巴開發手冊:所有的資料庫表都要配置上gmt_create、gmt_modified!而且需要自動化!
方式一:資料庫級別(工作中一般不用)
1、在表中新增欄位 gmt_create, gmt_modified
2、把實體類同步
private Date gmtCreate;private Date gmtModified;
3、再次檢視
2、實體類欄位屬性上需要增加註解
// 欄位新增填充內容 @TableField(fill = FieldFill.INSERT) private Date gmt_create; @TableField(fill = FieldFill.INSERT_UPDATE) private Date gmt_modified;
3、編寫處理器來處理這個註解即可!
@Slf4j@Component // 一定不要忘記把處理器加到IOC容器中!public class MyMetaObjectHandler implements MetaObjectHandler { // 插入時的填充策略 @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); } // 更新時的填充策略 @Override public void updateFill(MetaObject metaObject) { log.info("start update fill....."); this.setFieldValByName("gmt_modified",new Date(),metaObject); }}
4、測試插入和更新,檢查時間變化。
樂觀鎖樂觀鎖 : 顧名思義,十分樂觀,它總是認為不會出現問題,無論幹什麼不去上鎖!如果出現了問題, 再次更新值測試 悲觀鎖:顧名思義,十分悲觀,它總是認為總是出現問題,無論幹什麼都會上鎖!再去操作!
樂觀鎖實現方式:
取出記錄時,獲取當前version 更新時,帶上這個version 執行更新時, set version = newVersion where version = oldVersion 如果version不對,就更新失敗
樂觀鎖:1、先查詢,獲得版本號 version = 1
-- Aupdate user set name = "kwhua", version = version + 1where id = 2 and version = 1-- B 執行緒搶先完成,這個時候 version = 2,會導致 A 修改失敗!update user set name = "kwhua", version = version + 1where id = 2 and version = 1
樂觀鎖測試
1、給資料庫中增加version欄位!
2、實體類加對應的欄位
@Version //樂觀鎖Version註解 private Integer version;
3、註冊元件
// 掃描我們的 mapper 資料夾@MapperScan("com.kwhua.mapper")@EnableTransactionManagement@Configuration // 配置類public class MyBatisPlusConfig { // 註冊樂觀鎖外掛 @Bean public OptimisticLockerInterceptor optimisticLockerInterceptor() { return new OptimisticLockerInterceptor(); } }
4、測試
// 測試樂觀鎖成功! @Test public void testOptimisticLocker(){ // 1、查詢使用者資訊 User user = userMapper.selectById(1L); // 2、修改使用者資訊 user.setName("kwhua"); user.setEmail("[email protected]"); // 3、執行更新操作 userMapper.updateById(user); }
version欄位已經由1變成了2
// 測試樂觀鎖失敗!多執行緒下 @Test public void testOptimisticLocker2(){ // 執行緒 1 User user = userMapper.selectById(1L); user.setName("kwhua111"); user.setEmail("[email protected]"); // 模擬另外一個執行緒執行了插隊操作 User user2 = userMapper.selectById(1L); user2.setName("kwhua222"); user2.setEmail("[email protected]"); userMapper.updateById(user2); // 自旋鎖來多次嘗試提交! userMapper.updateById(user); // 如果沒有樂觀鎖就會覆蓋插隊執行緒的值! }
可以看到執行緒1執行更新失敗
查詢操作// 測試查詢 @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","kwhua"); map.put("age",15); List<User> users = userMapper.selectByMap(map); users.forEach(System.out::println); }
1、配置攔截器元件
// 分頁外掛@Beanpublic PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor();}
2、直接使用Page物件即可!
// 測試分頁查詢@Testpublic void testPage(){ // 引數一:當前頁 // 引數二:頁面大小 Page<User> page = new Page<>(2,5); userMapper.selectPage(page,null); page.getRecords().forEach(System.out::println); System.out.println(page.getTotal());}
物理刪除
// 測試刪除 @Test public void testDeleteById(){ userMapper.deleteById(1L); } // 透過id批次刪除 @Test public void testDeleteBatchId(){ userMapper.deleteBatchIds(Arrays.asList(2L,3L)); } // 透過map刪除 @Test public void testDeleteMap(){ HashMap<String, Object> map = new HashMap<>(); map.put("name","kwhua"); userMapper.deleteByMap(map); }
邏輯刪除物理刪除 :從資料庫中直接移除 邏輯刪除 :在資料庫中沒有被移除,而是透過一個變數來讓它失效!deleted = 0 => deleted = 1 管理員可以檢視被刪除的記錄!防止資料的丟失,類似於回收站!
1、在資料表中增加一個 deleted 欄位
2、實體類中增加屬性
// 邏輯刪除元件! @Bean public ISqlInjector sqlInjector() { return new LogicSqlInjector(); }
配置檔案配置
global-config: db-config: logic-delete-value: 1 logic-not-delete-value: 0
4、測試 測試刪除
欄位值也從0修改成了1測試查詢
效能分析外掛作用:效能分析攔截器,用於輸出每條 SQL 語句及其執行時間 MP也提供效能分析外掛,如果超過這個時間就停止執行!
1、匯入外掛
/** * SQL執行效率外掛 */ @Bean @Profile({"dev","test"})// 設定 dev test 環境開啟,保證我們的效率 public PerformanceInterceptor performanceInterceptor() { PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor(); performanceInterceptor.setMaxTime(100); //ms 設定sql執行的最大時間,如果超過了則不執行 performanceInterceptor.setFormat(true); return performanceInterceptor; }
條件構造器(Wrapper)isNotNull .gt
@Test void contextLoads() { // 查詢name不為空的使用者,並且郵箱不為空的使用者,年齡大於等於12 QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper .isNotNull("name") //不為空 .isNotNull("email") .ge("age",18); userMapper.selectList(wrapper).forEach(System.out::println); // 和我們剛才學習的map對比一下 }
.eq
@Test void test2(){ // 查詢名字kwhua QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.eq("name","kwhua"); User user = userMapper.selectOne(wrapper); // 查詢一個數據用selectOne,查詢多個結果使用List 或者 Map System.out.println(user); }
其他方法可以自己測試...
程式碼自動生成器// 程式碼自動生成器public class generateCode { public static void main(String[] args) { // 需要構建一個 程式碼自動生成器 物件 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);//實體屬性 Swagger2 註解 // 自定義檔案命名,注意 %s 會自動填充表實體屬性! gc.setServiceName("%sService"); gc.setControllerName("%sController"); gc.setServiceName("%sService"); gc.setServiceImplName("%sServiceImpl"); gc.setMapperName("%sMapper"); gc.setXmlName("%sMapper"); mpg.setGlobalConfig(gc); //2、設定資料來源 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以下的驅動 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"); // 設定要對映的表名 strategy.setNaming(NamingStrategy.underline_to_camel); strategy.setColumnNaming(NamingStrategy.underline_to_camel); strategy.setEntityLombokModel(true); // 自動lombok; strategy.setLogicDeleteFieldName("deleted"); // 自動填充配置 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"); //根據你的表名來建對應的類名,如果你的表名沒有下劃線,比如test,那麼你就可以取消這一步 strategy.setTablePrefix("t_"); strategy.setRestControllerStyle(true); //rest請求 //自動轉下劃線,比如localhost:8080/hello_id_2 strategy.setControllerMappingHyphenStyle(true); mpg.setStrategy(strategy); mpg.execute(); //執行 }}
執行主方法即可生成對應程式碼