MyBatis-Plus (opens new window)(簡稱 MP)是一個 MyBatis (opens new window)的加強工具,在 MyBatis 的基礎上只作加強不作改變,爲簡化開發、提升效率而生。國人的驕傲呀!!html
具體代碼下載:java
git clone https://gitee.com/antLaddie/mybatis_plus_demo.gitmysql
咱們在Mapper層上使用CRUD時就須要在具體的接口上繼承BaseMapper接口,爲Mybatis-Plus啓動時自動解析實體表關係映射轉換爲Mybatis內部對象注入容器;其中Searializable爲任意類型的主鍵,Mybatis-Plus不推薦使用複合主鍵約定每一張表都有本身的惟一ID主鍵git
Mybatis_Plus基本必備註解說明:
①:@TableId: 此註解表明當前實體字段對應數據庫表主鍵
type:設置主鍵生成類型
value:設置實體字段與數據庫字段映射不一致
②:@TableField 此註解表明當前實體字段對應數據庫表普通字段
value:設置實體字段與數據庫字段映射不一致
fill: 設置填充模式
③:@TableName 映射到數據庫表名稱
value:具體映射表名
// 插入一條記錄 T entity 傳入對應的實體類便可 int insert(T entity);
注意事項:在使用保存或根據ID查詢、更新、刪除時,主鍵字段必須添加@TableId算法
/** * @Auther: xiaoYang * @Date: 2021/4/6 12:38 * @Description: 學生實體類 */ //下面四個都是lombok插件註解 @Data @AllArgsConstructor @NoArgsConstructor @ToString public class Student implements Serializable { @TableId //必須添加此註解表明當前實體的主鍵 private Long sid; //學生ID private String sname; //姓名 private String ssex; //性別 private Integer sage; //年齡 private String saddress; //住址 private Integer isDel; //是否被刪除 0未刪除 1刪除 private Date createTime; //數據行建立時間 private Date updateTime; //數據行更新時間 private Integer version; //版本(用於樂觀鎖) }
Mapper層上也實現了特別多的基本查詢方法,如id查詢、條件查詢、分頁查詢、統計查詢...spring
## 參數類型 類型 參數名 描述 Serializable id 主鍵ID Wrapper<T> queryWrapper 實體對象封裝操做類(能夠爲 null) Collection<? extends Serializable> idList 主鍵ID列表(不能爲 null 以及 empty) Map<String, Object> columnMap 表字段 map 對象 IPage<T> page 分頁查詢條件(能夠爲 RowBounds.DEFAULT) ## 具體方法 // 根據 ID 查詢 T selectById(Serializable id); // 根據 entity 條件,查詢一條記錄 // 指定的條件查詢查詢到多條數據則出現異常 T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 查詢(根據ID 批量查詢) // 批量查詢 底層使用的是 where xx in ( *** ) IN關鍵字 List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList); // 根據 entity 條件,查詢所有記錄 全表查詢則傳入null List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 查詢(根據 columnMap 條件) // 傳入map類型,多個鍵值則爲 aaa = xxx AND bbb = xxx List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap); // 根據 Wrapper 條件,查詢所有記錄 //以鍵值對的形式返回 [ {xx=xx,...},{xx=xx,...} ] List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 根據 Wrapper 條件,查詢所有記錄。注意: 只返回第一個字段的值 List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 根據 entity 條件,查詢所有記錄(並翻頁) 分頁後面專門說 IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 根據 Wrapper 條件,查詢所有記錄(並翻頁) 分頁後面專門說 IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 根據 Wrapper 條件,查詢總記錄數 Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// ### 示例 @Test public void selectList() { //使用條件構造器模糊查詢帶 %壯%的名稱 QueryWrapper<Student> wrapper = new QueryWrapper<Student>().like("sname", "壯"); List<Student> students = studentMapper.selectList(wrapper); students.forEach(System.out::println); }
Mapper也提供了更新方法,能夠根據ID來更新一條,或者根據條件查詢更新多條sql
// 根據 ID 修改 int updateById(@Param(Constants.ENTITY) T entity); // 根據 whereEntity 條件,更新記錄 int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper);
// 根據 ID 修改 一次更新一個 // int updateById(@Param(Constants.ENTITY) T entity); @Test public void updateById() { //設置基本信息 Student stu = new Student(); stu.setSname("螞蟻小哥"); stu.setSsex("男"); stu.setSaddress("上海浦東"); stu.setSid(1379438319679049730L); //必須設置ID 要否則找不到 int i = studentMapper.updateById(stu); System.out.println("更新記錄條數:" + i); } // 根據 whereEntity 條件,更新記錄 能夠一次性更新多個 // int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper); @Test public void update() { //設置基本信息 更改年齡66 Student stu = new Student(); stu.setSage(66); int i = studentMapper.update(stu, new QueryWrapper<Student>().eq("ssex", "保密")); System.out.println("更新記錄條數:" + i); }
Mapper其實也提供了刪除方法,如根據ID刪除、批量刪除、條件刪除....數據庫
// 根據 entity 條件,刪除記錄 int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper); // 刪除(根據ID 批量刪除) int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList); // 根據 ID 刪除 int deleteById(Serializable id); // 根據 columnMap 條件,刪除記錄 int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
// 根據 entity 條件,刪除記錄 // int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper); @Test public void delete() { // 刪除名字中帶 %壯% 的名字 int i = studentMapper.delete(new QueryWrapper<Student>().like("sname", "壯")); System.out.println("刪除個數:" + i); } // 刪除(根據ID 批量刪除) // int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList); @Test public void deleteBatchIds() { // 傳入集合 刪除ID爲 2 4 6 8 int i = studentMapper.deleteBatchIds(Arrays.asList(2, 4, 6, 8)); System.out.println("刪除個數:" + i); }
細心的話,你們會發現,寫完了分頁查詢後卻不起效果,其實這是由於分頁查詢是須要配置Mybatis-Plus指定插件的;但要注意的是,分頁查詢和咱們上面的普通查詢是不同的,咱們得配置mybatis-plus特有的分頁插件纔可使用,具體配置以下:apache
@Configuration @MapperScan(basePackages = "cn.xw.mapper") //映射mapper掃描 public class MybatisPlusConfig { //在這個Bean對象裏面配置咱們所需的插件,這裏咱們只須要配置一個分頁插件便可 @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { //攔截器對象 MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); //建立分頁插件 並指定爲mysql數據庫方言 也能夠經過對象set設置各類屬性 PaginationInnerInterceptor pageInterceptor = new PaginationInnerInterceptor(DbType.MYSQL); //配置的每頁最多顯示多少條數據 pageInterceptor.setMaxLimit(10L); // 放入添加分頁攔截器插件 interceptor.addInnerInterceptor(pageInterceptor); return interceptor; } }
@SpringBootTest public class PageTests { @Autowired private StudentMapper studentMapper; // 根據 entity 條件,查詢所有記錄(並翻頁) // IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper); @Test public void selectPage() { //分頁對象 Page<Student> page = new Page<>(1, 10); //分頁查詢 Page<Student> pageResult = studentMapper.selectPage(page, null); System.out.println("總記錄數:" + pageResult.getTotal()); System.out.println("每頁總數" + pageResult.getSize()); System.out.println("當前頁:" + pageResult.getCurrent()); System.out.println("排序字段信息:" + pageResult.getOrders()); pageResult.getRecords().forEach(System.out::println); } // 根據 Wrapper 條件,查詢所有記錄(並翻頁) // IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper); @Test public void selectMapsPage() { //分頁對象 Page<Map<String, Object>> page = new Page<>(1, 5); //分頁查詢 Page<Map<String, Object>> pageResult = studentMapper.selectMapsPage(page, null); System.out.println("總記錄數:" + pageResult.getTotal()); System.out.println("每頁總數" + pageResult.getSize()); System.out.println("當前頁:" + pageResult.getCurrent()); System.out.println("排序字段信息:" + pageResult.getOrders()); List<Map<String, Object>> list = pageResult.getRecords(); for (Map<String, Object> li : list) { System.out.println("姓名:" + li.get("sname") + " 地址:" + li.get("saddress")); } } }
其實mybatis-plus內置了幾個主鍵增加策略,咱們只須要經過@TableId註解裏的type來完成對其設置,也內置了主鍵增加策略緩存
## ASSIGN_ID(雪花算法)
若是咱們不設置主鍵類型值,默認則使用 IdType.ASSIGN_ID 策略(自3.3.0起)該策略會使用雪花算法自動生成主鍵ID,主鍵類型爲長整型或字符串(分別對應的MySQL的表字段爲BIGINT和VARCHAR)
該策略使用接口IdentifierGenerator的方法nextId(以實現類爲DefaultIdentifierGenerator雪花算法)
雪花算法(雪花)是微博開源的分佈式ID生成算法其核心思想就是:使用一個64位的長整型的數字做爲全局惟一ID。在分佈式系統中的應用十分普遍,且ID引入了時間戳,基本上保持自增的
@Data public class Student implements Serializable { @TableId(type = IdType.ASSIGN_ID) //使用雪花算法生成主鍵 不寫也是使用雪花算法 private Long sid; //學生ID private String sname; //姓名 ......省略 }
## ASSIGN_UUID(去除了中橫線的UUID)
若是使用 IdType.ASSIGN_UUID 策略,並從新自動生成排除中劃線的UUID做爲主鍵。主鍵類型爲String,對應MySQL的表分段爲VARCHAR(32)
該策略使用接口IdentifierGenerator的方法nextUUID
@Data public class Student implements Serializable { @TableId(type = IdType.ASSIGN_UUID) //使用了UUID(沒有中橫線)的方式生成主鍵 注意的是主鍵字段是varchar(32)才行 private Long sid; //學生ID private String sname; //姓名 ......省略 }
## AUTO(數據庫自增加 適用於MySQL)
對於MySQL適用於這種自增加,可是咱們在建立數據庫字段主鍵時別忘了設置auto_increment
@Data public class Student implements Serializable { // 對於像MySQL這樣的支持主鍵自動遞增的數據庫,咱們可使用IdType.AUTO策略 @TableId(type = IdType.AUTO) // 特別注意 數據庫要加 auto_increment private Long sid; //學生ID private String sname; //姓名 ......省略 }
## INPUT(插入前自行設置主鍵自增)
針對有序列的數據庫:Oracle,SQLServer等,當須要創建一個自增序列時,須要用到序列
在Oracle 11g中,設置自增擴,須要先建立序列(SQUENCE)再建立一個觸發器(TRIGGER)
在Oracle 12c中,只須要使用IDENTITY屬性就能夠了,和MySQL同樣簡單
Mybatis -Plus已經定義好了常見的數據庫主鍵序列,咱們首先只須要在@Configuration類中定義好@Bean:
DB2KeyGenerator、H2KeyGenerator、KingbaseKeyGenerator、OracleKeyGenerator、PostgreKeyGenerator
// 配置類 @SpringBootConfiguration @MapperScan(basePackages = "cn.xw.mapper") //映射包掃描 public class MybatisPlusConfig { // 配置主鍵序列方式 @Bean public IKeyGenerator keyGenerator() { //建立Oracle序列 return new OracleKeyGenerator(); } }
而後實體類配置主鍵Sequence,指定主鍵策略爲IdType.INPUT便可;支持父類定義@KeySequence子類使用,這樣就能夠幾個表共享一個Sequence
若是主鍵是String類型的,也可使用;如何使用序列做爲主鍵,可是實體主鍵類型是字符串開頭,表的主鍵是varchar2,可是須要從序列中取值
實體定義@KeySequence註解clazz指定類型String.class
實體定義主鍵的類型字符串
注意:oracle的序列返回的是Long類型,若是主鍵類型是Integer,可能會引發ClassCastException
@KeySequence(value = "SQL_TEST", clazz = String.class) public class Student implements Serializable { @TableId(type = IdType.INPUT) private Long sid; //學生ID private String sname; //姓名 ...後面省略 }
咱們試想這樣一個場景,咱們每次添加數據時有個字段是記錄數據建立時間的createTime;那麼咱們修改數據時是要由updateTime來記錄咱們何時修改了數據;前提咱們使用自動填充功能實現時數據庫建立類型不要以timestamp時間戳,由於這樣數據庫層面就能夠實現此功能
自動填充功能不光能夠實現時間的自動填充,其它的咱們有需求都是能夠實現的
實現元對象處理器接口:com.baomidou.mybatisplus.core.handlers.MetaObjectHandler
註解填充字段 @TableField(.. fill = FieldFill.INSERT)
生成器策略部分也能夠配置
@Data @AllArgsConstructor @NoArgsConstructor @ToString @TableName(value = "student") // 數據庫表名稱 public class Student implements Serializable { //雪花ID @TableId(type = IdType.ASSIGN_ID)private Long sid; //學生ID private String sname; //姓名 private String ssex; //性別 private Integer sage; //年齡 private String saddress; //住址 private Integer isDel; //是否被刪除 0未刪除 1刪除 @TableField(value = "create_time", fill = FieldFill.INSERT) private Date createTime; //數據行建立時間 @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE) private Date updateTime; //數據行更新時間 private Integer version; //版本(用於樂觀鎖) }
@Slf4j //日誌 @Component //加入容器 public class MyMetaObjectHandler implements MetaObjectHandler { // 添加數據時的填充方案 @Override public void insertFill(MetaObject metaObject) { log.info("start insert fill ...."); Date currentDate = new Date(); // 插入建立時間 if (metaObject.hasSetter("createTime")) { this.strictInsertFill(metaObject, "createTime", Date.class, currentDate); } // 同時設置修改時間爲當前插入時間 if (metaObject.hasSetter("updateTime")) { this.strictUpdateFill(metaObject, "updateTime", Date.class, currentDate); } //this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推薦使用) } // 更新數據時的填充方案 @Override public void updateFill(MetaObject metaObject) { log.info("start update fill ...."); this.strictInsertFill(metaObject, "updateTime", Date.class, new Date()); // 起始版本 3.3.0(推薦使用) } }
public enum FieldFill { //取值範圍 //默認不處理 DEFAULT, //插入填充字段 INSERT, //更新填充字段 UPDATE, //插入和更新填充字段 INSERT_UPDATE }
其實邏輯刪除不是真正來刪除具體的數據,而是要對刪除的數據使用某個字段來記錄當前數據是否被刪除了,咱們一般使用 is_del 來記錄 0 表明未刪除 1 表明被刪除;此時個人表是存在 is_del 字段的,因此咱們就能夠認爲每次刪除就等於更新 is_del 字段值;可是咱們得對mybatis_plus配置後才能夠生效
在application.yml裏配置mybatis-plus:標紅的纔是咱們要配置的
mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 日誌輸出到控制檯 global-config: db-config: logic-delete-field: isDel # 全局邏輯刪除的實體字段名(since 3.3.0,配置後能夠忽略實體上不配置 @TableLogic) logic-not-delete-value: 0 # 邏輯未刪除值(默認爲 0) logic-delete-value: 1 # 邏輯已刪除值(默認爲 1)
在實體類上添加 @TableLogic 註解:
@TableName(value = "student")
public class Student implements Serializable {
//使用雪花算法生成主鍵 不寫也是使用雪花算法
@TableId(type = IdType.ASSIGN_ID)
@TableField(value = "sid")
private Long sid; //學生ID
......省略部分
@TableLogic // 在配置裏配置了logic-delete-field屬性後可不加此註解 在3.3.0版本以上才能夠不加
private Integer isDel; //是否被刪除 0未刪除 1刪除
......省略部分
}
還有一個問題就是,咱們在添加數據的時候,咱們得設置 is_del 字段數據值爲 0 (未刪除)嗎?其實這可使用自動填充功能完成,可是最好使用在數據庫定義默認值 is_del int default 0
在學習插件時首先介紹一下 MybatisPlusInterceptor 它是核心插件 目前代理了 Executor#query 和 Executor#update 和StatementHandler#prepare 方法
MybatisPlusInterceptor 屬性: private List<InnerInterceptor> interceptors = new ArrayList<>(); InnerInterceptor:它是一個接口,咱們提供的插件都將基於此接口來實現功能 目前以實現此接口的類: 自動分頁: PaginationInnerInterceptor 多租戶: TenantLineInnerInterceptor 動態表名: DynamicTableNameInnerInterceptor 樂觀鎖: OptimisticLockerInnerInterceptor sql性能規範: IllegalSQLInnerInterceptor 防止全表更新與刪除: BlockAttackInnerInterceptor
注意:
使用多個功能須要注意順序關係,建議使用以下順序
總結: 對sql進行單次改造的優先放入,不對sql進行改造的最後放入#使用方式(以分頁插件舉例)
在上面的分頁查詢有過介紹
樂觀鎖採用了更加寬鬆的加鎖機制,他相信其它人不會來和它爭搶鎖的,因此樂觀鎖更傾向於開發運用,不對數據進行加鎖,能夠提升數據庫的性能,而悲觀鎖就有點悲觀了,利用數據庫的鎖機制實現,把要操做的數據或者表進行加鎖,其它人不給操做,只有等它操做完了才輪到後面人使用,這每每來講就加大了數據庫的性能開銷
在mybatis-plus裏的樂觀鎖實現方式: ①:取出記錄時,獲取當前version字段值 ②:更新時,帶上version字段值 ③:執行更新時比對獲取的version和數據庫已有的version是否一致 set version = newVersion where version = oldVersion ④:若是提交的version和數據庫裏的version不對應則更新失敗
首先配置樂觀鎖插件:
@SpringBootConfiguration //聲明爲配置類 @MapperScan(basePackages = "cn.xw.mapper") //映射包掃描 public class MybatisPlusConfig { //Mybatis_plus攔截器 @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { //建立插件核心類對象(攔截器) MybatisPlusInterceptor plusInterceptor = new MybatisPlusInterceptor(); //建立樂觀鎖插件 對更新生效 並添加到核心插件對象實力裏 OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor = new OptimisticLockerInnerInterceptor(); plusInterceptor.addInnerInterceptor(optimisticLockerInnerInterceptor); //返回 return plusInterceptor; } }
而後在實體類上的version(名字隨便)必定要加上@Version註解
其實咱們在進行更新或者刪除數據時可能會一不當心刪除了所有,這都是由於咱們忘傳條件,或者全匹配條件,這樣就會形成數據的所有更新或者刪除,這顯然是不能夠的,因此mybatis-plus爲咱們提供了防止全表刪除或者更新插件
首先咱們去配置類配置 防止全表刪除更新插件:
@SpringBootConfiguration //聲明爲配置類 @MapperScan(basePackages = "cn.xw.mapper") //映射包掃描 public class MybatisPlusConfig { //Mybatis_plus攔截器 @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { //建立插件核心類對象(攔截器) MybatisPlusInterceptor plusInterceptor = new MybatisPlusInterceptor(); //建立樂觀鎖插件 對更新生效 並添加到核心插件對象實力裏 OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor = new OptimisticLockerInnerInterceptor(); plusInterceptor.addInnerInterceptor(optimisticLockerInnerInterceptor); //建立 防止全表更新與刪除插件(順序必須在樂觀鎖插件後面)並添加到核心插件對象實力裏 BlockAttackInnerInterceptor blockAttackInnerInterceptor = new BlockAttackInnerInterceptor(); plusInterceptor.addInnerInterceptor(blockAttackInnerInterceptor); //返回 return plusInterceptor; } }
//演示批量刪除或者批量修改 @Test void delTest() { int delete = studentMapper.delete(null); System.out.println("批量刪除了:" + delete + " 條數據"); // 防止全表更新與刪除插件加上後執行批量刪除 //### The error occurred while executing an update //解釋:執行更新時發生錯誤 //### Cause: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: // Prohibition of full table deletion //解釋禁止全表刪除 }
咱們在使用插件時和上述操做大同小異,直接建立指定插件並add添加到核心插件對象裏便可
條件構造器抽象類實際上是 AbstractWrapper 這個抽象類裏面有特別多的條件方法,而它的實現類有 QueryWrapper 查詢條件構造器和 UpdateWrapper 更新條件構造器,咱們在查詢的時候就用QueryWrapper;在接下來的例子中我都以QueryWrapper實現類來講明
allEq: 多條件匹配查詢所有 isNull: 字段 IS NULL eq: 等於 = isNotNull: 字段 IS NOT NULL ne: 不等於 <> in: 字段 IN (v0, v1, ...) gt: 大於 > notIn: 字段 NOT IN (v0, v1, ...) ge: 大於等於 >= or: 拼接 OR lt: 小於 < and: AND 嵌套 le: 小於等於 <= inSql: 字段 IN ( sql語句 ) between: BETWEEN 值1 AND 值2 notInSql: 字段 NOT IN ( sql語句 ) notBetween: NOT BETWEEN 值1 AND 值2 groupBy: 分組:GROUP BY 字段, ... like: LIKE '%值%' orderByAsc: 排序:ORDER BY 字段, ... ASC notLike: NOT LIKE '%值%' orderByDesc: 排序:ORDER BY 字段, ... DESC likeLeft: LIKE '%值' orderBy: 排序:ORDER BY 字段, ... likeRight: LIKE '值%' having: HAVING ( sql語句 ) func: func 方法(主要方便在出現if...else下調用不一樣方法能不斷鏈) nested: 正常嵌套 不帶 AND 或者 OR apply: 拼接 sql last: 無視優化規則直接拼接到 sql 的最後 exists: 拼接 EXISTS ( sql語句 ) notExists: 拼接 NOT EXISTS ( sql語句 )
allEq(Map<R, V> params) allEq(Map<R, V> params, boolean null2IsNull) allEq(boolean condition, Map<R, V> params, boolean null2IsNull) eq(R column, Object val) eq(boolean condition, R column, Object val) 參數說明: Map<R, V> params :傳入Map鍵值對 R 表字段 V 值 boolean null2IsNull:傳入的map鍵值對中,爲true是,若是有鍵值爲null的話是否以isNull查詢 是否參數爲 null 自動執行 isNull 方法, false 則忽略這個字段 boolean condition:執行條件默認true,false 忽略咱們條件
// 多條件查詢匹配條件 allEq @Test void allEqTest() { //建立查詢對象條件 QueryWrapper<Student> wrapper = new QueryWrapper<>(); //建立查詢條件參數 Map<String, Object> map = new HashMap<>(); map.put("sage", 20); map.put("ssex", null); wrapper.allEq(true, map, true); List<Student> students = studentMapper.selectList(wrapper); students.forEach(System.out::println); } // 條件查詢 等於 = @Test void eqTest() { //建立查詢對象條件所有爲男生數據 並查詢 List<Student> students = studentMapper .selectList(new QueryWrapper<Student>().eq("ssex", "男")); students.forEach(System.out::println); }
ne(R column, Object val) ne(boolean condition, R column, Object val) gt(R column, Object val) gt(boolean condition, R column, Object val) ge(R column, Object val) ge(boolean condition, R column, Object val) lt(R column, Object val) lt(boolean condition, R column, Object val) le(R column, Object val) le(boolean condition, R column, Object val)
// 都差很少,以 gt爲例 // ne <> ; gt > ; ge >= ; lt < ; le <= 查詢 // .... FROM student WHERE (sage > ? AND ssex <> ?) @Test void gtTest(){ List<Student> students = studentMapper .selectList(new QueryWrapper<Student>() .gt("sage",21) .ne("ssex","保密")); students.forEach(System.out::println); }
//綜合編寫 @Test void sumTest(){ //建立查詢對象條件 QueryWrapper<Student> wrapper = new QueryWrapper<>(); //條件:查詢學生id在5~15之間 wrapper.between("sid",4,15); //條件:模糊查詢學生中不帶 %壯% 子的名字 wrapper.notLike("sname","%壯%"); List<Student> students = studentMapper.selectList(wrapper); students.forEach(System.out::println); }
該功能依賴 p6spy 組件,能夠完美輸出打印SQL執行時常及其它信息
<!--性能測試--> <dependency> <groupId>p6spy</groupId> <artifactId>p6spy</artifactId> <version>3.9.1</version> </dependency>
更改配置application.yml配置:
spring: # 設置數據庫鏈接信息 datasource: driver-class-name: com.p6spy.engine.spy.P6SpyDriver url: jdbc:p6spy:mysql://localhost:3306/demo_m...... .....
在resources資源目錄下建立spy.properties配置:
#3.2.1以上使用 modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory #3.2.1如下使用或者不配置 #modulelist=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,commit,resultset # 日期格式 dateformat=yyyy-MM-dd HH:mm:ss # 實際驅動可多個 #driverlist=org.h2.Driver # 是否開啓慢SQL記錄 outagedetection=true # 慢SQL記錄標準 2 秒 outagedetectioninterval=2
而後就和咱們正常使用同樣來執行SQL語句查詢等其它操做;在控制檯會打印當前的運行sql執行時間等信息 以下打印:
Consume Time:17 ms 2021-04-12 19:37:25
Execute SQL:SELECT sid ...... FROM student WHERE (sid BETWEEN 4 AND 15 AND sname NOT LIKE '%%壯%%')
AutoGenerator 是 MyBatis-Plus 的代碼生成器,經過 AutoGenerator 能夠快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各個模塊的代碼,極大的提高了開發效率
在使用代碼生成器以前咱們須要導入相應座標
<!--代碼生成器座標 我mybatis-plus版本是3.4.1--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.4.1</version> </dependency> <!--下面兩個根據本身選擇的模板導入其中一個便可--> <!--模板座標 freemarker--> <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.31</version> </dependency> <!--模板座標 velocity-engine-core--> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity-engine-core</artifactId> <version>2.3</version> </dependency>
在學習代碼生成器以前資料的準備
public void fun() { System.out.println("java版本號:" + System.getProperty("java.version")); // java版本號 System.out.println("Java提供商名稱:" + System.getProperty("java.vendor")); // Java提供商名稱 System.out.println("Java提供商網站:" + System.getProperty("java.vendor.url")); // Java提供商網站 System.out.println("jre目錄:" + System.getProperty("java.home")); // Java,哦,應該是jre目錄 System.out.println("Java虛擬機規範版本號:" + System.getProperty("java.vm.specification.version")); // Java虛擬機規範版本號 System.out.println("Java虛擬機規範提供商:" + System.getProperty("java.vm.specification.vendor")); // Java虛擬機規範提供商 System.out.println("Java虛擬機規範名稱:" + System.getProperty("java.vm.specification.name")); // Java虛擬機規範名稱 System.out.println("Java虛擬機版本號:" + System.getProperty("java.vm.version")); // Java虛擬機版本號 System.out.println("Java虛擬機提供商:" + System.getProperty("java.vm.vendor")); // Java虛擬機提供商 System.out.println("Java虛擬機名稱:" + System.getProperty("java.vm.name")); // Java虛擬機名稱 System.out.println("Java規範版本號:" + System.getProperty("java.specification.version")); // Java規範版本號 System.out.println("Java規範提供商:" + System.getProperty("java.specification.vendor")); // Java規範提供商 System.out.println("Java規範名稱:" + System.getProperty("java.specification.name")); // Java規範名稱 System.out.println("Java類版本號:" + System.getProperty("java.class.version")); // Java類版本號 System.out.println("Java類路徑:" + System.getProperty("java.class.path")); // Java類路徑 System.out.println("Java lib路徑:" + System.getProperty("java.library.path")); // Java lib路徑 System.out.println("Java輸入輸出臨時路徑:" + System.getProperty("java.io.tmpdir")); // Java輸入輸出臨時路徑 System.out.println("Java編譯器:" + System.getProperty("java.compiler")); // Java編譯器 System.out.println("Java執行路徑:" + System.getProperty("java.ext.dirs")); // Java執行路徑 System.out.println("操做系統名稱:" + System.getProperty("os.name")); // 操做系統名稱 System.out.println("操做系統的架構:" + System.getProperty("os.arch")); // 操做系統的架構 System.out.println("操做系統版本號:" + System.getProperty("os.version")); // 操做系統版本號 System.out.println("文件分隔符:" + System.getProperty("file.separator")); // 文件分隔符 System.out.println("路徑分隔符:" + System.getProperty("path.separator")); // 路徑分隔符 System.out.println("直線分隔符:" + System.getProperty("line.separator")); // 直線分隔符 System.out.println("操做系統用戶名:" + System.getProperty("user.name")); // 用戶名 System.out.println("操做系統用戶的主目錄:" + System.getProperty("user.home")); // 用戶的主目錄 System.out.println("當前程序所在目錄:" + System.getProperty("user.dir")); // 當前程序所在目錄 }
package cn.xw; import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.annotation.FieldFill; import com.baomidou.mybatisplus.core.toolkit.StringPool; import com.baomidou.mybatisplus.generator.AutoGenerator; import com.baomidou.mybatisplus.generator.InjectionConfig; import com.baomidou.mybatisplus.generator.config.*; import com.baomidou.mybatisplus.generator.config.po.TableFill; import com.baomidou.mybatisplus.generator.config.po.TableInfo; import com.baomidou.mybatisplus.generator.config.rules.DateType; import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy; import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine; import java.util.ArrayList; import java.util.List; /** * @Auther: xiaoYang * @Date: 2021/4/13 14:29 * @Description: */ public class GeneratorCodeUtils { public static void main(String[] args) { //設置做者信息 String author = "xiaoYang"; //數據庫URL / username / password String url = "jdbc:mysql://localhost:3306/demo_mp?useSSL=true&useUnicode=true&" + "characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&" + "useLegacyDatetimeCode=false&serverTimezone=GMT%2B8"; String username = "root"; String password = "123"; //這個include 和exclude只能存在一個 String[] include = {"student"}; String[] exclude = {}; //樂觀鎖屬性名稱 和數據庫表字段保持一致 String version = "version"; //邏輯刪除屬性名稱 和數據庫表字段保持一致 String isDel = "is_del"; //代碼生成器 AutoGenerator autoGenerator = new AutoGenerator(); //全局相關配置 GlobalConfig gc = new GlobalConfig(); String propertyPath = System.getProperty("user.dir"); gc.setOutputDir(propertyPath + "./src/main/java") .setAuthor(author) .setDateType(DateType.ONLY_DATE) .setEnableCache(false) .setBaseResultMap(true) .setBaseColumnList(true) .setOpen(false) .setServiceName("%sService"); autoGenerator.setGlobalConfig(gc); //數據源配置 DataSourceConfig dsc = new DataSourceConfig(); dsc.setDbType(DbType.MYSQL); dsc.setDriverName("com.mysql.cj.jdbc.Driver"); dsc.setUrl(url); dsc.setUsername(username); dsc.setPassword(password); autoGenerator.setDataSource(dsc); //包配置 PackageConfig pgc = new PackageConfig(); pgc.setParent("cn.xw"); autoGenerator.setPackageInfo(pgc); //自定義配置 InjectionConfig cfg = new InjectionConfig() { @Override public void initMap() { } }; String templatePath = "/templates/mapper.xml.ftl"; ArrayList<FileOutConfig> focList = new ArrayList<>(); focList.add(new FileOutConfig(templatePath) { @Override public String outputFile(TableInfo tableInfo) { // 自定義輸出文件名 , 若是你 Entity 設置了先後綴、此處注意 xml 的名稱會跟着發生變化!! return propertyPath + "/src/main/resources/mapper/" + pgc.getModuleName() + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML; } }); cfg.setFileOutConfigList(focList); autoGenerator.setCfg(cfg); //模板信息配置 TemplateConfig templateConfig = new TemplateConfig(); templateConfig.setXml(null); autoGenerator.setTemplate(templateConfig); //其它信息配置 //策略配置 StrategyConfig strategy = new StrategyConfig(); strategy.setNaming(NamingStrategy.underline_to_camel); strategy.setColumnNaming(NamingStrategy.underline_to_camel); strategy.setInclude(include); //排除生成的表名稱,就是寫數據庫表名稱 String ... include strategy.setExclude(exclude); //strategy.setFieldPrefix(""); strategy.setTablePrefix(""); strategy.setEntityLombokModel(true); strategy.setRestControllerStyle(true); //表填充字段 List<TableFill> fill = new ArrayList<>(); fill.add(new TableFill("create_time", FieldFill.INSERT)); fill.add(new TableFill("update_time", FieldFill.INSERT_UPDATE)); strategy.setTableFillList(fill); strategy.setVersionFieldName(version); strategy.setLogicDeleteFieldName(isDel); autoGenerator.setStrategy(strategy); autoGenerator.setTemplateEngine(new FreemarkerTemplateEngine()); autoGenerator.execute(); } }
代碼生成器具體詳細註釋:
public static void main(String[] args) { //記得生成成功以後,在測試運行的時候要在主啓動類上添加@MapperScan(value = 「」) //################## 基本信息準備 ################## //獲取當前程序所在目錄 如:h://demo_mybatis_test String propertyPath = System.getProperty("user.dir"); //建立代碼生成器實例對象 (主要) AutoGenerator autoGenerator = new AutoGenerator(); //################## 全局相關配置 ################## GlobalConfig gc = new GlobalConfig(); //生成文件的輸出目錄【默認 D:\\ 盤根目錄】 gc.setOutputDir(propertyPath + "./src/main/java"); //設置做者信息 gc.setAuthor("xiaoYang"); //時間類型對應策略 // ONLY_DATE:使用 java.util.date 代替 // SQL_PACK:使用 java.sql 包下的 // TIME_PACK:使用 java.time 包下的 java8 新的時間類型 gc.setDateType(DateType.ONLY_DATE); //是否在xml中添加二級緩存配置 默認false gc.setEnableCache(false); //是否在XML裏建立 ResultMap開啓 就是建立<resultMap></resultMap>標籤 默認false gc.setBaseResultMap(true); //是否在XML裏建立 columnList開啓 就是建立<sql> 表所有字段 </sql>標籤 默認false gc.setBaseColumnList(true); //是否打開輸出目錄 就是說建立完是否彈出window文件夾位置 gc.setOpen(false); //!!!各層文件名稱方式,註釋的所有是默認的 例如: I%sService 生成 IStudentService //gc.setEntityName("%s"); //gc.setMapperName("%sMapper"); //gc.setXmlName("%sMapper"); gc.setServiceName("%sService"); //gc.setServiceImplName("%sServiceImpl"); //gc.setControllerName("%sController"); //實體屬性 Swagger2 註解 //gc.setSwagger2(true); // 把全局配置添加到 代碼生成器實例裏 autoGenerator.setGlobalConfig(gc); //################## 數據源配置 ################## DataSourceConfig dsc = new DataSourceConfig(); //數據庫類型 dsc.setDbType(DbType.MYSQL); //設置數據庫驅動 此處是mysql8.0版本 dsc.setDriverName("com.mysql.cj.jdbc.Driver"); // 數據庫連接URL 我這裏使用的是MySQL8的連接URL 同樣的 dsc.setUrl("jdbc:mysql://localhost:3306/demo_mp?useSSL=true&useUnicode=true&" + "characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&" + "useLegacyDatetimeCode=false&serverTimezone=GMT%2B8"); // 設置數據庫用戶名 dsc.setUsername("root"); // 設置數據庫密碼 dsc.setPassword("123"); // 架構名稱 默認就行 //dsc.setSchemaName("public"); //自定義類型轉換 寫這個方法是爲了mybatis-plus根據數據庫類型自動生成的實體類型不符合咱們要求是才自定義 //當生成的model實體類,java類型不知足時能夠自定義轉換 dsc.setTypeConvert(new ITypeConvert() { //數據庫類型datetime默認生成的java類型爲localDateTime, 下面示例要改爲Date類型 //類型轉換(流程類型轉換方法) @Override public IColumnType processTypeConvert(GlobalConfig globalConfig, String fieldType) { //獲取數據庫類型字符串轉爲小寫 String t = fieldType.toLowerCase(); //判斷當前獲取的類型裏是否包含"datetime" 成立的話就轉類型返回 if (t.contains("datetime")) { System.out.println("類型捕捉datetime 將從LocalDateTime類型轉Date"); return DbColumnType.DATE; } //其它字段採用默認轉換(非mysql數據庫可使用其它默認的數據庫轉換器) return new MySqlTypeConvert().processTypeConvert(globalConfig, fieldType); } }); // 把數據源配置添加到 代碼生成器實例裏 autoGenerator.setDataSource(dsc); //################## 包配置 ################## PackageConfig pgc = new PackageConfig(); pgc.setParent("cn.xw") //父包名 默認"com.baomidou" .setPathInfo(null) //路徑配置信息,就是配置各個文件模板的路徑信息 .setModuleName("") //父包模塊名 默認 」「 .setEntity("entity") //實體類包名 默認entity .setService("service") //Service包名 默認」service「 .setServiceImpl("service.impl") //Service裏面的實現類位置 默認」service.impl「 .setMapper("mapper") //Mapper映射包名 默認」mapper「 .setXml("mapper.xml") //Mapper XML包名 默認"mapper.xml" .setController("controller"); //Controller包名 默認」controller「 // 把包配置添加到 代碼生成器實例裏 autoGenerator.setPackageInfo(pgc); //################## 自定義配置 ################## InjectionConfig cfg = new InjectionConfig() { @Override public void initMap() { // 這裏面能夠設置具體的自定義表信息等等 正常不寫 特殊百度學習 //https://www.bookstack.cn/read/mybatis-3.3.2/spilt.7.4267953fc3057caf.md } }; //注意 freemarker 和 velocity 使用哪一個模板就去導入哪一個座標 // 若是模板引擎是 freemarker String templatePath = "/templates/mapper.xml.ftl"; // 若是模板引擎是 velocity // String templatePath = "/templates/mapper.xml.vm"; //自定義輸出配置 就是說找到並定位到咱們生成的xxx(mapper).xml在哪裏了 ArrayList<FileOutConfig> focList = new ArrayList<>(); focList.add(new FileOutConfig(templatePath) { @Override public String outputFile(TableInfo tableInfo) { // 自定義輸出文件名 , 若是你 Entity 設置了先後綴、此處注意 xml 的名稱會跟着發生變化!! return propertyPath + "/src/main/resources/mapper/" + pgc.getModuleName() + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML; } }); //設置自定義輸出文件(就是上面幾行咱們配置的) cfg.setFileOutConfigList(focList); // 把自定義配置添加到 代碼生成器實例裏 autoGenerator.setCfg(cfg); //################## 模板信息配置 ################## TemplateConfig templateConfig = new TemplateConfig(); templateConfig.setXml(null); //把模板信息配置添加到 代碼生成器實例裏 autoGenerator.setTemplate(templateConfig); //################## 其它信息配置 ################## //策略配置 StrategyConfig strategy = new StrategyConfig(); //數據庫表映射到實體類的命名策略 此時設置下劃線轉駝峯命名 strategy.setNaming(NamingStrategy.underline_to_camel); //數據庫表字段映射到實體字段的命名策略 此時設置下劃線轉駝峯命名 strategy.setColumnNaming(NamingStrategy.underline_to_camel); //setInclude和setExclude只能存在一個,不然某一個爲空 //setInclude必須寫須要生成的數據庫表 //setExclude根據咱們鏈接的數據庫,排除哪一個表不生產,其它全生成 //須要生成的表名稱,就是寫數據庫表名稱 String ... include strategy.setInclude("student"); //排除生成的表名稱,就是寫數據庫表名稱 String ... include //strategy.setExclude("teacher"); //忽略表字段前綴 strategy.setFieldPrefix("s"); // 忽略表名的前綴 strategy.setTablePrefix(""); // 是否跳過視圖 strategy.setSkipView(true); //使用LomBok 默認false ,設置true注意要導入lombok座標 strategy.setEntityLombokModel(true); //生成的Controller層使用Rest風格 默認不使用 strategy.setRestControllerStyle(true); //公共父類 若是你有本身的baseController的控制器能夠寫上 //strategy.setSuperControllerClass("你本身的父類控制器,沒有就不用設置!"); // 寫於父類中的公共字段 strategy.setSuperEntityColumns("id"); //表填充字段 List<TableFill> fill = new ArrayList<>(); fill.add(new TableFill("create_time", FieldFill.INSERT)); fill.add(new TableFill("update_time", FieldFill.INSERT_UPDATE)); strategy.setTableFillList(fill); //樂觀鎖屬性名稱 strategy.setVersionFieldName("version"); //邏輯刪除屬性名稱 strategy.setLogicDeleteFieldName("is_del"); //把策略配置添加到 代碼生成器實例裏 autoGenerator.setStrategy(strategy); //設置那種模板引擎 autoGenerator.setTemplateEngine(new FreemarkerTemplateEngine()); // 執行生成 autoGenerator.execute(); }
.