ssm項(xiàng)目,用mybatisplus很普遍,在原來(lái)的mybatis基礎(chǔ)上升級(jí)mybatisplus幾乎不用改什么東西模聋,mybatisplus只對(duì)mybatis做了功能擴(kuò)展肩民,不會(huì)影響原來(lái)的已有功能,單表查詢的時(shí)候的確比單純mybatis寫sql自己做映射要節(jié)省很多時(shí)間链方,也方便很多持痰。
baomidou mybatisplus教程文檔可參考:https://baomidou.com/pages/226c21/
步驟
原來(lái)的Springboot ssm框架升級(jí)mybatisplus主要有以下幾步:
1.注釋掉原來(lái)的mybatis引入包
2.引入mybatisplus包
3.啟動(dòng)類配置@MapperScan注解掃描路徑
4.Spring注入分頁(yè)配置
5.實(shí)現(xiàn)MetaObjectHandler配置數(shù)據(jù)庫(kù)自動(dòng)填充(非必須)
實(shí)現(xiàn)
注釋掉原來(lái)的mybatis包
<!-- <dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency> -->
引入mybatisplus包
maven引入(截止發(fā)文mvn倉(cāng)庫(kù)最新版本是3.5.1):
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
啟動(dòng)類配置@MapperScan注解掃描路徑
比如我所有的Mapper接口都在com.zhaohy.app.dao包路徑下,則在啟動(dòng)類加入
@MapperScan("com.zhaohy.app.dao")
貼一下啟動(dòng)類:
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());
// 過(guò)濾全部請(qǐng)求
regist.addUrlPatterns("/*");//過(guò)濾url
regist.setOrder(1);//過(guò)濾器順序
return regist;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Bean
public ServletListenerRegistrationBean listenerRegist() {
ServletListenerRegistrationBean srb = new ServletListenerRegistrationBean();
srb.setListener(new OnLineCountListener());
//System.out.println("listener====");
return srb;
}
}
spring注入分頁(yè)配置
新建MybatisPlusConfig類(示例用的oracle數(shù)據(jù)庫(kù)祟蚀,所以DbType.ORACLE工窍,其他數(shù)據(jù)庫(kù)要改下這里):
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 {
/**
* 新的分頁(yè)插件,一緩和二緩遵循mybatis的規(guī)則,需要設(shè)置 MybatisConfiguration#useDeprecatedExecutor = false 避免緩存出現(xiàn)問(wèn)題(該屬性會(huì)在舊插件移除后一同移除)
*/
@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);
// }
}
實(shí)現(xiàn)MetaObjectHandler配置數(shù)據(jù)庫(kù)自動(dòng)填充
當(dāng)數(shù)據(jù)庫(kù)有些字段比如create_date或者update_date這種邏輯簡(jiǎn)單的固定字段可以用mybatisplus提供的自動(dòng)填充來(lái)填充時(shí)間
新建AutoFillHandler類,如下代碼所示前酿,執(zhí)行insert的時(shí)候會(huì)自動(dòng)填充create_date和update_date字段患雏,執(zhí)行update的時(shí)候會(huì)自動(dòng)填充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ù)庫(kù)字段自動(dòng)填充
*
* @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);
}
}
}
代碼測(cè)試
數(shù)據(jù)庫(kù)用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
)
新建映射實(shí)體類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;
}
}
可以看到上面用到了以下幾個(gè)注解:
@TableName("da_user"):指定實(shí)體類映射的表名
@TableId(value = "user_id", type = IdType.ASSIGN_ID):指定表的主鍵列,value可以配置數(shù)據(jù)庫(kù)的列名,type用來(lái)指定生成ID的類型罢维,因?yàn)樵趏racle數(shù)據(jù)庫(kù)里沒(méi)有配置自增序列淹仑,此時(shí)配置IdType.AUTO(數(shù)據(jù)庫(kù)ID自增)是不生效的,可以在java程序里生成唯一id,IdType.ASSIGN_ID默認(rèn)用的雪花算法生成的id,其實(shí)我在程序里也沒(méi)有用到言津,每次新增的時(shí)候我是用redis自己生成id的攻人,詳細(xì)了解請(qǐng)看我另一篇文章:redis實(shí)現(xiàn)全局唯一id。
@JsonIgnore:這是jackson的注解悬槽,當(dāng)實(shí)體類傳給前端序列化成json的時(shí)候可以忽略指定字段怀吻,比如這里的password是不想給前端看到的,序列化json的時(shí)候就不會(huì)有這個(gè)字段初婆。
@JsonFormat(shape =JsonFormat.Shape.STRING,pattern ="yyyy-MM-dd HH:mm:ss",timezone ="GMT+8"):這是jackson的注解蓬坡,序列化json的時(shí)候可以把LocalDateTime序列化成指定格式的String字符串
@TableField(fill = FieldFill.INSERT):用來(lái)指定填充策略,和上面建的AutoFillHandler配合使用磅叛。
@TableField(exist = false):用來(lái)指定是否為數(shù)據(jù)庫(kù)表字段屑咳,默認(rèn)為true,多余的字段可以設(shè)為false。
@TableField(updateStrategy = FieldStrategy.IGNORED):更新策略忽略判斷弊琴,默認(rèn)為FieldStrategy.DEFAULT(在全局里代表 NOT_NULL)兆龙。比如有些可為空的字段,默認(rèn)全局配置是NOT_NULL的值才可以被更新敲董,如果就是想更新這個(gè)字段為null紫皇,則要使用FieldStrategy.IGNORED單獨(dú)把這個(gè)字段忽略,就可以更新了腋寨。
大體上面這些注解勉強(qiáng)夠用聪铺,更多注解使用方式可以點(diǎn)擊上面貼的教程文檔地址,里面講的很詳細(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;
}
}
上面代碼里簡(jiǎn)單寫了增刪改查的例子铃剔,用起來(lái)比較簡(jiǎn)單撒桨,更多用法可以去查上線貼的文檔地址,這里著重說(shuō)一下里面的兩個(gè)分頁(yè)接口吧键兜。
上面的兩個(gè)分頁(yè)凤类,分別用了兩種方式:
1.mybatisplus自帶的selectPage方法,里面可以傳兩個(gè)參數(shù)蝶押,一個(gè)是不能為null的IPage<T>對(duì)象踱蠢,可以用IPage<UserPO> page = new Page(page,size);來(lái)構(gòu)建,page參數(shù)代表當(dāng)前頁(yè)棋电,size代表每頁(yè)顯示的條數(shù)茎截,此方法返回一個(gè)IPage<T>對(duì)象,可以通過(guò)getRecords()方法獲取結(jié)果集合赶盔,getCurrent()方法獲取當(dāng)前頁(yè)企锌,page.getPages()方法獲取總頁(yè)數(shù),page.getTotal()方法獲取總條數(shù)于未,getSize()方法獲取每頁(yè)顯示條數(shù)撕攒;
另一個(gè)是LambdaQueryWrapper對(duì)象,用來(lái)構(gòu)建查詢條件
2.用傳統(tǒng)的實(shí)體類或者HashMap傳參烘浦,自己構(gòu)建查詢方法抖坪,直接返回List集合,List<UserPO> list = userMapper.getUserList(page, paramsMap);
此時(shí)的page對(duì)象可以為null,如果為null則代表不分頁(yè)闷叉,這時(shí)返回的list就是要查詢的集合擦俐,但是page對(duì)象里的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)建的分頁(yè)蚯瞧,可以在xml里直接寫sql,但是此時(shí),必須要指定@Param("params")品擎,這樣在sql里拿參數(shù)的時(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>
如此埋合,分頁(yè)查詢的時(shí)候既可以直接用selectPage方法單表分頁(yè)查詢,也可以用傳統(tǒng)的xml sql方式進(jìn)行多表關(guān)聯(lián)查詢萄传。
打印日志可以看到后臺(tái)執(zhí)行分頁(yè)查詢的時(shí)候用了兩步:
1.SELECT COUNT(*) AS total FROM da_user WHERE user_type_id = ?
- 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 > ?
這個(gè)和我們自己寫分頁(yè)是差不多的甚颂,都是先算總數(shù),再根據(jù)參數(shù)計(jì)算rowNum用sql分頁(yè)秀菱。
分頁(yè)插件雖然方便西设,用的久了很容易忘掉分頁(yè)原理,有些場(chǎng)景還是要自己寫分頁(yè)的答朋,比如調(diào)用別人接口分頁(yè)查詢大數(shù)據(jù)量的時(shí)候,就得根據(jù)這個(gè)原理自己實(shí)現(xiàn)分頁(yè)了棠笑,之前寫過(guò)一篇關(guān)于自己實(shí)現(xiàn)分頁(yè)的文章:
java后臺(tái)分頁(yè)算法小記
以上~