官網:Mybatis-plus官方文檔 簡化 MyBatis !java
數據庫名爲mybatis_plusmysql
建立user表spring
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');
注意:-- 真實開發中每每都會有這四個字段,version(樂觀鎖)、deleted(邏輯刪除)、gmt_create(建立時間)、gmt_modified(修改時間)sql
使用SpringBoot器 初始化!
<!-- 數據庫驅動 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!-- lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!-- mybatis-plus --> <!-- mybatis-plus 是本身開發,並不是官方的! --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.0.5</version> </dependency>
注意:儘可能不要同時導入 mybatis 和 mybatis-plus!避免版本的差別形成沒法預知的問題。數據庫
建立application.ymlapache
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 @NoArgsConstructor public class User { private Long id; private String name; private Integer age; private String email; }
mapper接口mybatis
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」)多線程
@SpringBootTest class MybatisPlusApplicationTests { // 繼承了BaseMapper,全部的方法都來本身父類 // 咱們也能夠編寫本身的擴展方法! @Autowired private UserMapper userMapper; @Test void contextLoads() { // 參數是一個 Wrapper ,條件構造器,這裏咱們先設置條件爲空,查詢全部。 List<User> users = userMapper.selectList(null); users.forEach(System.out::println); } }
全部數據輸出app
咱們全部的sql如今是不可見的,咱們但願知道它是怎麼執行的,全部咱們要配置日誌的輸出
application.yml文件添加日誌配置ide
#配置日誌 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
查看執行sql的日誌信息
// 測試插入 @Test public void testInsert(){ User user = new User(); user.setName("kwhua_mybatis-plus_insertTest"); user.setAge(15); user.setEmail("310697723@qq.com"); int result = userMapper.insert(user); // 幫咱們自動生成id System.out.println(result); // 受影響的行數 System.out.println(user); // 看到id會自動填充。 }
看到id會自動填充。數據庫插入的id的默認值爲:全局的惟一id
1)主鍵自增
一、實體類字段上 @TableId(type = IdType.AUTO)
二、數據庫id字段設置爲自增!
三、再次測試(能夠看到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!並且須要自動化!
方式一:數據庫級別(工做中通常不用)
一、在表中新增字段 gmt_create, gmt_modified
二、把實體類同步
private Date gmtCreate; private Date gmtModified;
三、再次查看
方式二:代碼級別
一、刪除數據庫的默認值、更新操做!
二、實體類字段屬性上須要增長註解
// 字段添加填充內容 @TableField(fill = FieldFill.INSERT) private Date gmt_create; @TableField(fill = FieldFill.INSERT_UPDATE) private Date gmt_modified;
三、編寫處理器來處理這個註解便可!
@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); } }
四、測試插入和更新,檢查時間變化。
樂觀鎖 : 故名思意,十分樂觀,它老是認爲不會出現問題,不管幹什麼不去上鎖!若是出現了問題,
再次更新值測試
悲觀鎖:故名思意,十分悲觀,它老是認爲老是出現問題,不管幹什麼都會上鎖!再去操做!
樂觀鎖實現方式:
取出記錄時,獲取當前version
更新時,帶上這個version
執行更新時, set version = newVersion where version = oldVersion
若是version不對,就更新失敗
樂觀鎖:一、先查詢,得到版本號 version = 1 -- A update user set name = "kwhua", version = version + 1 where id = 2 and version = 1 -- B 線程搶先完成,這個時候 version = 2,會致使 A 修改失敗! update user set name = "kwhua", version = version + 1 where id = 2 and version = 1
樂觀鎖測試
一、給數據庫中增長version字段!
二、實體類加對應的字段
@Version //樂觀鎖Version註解 private Integer version;
三、註冊組件
// 掃描咱們的 mapper 文件夾 @MapperScan("com.kwhua.mapper") @EnableTransactionManagement @Configuration // 配置類 public class MyBatisPlusConfig { // 註冊樂觀鎖插件 @Bean public OptimisticLockerInterceptor optimisticLockerInterceptor() { return new OptimisticLockerInterceptor(); } }
四、測試
// 測試樂觀鎖成功! @Test public void testOptimisticLocker(){ // 一、查詢用戶信息 User user = userMapper.selectById(1L); // 二、修改用戶信息 user.setName("kwhua"); user.setEmail("123456@qq.com"); // 三、執行更新操做 userMapper.updateById(user); }
version字段已經由1變成了2
// 測試樂觀鎖失敗!多線程下 @Test public void testOptimisticLocker2(){ // 線程 1 User user = userMapper.selectById(1L); user.setName("kwhua111"); user.setEmail("123456@qq.com"); // 模擬另一個線程執行了插隊操做 User user2 = userMapper.selectById(1L); user2.setName("kwhua222"); user2.setEmail("123456@qq.com"); 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); }
6.1分頁查詢
一、配置攔截器組件
// 分頁插件 @Bean public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); }
二、直接使用Page對象便可!
// 測試分頁查詢 @Test public 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
管理員能夠查看被刪除的記錄!防止數據的丟失,相似於回收站!
一、在數據表中增長一個 deleted 字段
二、實體類中增長屬性
@TableLogic //邏輯刪除 private Integer deleted;
三、配置
// 邏輯刪除組件! @Bean public ISqlInjector sqlInjector() { return new LogicSqlInjector(); }
配置文件配置
global-config: db-config: logic-delete-value: 1 logic-not-delete-value: 0
四、測試
測試刪除
字段值也從0修改爲了1
測試查詢
做用:性能分析攔截器,用於輸出每條 SQL 語句及其執行時間
MP也提供性能分析插件,若是超過這個時間就中止運行!
一、導入插件
/** * SQL執行效率插件 */ @Bean @Profile({"dev","test"})// 設置 dev test 環境開啓,保證咱們的效率 public PerformanceInterceptor performanceInterceptor() { PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor(); performanceInterceptor.setMaxTime(100); //ms 設置sql執行的最大時間,若是超過了則不執行 performanceInterceptor.setFormat(true); return performanceInterceptor; }
@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對比一下 }
@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); }
@Test void test3(){ // 查詢年齡在 20 ~ 30 歲之間的用戶 QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.between("age",20,30); // 區間 Integer count = userMapper.selectCount(wrapper);// 查詢結果數 System.out.println(count); }
// 模糊查詢 @Test void test4(){ // 查詢名字中不帶e且 郵箱以t開頭的數據 QueryWrapper<User> wrapper = new QueryWrapper<>(); // 左 likelift %t ,右 likeRight 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); }
//測試六 @Test void test6(){ QueryWrapper<User> wrapper = new QueryWrapper<>(); // 經過id進行排序 wrapper.orderByAsc("id"); List<User> users = userMapper.selectList(wrapper); users.forEach(System.out::println); }
能夠在test文件夾在建立一個java類
// 代碼自動生成器 public class generateCode { public static void main(String[] args) { // 須要構建一個 代碼自動生成器 對象 AutoGenerator mpg = new AutoGenerator(); // 配置策略 // 一、全局配置 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); //二、設置數據源 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); //三、包的配置 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); //四、策略配置 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(); //執行 } }
執行主方法便可生成對應代碼
感謝你看到這裏,文章有什麼不足還請指正,以爲文章對你有幫助的話記得給我點個贊,天天都會分享java相關技術文章或行業資訊,歡迎你們關注和轉發文章!