Springboot整合mybatisplus

ssm項目,用mybatisplus很普遍,在原來的mybatis基礎(chǔ)上升級mybatisplus幾乎不用改什么東西,mybatisplus只對mybatis做了功能擴展,不會影響原來的已有功能,單表查詢的時候的確比單純mybatis寫sql自己做映射要節(jié)省很多時間,也方便很多。
baomidou mybatisplus教程文檔可參考:https://baomidou.com/pages/226c21/

步驟

原來的Springboot ssm框架升級mybatisplus主要有以下幾步:
1.注釋掉原來的mybatis引入包
2.引入mybatisplus包
3.啟動類配置@MapperScan注解掃描路徑
4.Spring注入分頁配置
5.實現(xiàn)MetaObjectHandler配置數(shù)據(jù)庫自動填充(非必須)

實現(xiàn)

注釋掉原來的mybatis包
   <!-- <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.0</version>
        </dependency> -->
引入mybatisplus包

maven引入(截止發(fā)文mvn倉庫最新版本是3.5.1):

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.1</version>
        </dependency>
啟動類配置@MapperScan注解掃描路徑

比如我所有的Mapper接口都在com.zhaohy.app.dao包路徑下,則在啟動類加入
@MapperScan("com.zhaohy.app.dao")
貼一下啟動類:

package com.zhaohy.app;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.context.annotation.Bean;

import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure;
import com.zhaohy.app.sys.filter.LoginProcessFilter;
import com.zhaohy.app.utils.OnLineCountListener;

@SpringBootApplication(exclude = DruidDataSourceAutoConfigure.class)
@MapperScan("com.zhaohy.app.dao")
public class ImageSaveApp {

    public static void main(String[] args) {
        SpringApplication.run(ImageSaveApp.class, args);
        System.out.println("springboot started...");
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    @Bean
    public FilterRegistrationBean myFilterRegistration() {
        FilterRegistrationBean regist = new FilterRegistrationBean(new LoginProcessFilter());
        // 過濾全部請求
        regist.addUrlPatterns("/*");//過濾url
        regist.setOrder(1);//過濾器順序
        return regist;
    }
    
    @SuppressWarnings({ "rawtypes", "unchecked" })
    @Bean
    public ServletListenerRegistrationBean listenerRegist() {
        ServletListenerRegistrationBean srb = new ServletListenerRegistrationBean();
        srb.setListener(new OnLineCountListener());
        //System.out.println("listener====");
        return srb;
    }
}
spring注入分頁配置

新建MybatisPlusConfig類(示例用的oracle數(shù)據(jù)庫,所以DbType.ORACLE,其他數(shù)據(jù)庫要改下這里):

package com.zhaohy.app.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;

@Configuration
public class MybatisPlusConfig {
    /**
     * 新的分頁插件,一緩和二緩遵循mybatis的規(guī)則,需要設(shè)置 MybatisConfiguration#useDeprecatedExecutor = false 避免緩存出現(xiàn)問題(該屬性會在舊插件移除后一同移除)
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.ORACLE));
        return interceptor;
    }

//    @Bean
//    public ConfigurationCustomizer configurationCustomizer() {
//        return configuration -> configuration.setUseDeprecatedExecutor(false);
//    }
}
實現(xiàn)MetaObjectHandler配置數(shù)據(jù)庫自動填充

當(dāng)數(shù)據(jù)庫有些字段比如create_date或者update_date這種邏輯簡單的固定字段可以用mybatisplus提供的自動填充來填充時間
新建AutoFillHandler類,如下代碼所示,執(zhí)行insert的時候會自動填充create_date和update_date字段,執(zhí)行update的時候會自動填充update_date字段

package com.zhaohy.app.common;

import java.time.LocalDateTime;

import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;

/**
 * 數(shù)據(jù)庫字段自動填充
 * 
 * @author ly-licy
 *
 */
@Component
public class AutoFillHandler implements MetaObjectHandler {

    @Override
    public void insertFill(MetaObject metaObject) {
        if (metaObject.hasSetter("createDate") 
                && (metaObject.getValue("createDate") instanceof LocalDateTime 
                        || null == metaObject.getValue("createDate"))) {
            this.setFieldValByName("createDate", LocalDateTime.now(), metaObject);
        }
        if (metaObject.hasSetter("updateDate") 
                && (metaObject.getValue("updateDate") instanceof LocalDateTime 
                        || null == metaObject.getValue("updateDate"))) {
            this.setFieldValByName("updateDate", LocalDateTime.now(), metaObject);
        }
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        if (metaObject.hasSetter("updateDate") && (metaObject.getValue("updateDate") instanceof LocalDateTime 
                || null == metaObject.getValue("updateDate"))) {
            this.setFieldValByName("updateDate", LocalDateTime.now(), metaObject);
        }
    }

}

代碼測試

數(shù)據(jù)庫用oracle為例

oracle新建da_user表
CREATE TABLE "DA_USER" 
   (    "USER_ID" NUMBER, 
    "USER_NAME" VARCHAR2(128), 
    "PASSWORD" VARCHAR2(64), 
    "EMAIL" VARCHAR2(64), 
    "USER_TYPE_ID" NUMBER(*,0), 
    "CREATE_DATE" DATE, 
    "UPDATE_DATE" DATE, 
    "APP_TYPE" VARCHAR2(32), 
    "LOGIN_TYPE" NUMBER
   ) 
新建映射實體類UserPO
package com.zhaohy.app.po;

import java.time.LocalDateTime;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
@TableName("da_user")
public class UserPO {
    @TableId(value = "user_id", type = IdType.ASSIGN_ID)
//  @TableId(value = "user_id")
    private Long userId;
    
    private String userName;
    @JsonIgnore
    private String password;
    
    private String email;
    
    private Integer userTypeId;
    @JsonFormat(shape =JsonFormat.Shape.STRING,pattern ="yyyy-MM-dd HH:mm:ss",timezone ="GMT+8")
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createDate;
    @JsonFormat(shape =JsonFormat.Shape.STRING,pattern ="yyyy-MM-dd HH:mm:ss",timezone ="GMT+8")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateDate;
    
    private String appType;
    
    private Integer loginType;

    public Long getUserId() {
        return userId;
    }

    public void setUserId(Long userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Integer getUserTypeId() {
        return userTypeId;
    }

    public void setUserTypeId(Integer userTypeId) {
        this.userTypeId = userTypeId;
    }

    public LocalDateTime getCreateDate() {
        return createDate;
    }

    public void setCreateDate(LocalDateTime createDate) {
        this.createDate = createDate;
    }

    public LocalDateTime getUpdateDate() {
        return updateDate;
    }

    public void setUpdateDate(LocalDateTime updateDate) {
        this.updateDate = updateDate;
    }

    public String getAppType() {
        return appType;
    }

    public void setAppType(String appType) {
        this.appType = appType;
    }

    public Integer getLoginType() {
        return loginType;
    }

    public void setLoginType(Integer loginType) {
        this.loginType = loginType;
    }
}

可以看到上面用到了以下幾個注解:
@TableName("da_user"):指定實體類映射的表名

@TableId(value = "user_id", type = IdType.ASSIGN_ID):指定表的主鍵列,value可以配置數(shù)據(jù)庫的列名,type用來指定生成ID的類型,因為在oracle數(shù)據(jù)庫里沒有配置自增序列,此時配置IdType.AUTO(數(shù)據(jù)庫ID自增)是不生效的,可以在java程序里生成唯一id,IdType.ASSIGN_ID默認(rèn)用的雪花算法生成的id,其實我在程序里也沒有用到,每次新增的時候我是用redis自己生成id的,詳細(xì)了解請看我另一篇文章:redis實現(xiàn)全局唯一id

@JsonIgnore:這是jackson的注解,當(dāng)實體類傳給前端序列化成json的時候可以忽略指定字段,比如這里的password是不想給前端看到的,序列化json的時候就不會有這個字段。

@JsonFormat(shape =JsonFormat.Shape.STRING,pattern ="yyyy-MM-dd HH:mm:ss",timezone ="GMT+8"):這是jackson的注解,序列化json的時候可以把LocalDateTime序列化成指定格式的String字符串

@TableField(fill = FieldFill.INSERT):用來指定填充策略,和上面建的AutoFillHandler配合使用。

@TableField(exist = false):用來指定是否為數(shù)據(jù)庫表字段,默認(rèn)為true,多余的字段可以設(shè)為false。

@TableField(updateStrategy = FieldStrategy.IGNORED):更新策略忽略判斷,默認(rèn)為FieldStrategy.DEFAULT(在全局里代表 NOT_NULL)。比如有些可為空的字段,默認(rèn)全局配置是NOT_NULL的值才可以被更新,如果就是想更新這個字段為null,則要使用FieldStrategy.IGNORED單獨把這個字段忽略,就可以更新了。

大體上面這些注解勉強夠用,更多注解使用方式可以點擊上面貼的教程文檔地址,里面講的很詳細(xì)。

新建TestController
package com.zhaohy.app.controller;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ThreadLocalRandom;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zhaohy.app.dao.UserMapper;
import com.zhaohy.app.entity.ResponsePageVO;
import com.zhaohy.app.entity.ResponseVO;
import com.zhaohy.app.enums.ErrorCode;
import com.zhaohy.app.po.UserPO;
import com.zhaohy.app.service.GenerateIdService;
import com.zhaohy.app.utils.AppFrameworkUtil;
import com.zhaohy.app.utils.MD5Util;

@Controller
public class TestController {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private GenerateIdService generateIdService;
    
    @RequestMapping("common/insertTest.do")
    @ResponseBody
    public ResponseVO<List<UserPO>> insertTest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        UserPO user = new UserPO();
        //user.setUserId(Long.parseLong(generateIdService.getGenerateId()));
        user.setUserId(1651912471041000011L);
        user.setUserName("insertTest");
        user.setPassword(MD5Util.string2MD5("111"));
        user.setEmail("test@qq.com");
        user.setUserTypeId(2);
        user.setAppType("test");
        user.setLoginType(0);
        userMapper.insert(user);
        List<UserPO> userList = userMapper.selectList(null);
        return new ResponseVO<>(userList);
    }
    
    @RequestMapping("common/updateTest.do")
    @ResponseBody
    public ResponseVO<UserPO> updateTest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        UserPO user = new UserPO();
        user.setUserId(1651912471041000011L);
        user.setUserName("insertTest1");
        userMapper.updateById(user);
        user = userMapper.selectById(user.getUserId());
        return new ResponseVO<>(user);
    }
    
    @RequestMapping("common/deleteTest.do")
    @ResponseBody
    public ResponseVO<?> deleteTest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        UserPO user = new UserPO();
        user.setUserId(1651912471041000011L);
        userMapper.deleteById(user.getUserId());
        return new ResponseVO<>();
    }
    
    @RequestMapping("common/userList.do")
    @ResponseBody
    public ResponseVO<List<UserPO>> userList(HttpServletRequest request, HttpServletResponse response) throws Exception {
        IPage<UserPO> page = new Page(1,2);
        page = userMapper.selectPage(page, new LambdaQueryWrapper<UserPO>().orderByDesc(UserPO::getCreateDate));
        List<UserPO> list = page.getRecords();
        ResponsePageVO vo = new ResponsePageVO();
        vo.setCode(ErrorCode.SUCCESS.getCode());
        vo.setMsg(ErrorCode.SUCCESS.getCn());
        vo.setPage(page.getCurrent());
        vo.setTotalPages(page.getPages());
        vo.setTotalRecords(page.getTotal());
        vo.setRecords(page.getSize());
        vo.setData(list);
        return vo;
    }
    
    @RequestMapping("common/userList1.do")
    @ResponseBody
    public ResponseVO<List<UserPO>> userList1(HttpServletRequest request, HttpServletResponse response) throws Exception {
        IPage<UserPO> page = new Page(1,2);
        Map<String, Object> paramsMap = new HashMap<>();
        paramsMap.put("userTypeId", "2");
        List<UserPO> list = userMapper.getUserList(page, paramsMap);
        ResponsePageVO vo = new ResponsePageVO();
        vo.setCode(ErrorCode.SUCCESS.getCode());
        vo.setMsg(ErrorCode.SUCCESS.getCn());
        vo.setPage(page.getCurrent());
        vo.setTotalPages(page.getPages());
        vo.setTotalRecords(page.getTotal());
        vo.setRecords(page.getSize());
        vo.setData(list);
        return vo;
    }
}

上面代碼里簡單寫了增刪改查的例子,用起來比較簡單,更多用法可以去查上線貼的文檔地址,這里著重說一下里面的兩個分頁接口吧。

上面的兩個分頁,分別用了兩種方式:
1.mybatisplus自帶的selectPage方法,里面可以傳兩個參數(shù),一個是不能為null的IPage<T>對象,可以用IPage<UserPO> page = new Page(page,size);來構(gòu)建,page參數(shù)代表當(dāng)前頁,size代表每頁顯示的條數(shù),此方法返回一個IPage<T>對象,可以通過getRecords()方法獲取結(jié)果集合,getCurrent()方法獲取當(dāng)前頁,page.getPages()方法獲取總頁數(shù),page.getTotal()方法獲取總條數(shù),getSize()方法獲取每頁顯示條數(shù);
另一個是LambdaQueryWrapper對象,用來構(gòu)建查詢條件

2.用傳統(tǒng)的實體類或者HashMap傳參,自己構(gòu)建查詢方法,直接返回List集合,List<UserPO> list = userMapper.getUserList(page, paramsMap);
此時的page對象可以為null,如果為null則代表不分頁,這時返回的list就是要查詢的集合,但是page對象里的getRecords()方法返回的就是空數(shù)組了,所以用這種方法就不要用page.getRecords()獲取結(jié)果集合了。

UserMapper接口:
package com.zhaohy.app.dao;

import java.util.List;
import java.util.Map;

import org.apache.ibatis.annotations.Param;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.zhaohy.app.po.UserPO;

public interface UserMapper extends BaseMapper<UserPO> {

    List<UserPO> getUserList(IPage<UserPO> page, @Param("params") Map<String, Object> paramsMap);

}

上面的getUserList是自己創(chuàng)建的分頁,可以在xml里直接寫sql,但是此時,必須要指定@Param("params"),這樣在sql里拿參數(shù)的時候才可以找得到,比如:#{params.userTypeId}
UserMapper.xml里如下:

<select id="getUserList" resultType="com.zhaohy.app.po.UserPO">
        select * from da_user where user_type_id = #{params.userTypeId}
        order by user_id desc
</select>

如此,分頁查詢的時候既可以直接用selectPage方法單表分頁查詢,也可以用傳統(tǒng)的xml sql方式進行多表關(guān)聯(lián)查詢。

打印日志可以看到后臺執(zhí)行分頁查詢的時候用了兩步:
1.SELECT COUNT(*) AS total FROM da_user WHERE user_type_id = ?

  1. SELECT * FROM ( SELECT TMP.*, ROWNUM ROW_ID FROM ( select * from da_user where user_type_id = ? order by user_id desc ) TMP WHERE ROWNUM <=?) WHERE ROW_ID > ?
    這個和我們自己寫分頁是差不多的,都是先算總數(shù),再根據(jù)參數(shù)計算rowNum用sql分頁。

分頁插件雖然方便,用的久了很容易忘掉分頁原理,有些場景還是要自己寫分頁的,比如調(diào)用別人接口分頁查詢大數(shù)據(jù)量的時候,就得根據(jù)這個原理自己實現(xiàn)分頁了,之前寫過一篇關(guān)于自己實現(xiàn)分頁的文章:
java后臺分頁算法小記

以上~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,786評論 6 534
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,656評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,697評論 0 379
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,098評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,855評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,254評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,322評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,473評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,014評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,833評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,016評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,568評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,273評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,680評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,946評論 1 288
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,730評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 48,006評論 2 374

推薦閱讀更多精彩內(nèi)容