mybatis-Plus 實踐篇之CRUD操作


@

上篇我們說了mybaits-plus的逆向工程的操作,這篇我們來說下CRUD操作吧,本來打算寫一篇的,但是篇幅實在有點長;可讀性不好,還是拆一下;

快速開始

這里就不重新建項目引入依賴了,我們直接在上篇的項目中開始
開始之前,我們需要開啟打印下mybatis-plus在控制台打印的sql,只需要在yaml文件中加上如下配置即可

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

測試查詢查看控制台輸出:

我們先來看下BaseMapper中有哪些方法,如下:

查詢操作

添加分頁攔截器,新建MpHandler如下:

package com.demo.study.config;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.context.annotation.Configuration;
import java.util.Date;

@Configuration
public class MpHandler {
     // 分頁插件
    @Bean
    public PaginationInterceptor paginationInterceptor(){
        return new PaginationInterceptor();
    }
}

測試查詢

// 查詢(使用的最多,單個,多個,數量,分頁查詢!)
@Test
void testSelectById(){
    User user = userMapper.selectById(1L);
    System.out.println(user);
}

@Test
void testSelectByIds(){
    // 批量查詢
    List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
    users.forEach(System.out::println);
}

@Test
void testSelectByCount(){
    // 查詢數據量 SELECT COUNT(1) FROM user
    Integer integer = userMapper.selectCount(null);
    System.out.println(integer);
}

@Test
void testSelectByMap(){
    // 簡單的條件查詢 不建議map查詢而是querywrapper.setEntity
    //HashMap<String, Object> map = new HashMap<>();
    //map.put("name","張三");
    //map.put("age",21);
    //List<User> users = userMapper.selectByMap(map);
   QueryWrapper<User> queryWrapper = new QueryWrapper<>();
   User queryUser = new User();
   user.setName("張三").setAge(21);
   queryWrapper.setEntity(user);
   List<User> users = userMapper.selectList(queryWrapper);
   users.forEach(System.out::println);
}
// 分頁實現 limit(sql) PageHelper Mp內置分頁插件(導入即可!)
@Test
void testPage(){ // 當前頁、總頁數
    //1、先查詢總數
    //2、本質還是 LIMIT 0,10 (默認的)
    // 參數 (當前頁,每個頁面的大小!)
    // 以后做分頁就使用Page對象即可!
    Page<User> page = new Page<>(2,5);
    userMapper.selectPage(page,null);
    System.out.println(page.getTotal());
    System.out.println(page.hasNext());
    System.out.println(page.hasPrevious());
    page.getRecords().forEach(System.out::println);
    System.out.println(page.getSize());
}

新增操作

user實體對應字段

// 配置主鍵自增策略 當然還有其他策略可以自行選擇(還有雪花算法,手動設置,uuid等)
@ApiModelProperty(value = "主鍵")
@TableId(value = "id", type = IdType.AUTO)
private Integer id;

//因為上篇文章中我們配置了這兩個時間字段的自動填充策略,
//那么我們在執行插入和更新操作的時候就會自動更新為當前時間
@ApiModelProperty(value = "創建時間")
@TableField(fill = FieldFill.INSERT)
private Date createdDate;

@ApiModelProperty(value = "更新時間")
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updatedDate;

開始之前,我們還需要配置下注解處理器,處理時間的自動填充
MpHandler implements MetaObjectHandler

// 插入的策略
@Override
public void insertFill(MetaObject metaObject) {
    // this.setFieldValByName()設置當前字段的值!
    // String fieldName, Object fieldVal, MetaObject metaObjec
    // 以后只要是插入操作就會自動控制
    // createTime updateTime 使用 new Date() 進行填充
    this.setFieldValByName("createdDate",new Date(),metaObject);
    this.setFieldValByName("updatedDate",new Date(),metaObject);
}
    // 更新策略
@Override
public void updateFill(MetaObject metaObject) {
    this.setFieldValByName("updatedDate",new Date(),metaObject);
}

測試類中測試新增

// 因為這里我們主鍵配置的自增 時間配置了自動填充所以無需我們再處理
@Test
void testInsert(){
    User user = new User();
    user.setAge(18).setName("如花").setCreatedBy("echo");
    userMapper.insert(user);
    // mp 插入成功后主鍵會自動回顯
    System.out.println(userMapper.selectById(user.getId()));
}

注意:mp 沒有提供批量插入,我們需要自己寫xml sql來實現!
這里來回顧下,批量插入的寫法

<insert id="batchInsert" useGeneratedKeys="true" keyProperty="id" parameterType="java.util.List">
        insert into user
        (name, age) values
        <foreach collection="list" item="item" index="index" separator=",">
            <trim prefix="(" suffix=")" suffixOverrides=",">
                #{item.name,jdbcType=VARCHAR},
                #{item.age,jdbcType=INTEGER}
            </trim>
        </foreach>
</insert>

刪除操作

// 刪除(單個,多個)
@Test
void testDeleteById(){
    userMapper.deleteById(1);
}
@Test
void testDeleteByIds(){
    userMapper.deleteBatchIds(Arrays.asList(2,3,4,5));
}

一般,我們刪除都采用邏輯刪除,即將is_deleted改為1即可!

修改操作

開始之前我們先來了解下樂觀鎖、悲觀鎖!
樂觀鎖 : 非常樂觀,無論什么操作都不加鎖!(分布式環境怎么處理沖突問題呢?版本號)
悲觀鎖 : 非常悲觀,無論什么操作都加鎖!(性能問題)
當要更新一條記錄的時候,希望這條記錄沒有被別人更新過,通常的方式就增加一個樂觀鎖字段即可 (version)

MP 對樂觀鎖也進行了支持!

  • 1、添加version注解到字段上面
  • 2、添加 樂觀鎖插件即可!
  • 3、測試就自動帶上了版本號!
    數據庫新增version字段我們設置初始值為1,那么它每次都會去上一次值做比較,如果是原值就證明沒被改動過就更新,如果非原值就更新失敗!
    對應的user實體及數據庫中我們也新增此version字段加上@version注解
@Version
private Integer version;

這里我們在MpHandler中加入 樂觀鎖攔截器

@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor(){
    return new OptimisticLockerInterceptor();
}

接下來我們測試更新操作:

@Test
void testLock(){
    User user = userMapper.selectById(2);
    user.setName("老鐵");
    userMapper.updateById(user);

}


可以看到更新的時候它幫我們自動加上了這個version 字段

接下來我們來模仿下並發的情況下,因為version加鎖更新失敗的情況:

 @Test
void testLockFail(){
    // 老鐵-> 狂鐵
    User initUser = userMapper.selectById(2);
    initUser.setName("狂鐵");

    // x 先修改為 打鐵
    User xUser = userMapper.selectById(2);
    xUser.setName("打鐵");
    userMapper.updateById(xUser);

    // 改成 狂鐵
    userMapper.updateById(initUser);
}

執行查看控制台輸出

可以看到並發的情況下若是版本號被修改了,initUser則會更新失敗!

刪除操作

// 刪除(單個,多個)
@Test
void testDeleteById(){
    userMapper.deleteById(3);
}
@Test
void testDeleteByIds(){
    userMapper.deleteBatchIds(Arrays.asList(4,5,6);
}
// 簡單的條件刪除
@Test
void testDeleteByMap(){
    HashMap<String, Object> map = new HashMap<>();
    map.put("name","張三");
    map.put("age",20);
    userMapper.deleteByMap(map);
}

但是我們實際開發中基本不這么干,只是更新is_deleted字段,改成邏輯刪除!

然后我們查詢的時候只會把沒有刪除的查詢出來,添加@TableLogic

這里因為我們在代碼生成器中指定了刪除標識字段,所以這個注解自動加上了

@TableLogic 所標注的字段是一個邏輯刪除字段

另外我們還需在yaml中增加邏輯刪除值的配置

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  global-config:
    db-config:
      logic-delete-value: 1 #刪除
      logic-not-delete-value: 0 #未刪除

MpHandler 中新增邏輯刪除插件

@Bean
public ISqlInjector sqlInjector(){
    return new LogicSqlInjector();
}

這兩步完了之后,我們再測一下

@Test
void testDeleteById(){
    userMapper.deleteById(4);
}
@Test
void testSelectById(){
    userMapper.selectById(4);
}


可以看到刪除的數據我們這里已經查不到了,查詢的時候自動給我們帶上了is_deleted=0的條件

sql 性能分析插件

MpHandler 中加入以下攔截器,就可以攔截慢sql

// SQL執行效率插件
@Bean
@Profile({"dev","test"})// 設置 dev test 環境開啟
public PerformanceInterceptor performanceInterceptor() {
    PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
    // 允許執行的sql的最長時間 ,默認的單位是ms
    performanceInterceptor.setMaxTime(2000);
    return performanceInterceptor;
}

這里就不演示了,感興趣的可以自行試下,maxTime改成1 試下,哈哈

根據控制台的報錯信息或者說你配置了統一日志打印的話,可以根據里面的報錯信息找到對應的慢sql

然后,我們就可以分析對應的問題(分庫分表,索引,緩存等)

常用的條件構造器

就是大於,等於,小於,包含,不包含,子查詢等

// 條件查詢器
@Test
void testLogicDelete(){
    QueryWrapper<User> wrapper = new QueryWrapper();
    wrapper //
    .isNotNull("name") .ge("age",21); // 大於等於
    .eq("age",0);
    userMapper.delete(wrapper);
}

// 邊界查詢
@Test
void testSelectCount(){
    QueryWrapper<User> wrapper = new QueryWrapper();
    wrapper.between("age",20,25);
    Integer integer = userMapper.selectCount(wrapper);
}

// 精准匹配
@Test
void testSelectList(){
    QueryWrapper<User> wrapper = new QueryWrapper();
    HashMap<String, Object> map = new HashMap<>(16);
    map.put("name","李四");
    wrapper.allEq(map);
    List<User> users = userMapper.selectList(wrapper);
}

// 模糊查詢
@Test
void testSelectMaps(){
    QueryWrapper<User> wrapper = new QueryWrapper();
    wrapper.notLike("name","e")
    .likeRight("email","t");
    List<Map<String,Object>> = userMapper.selectMaps(wrapper);
    maps.forEach(System.out::println);
}

// 子查詢
@Test
void testSelectObjs(){
    QueryWrapper<User> wrapper = new QueryWrapper();
    // 條件in查詢
    wrapper.in("id",1,2,3,4);
    // 子查詢!
    wrapper.inSql("id","select id from user where id < 3");
    List<Object> objects = userMapper.selectObjs(wrapper);
    objects.forEach(System.out::println);
}
// and or
@Test
void testUpdate(){
    // 修改值
    User user = new User();
    user.setAge(99).setName("老佛爺");
    UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
    // 在一些新的框架中,鏈式編程,lambda表達式,函數式接口十分常用!
    updateWrapper.like("name","張三")
    .or(i->i.eq("name","李四")
    .eq("age",0));
    int update = userMapper.update(user, updateWrapper);
}

// 排序
@Test
void selectList(){
    QueryWrapper<User> wrapper = new QueryWrapper();
    wrapper.orderByAsc("id");
    wrapper.orderByDesc("id");
    List<User> users = userMapper.selectList(wrapper);
    users.forEach(System.out::println);
}

@Test
void tSelectList(){
    // 多表查詢解決方案 last (不建議使用,相當於直接拼接到sql 后面)
    // 盡量使用單表查詢
    // 如果要多表查詢,沒有簡便方法,就只有xml 中自己寫了!
    QueryWrapper<User> wrapper = new QueryWrapper();
    wrapper.last("limit 1");
    List<User> users = userMapper.selectList(wrapper);
    users.forEach(System.out::println);
}

多表操作還需添加的配置

如果,單表查詢不滿足我們的要求的話,就需要自己寫sql 查詢了;
自己寫sql的話,我們需要綁定mapper.xml 和實體的關系
mybatis-plus 需要增加以下配置

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  global-config:
    db-config:
      logic-delete-value: 1 # 已刪除
      logic-not-delete-value: 0 # 未刪除
  mapper-locations: classpath*:com/demo/**/*.xml
  type-aliases-package: com.demo.**.entity

pom 文件中增加如下配置:

<build>
    <resources>
       <resource>
         <directory>src/main/java</directory>
            <includes>
                <include>**/*.xml</include>
            </includes>
       </resource>
    </resources>
</build>

好了,至此我們已經完成了mybatis-plus crud語法的學習和常用插件的配置,那么就盡快使用起來吧;


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM