@
上篇我們說了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語法的學習和常用插件的配置,那么就盡快使用起來吧;