前言
之前寫了《SpringBoot | 第九章:Mybatis-plus的集成和使用》一文,只是簡單的使用條件構造器列舉了一些通用的CURD操作。本人也想寫一篇通用的關於
mybatis-plus
的使用示例,一方面也讓自己更加了解mybatis
和mybatis-plus
,另一方面,也因為很多新人剛入職公司時,對這塊不是很熟悉,會有一些疑惑。所以,總的來說還是作為一份資產,可供人查閱,這樣也能減少了很多的溝通成本。
所以本章節,就主要來講解下關於Mybatis-plus
的不同場景的用法,目前主要想到的是以下幾個知識點,也是很常用的知識點了,后面有補充的會再啟章節來記錄的。另外,官網的文檔已經很詳盡了,大家可認真查閱下。
- 代碼生成器
- 通用的CURD
- 條件構造器
- 自定義SQL語句
- 分頁插件、性能分析插件
- 公共字段自動填充
工程准備
這里還是以user
表為例子,數據庫為mysql
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` bigint(20) DEFAULT NULL COMMENT '唯一標示',
`code` varchar(20) DEFAULT NULL COMMENT '編碼',
`name` varchar(64) DEFAULT NULL COMMENT '名稱',
`status` char(1) DEFAULT '1' COMMENT '狀態 1啟用 0 停用',
`gmt_create` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '創建時間',
`gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改時間'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
關於SpringBoot
和Mybatis-plus
的集成,這里就不闡述了,這個不是今天的重點。不熟悉的同學,可移步:http://blog.lqdev.cn/2018/07/21/springboot/chapter-nine/進行查看了解。
代碼生成器
Mybatis-Plus
已經提供了大量的自定義設置,生成的代碼完全能夠滿足各類型的需求,基本覆蓋了大部分的配置了。這里貼一個比較完整的代碼生成器類,大家可根據實際情況進行修改。
MysqlGenerator.java:
//省略了import
public class MysqlGenerator {
/**
* 包名
*/
private static final String PACKAGE_NAME = "cn.lqdev.learning.mybatisplus.samples";
/**
* 模塊名稱
*/
private static final String MODULE_NAME = "biz";
/**
* 輸出文件的路徑
*/
private static final String OUT_PATH = "D:\\develop\\code";
/**
* 代碼生成者
*/
private static final String AUTHOR = "oKong";
/**
* JDBC相關配置
*/
private static final String DRIVER = "com.mysql.jdbc.Driver";
private static final String URL = "jdbc:mysql://127.0.0.1:3306/learning?useUnicode=true&characterEncoding=UTF-8";
private static final String USER_NAME = "root";
private static final String PASSWORD = "bs";
/**
* <p>
* MySQL 生成演示
* </p>
*/
public static void main(String[] args) {
// 自定義需要填充的字段
List<TableFill> tableFillList = new ArrayList<TableFill>();
//如 每張表都有一個創建時間、修改時間
//而且這基本上就是通用的了,新增時,創建時間和修改時間同時修改
//修改時,修改時間會修改,
//雖然像Mysql數據庫有自動更新幾只,但像ORACLE的數據庫就沒有了,
//使用公共字段填充功能,就可以實現,自動按場景更新了。
//如下是配置
TableFill createField = new TableFill("gmt_create", FieldFill.INSERT);
TableFill modifiedField = new TableFill("gmt_modified", FieldFill.INSERT_UPDATE);
tableFillList.add(createField);
tableFillList.add(modifiedField);
// 代碼生成器
AutoGenerator mpg = new AutoGenerator().setGlobalConfig(
// 全局配置
new GlobalConfig().setOutputDir(OUT_PATH)// 輸出目錄
.setFileOverride(true)// 是否覆蓋文件
.setActiveRecord(true)// 開啟 activeRecord 模式
.setEnableCache(false)// XML 二級緩存
.setBaseResultMap(false)// XML ResultMap
.setBaseColumnList(true)// XML columList
.setAuthor(AUTHOR)
// 自定義文件命名,注意 %s 會自動填充表實體屬性!
.setXmlName("%sMapper").setMapperName("%sDao")
// .setServiceName("MP%sService")
// .setServiceImplName("%sServiceDiy")
// .setControllerName("%sAction")
).setDataSource(
// 數據源配置
new DataSourceConfig().setDbType(DbType.MYSQL)// 數據庫類型
.setTypeConvert(new MySqlTypeConvert() {
// 自定義數據庫表字段類型轉換【可選】
@Override
public DbColumnType processTypeConvert(String fieldType) {
System.out.println("轉換類型:" + fieldType);
// if ( fieldType.toLowerCase().contains( "tinyint" ) ) {
// return DbColumnType.BOOLEAN;
// }
return super.processTypeConvert(fieldType);
}
}).setDriverName(DRIVER).setUsername(USER_NAME).setPassword(PASSWORD).setUrl(URL))
.setStrategy(
// 策略配置
new StrategyConfig()
// .setCapitalMode(true)// 全局大寫命名
.setDbColumnUnderline(true)// 全局下划線命名
// .setTablePrefix(new String[]{"unionpay_"})// 此處可以修改為您的表前綴
.setNaming(NamingStrategy.underline_to_camel)// 表名生成策略
// .setInclude(new String[] {"user"}) // 需要生成的表
// .setExclude(new String[]{"test"}) // 排除生成的表
// 自定義實體,公共字段
// .setSuperEntityColumns(new String[]{"test_id"})
.setTableFillList(tableFillList)
// 自定義實體父類
// .setSuperEntityClass("com.baomidou.demo.base.BsBaseEntity")
// // 自定義 mapper 父類
// .setSuperMapperClass("com.baomidou.demo.base.BsBaseMapper")
// // 自定義 service 父類
// .setSuperServiceClass("com.baomidou.demo.base.BsBaseService")
// // 自定義 service 實現類父類
// .setSuperServiceImplClass("com.baomidou.demo.base.BsBaseServiceImpl")
// 自定義 controller 父類
// .setSuperControllerClass("com.baomidou.demo.TestController")
// 【實體】是否生成字段常量(默認 false)
// public static final String ID = "test_id";
.setEntityColumnConstant(true)
// 【實體】是否為構建者模型(默認 false)
// public User setName(String name) {this.name = name; return this;}
.setEntityBuilderModel(true)
// 【實體】是否為lombok模型(默認 false)<a href="https://projectlombok.org/">document</a>
.setEntityLombokModel(true)
// Boolean類型字段是否移除is前綴處理
// .setEntityBooleanColumnRemoveIsPrefix(true)
// .setRestControllerStyle(true)
// .setControllerMappingHyphenStyle(true)
).setPackageInfo(
// 包配置
new PackageConfig().setModuleName(MODULE_NAME).setParent(PACKAGE_NAME)// 自定義包路徑
.setController("controller")// 這里是控制器包名,默認 web
.setXml("mapper").setMapper("dao")
).setCfg(
// 注入自定義配置,可以在 VM 中使用 cfg.abc 設置的值
new InjectionConfig() {
@Override
public void initMap() {
Map<String, Object> map = new HashMap<String, Object>();
map.put("abc", this.getConfig().getGlobalConfig().getAuthor() + "-mp");
this.setMap(map);
}
}.setFileOutConfigList(
Collections.<FileOutConfig>singletonList(new FileOutConfig("/templates/mapper.xml.vm") {
// 自定義輸出文件目錄
@Override
public String outputFile(TableInfo tableInfo) {
return OUT_PATH + "/xml/" + tableInfo.getEntityName() + "Mapper.xml";
}
})))
.setTemplate(
// 關閉默認 xml 生成,調整生成 至 根目錄
new TemplateConfig().setXml(null)
// 自定義模板配置,模板可以參考源碼 /mybatis-plus/src/main/resources/template 使用 copy
// 至您項目 src/main/resources/template 目錄下,模板名稱也可自定義如下配置:
// .setController("...");
// .setEntity("...");
// .setMapper("...");
// .setXml("...");
// .setService("...");
// .setServiceImpl("...");
);
// 執行生成
mpg.execute();
}
}
按以上代碼生成器,生成的目錄結構如下(依賴中需要加入velocity-engine-core
包,是利用模版引擎來生成的)
對應mapper.xml
對於需要自定義模版時,大家可查看官方的mybatis-plus-generate
包,默認的官方模版都放在此包下。
有了代碼生成器,省了很多機械性的復制黏貼操作,還不會出錯,寫錯了再執行一次就好了!
通用的CURD
MP
提供了ActiveRecord
的支持,所以實體類只需繼承 Model 類即可實現基本 CRUD 操作。
這里以編寫測試類的形式,進行通用CURD操作,代碼類有相應的注釋說明。
GeneralTest.java:
/**
* 通用CURD示例
* @author oKong
*
*/
@RunWith(SpringRunner.class)
//SpringBootTest 是springboot 用於測試的注解,可指定啟動類或者測試環境等,這里直接默認。
@SpringBootTest
@Slf4j
public class GeneralTest {
@Autowired
IUserService userService;
@Test
public void testInsert() {
User user = new User();
user.setCode("001");
user.setName("okong-insert");
//默認的插入策略為:FieldStrategy.NOT_NULL,即:判斷 null
//對應在mapper.xml時寫法為:<if test="field!=null">
//這個可以修改的,設置字段的@TableField(strategy=FieldStrategy.NOT_EMPTY)
//所以這個時候,為null的字段是不會更新的,也可以開啟性能插件,查看sql語句就可以知道
userService.insert(user);
//新增所有字段,
userService.insertAllColumn(user);
log.info("新增結束");
}
@Test
public void testUpdate() {
User user = new User();
user.setCode("101");
user.setName("oKong-insert");
//這就是ActiveRecord的功能
user.insert();
//也可以直接 userService.insert(user);
//更新
User updUser = new User();
updUser.setId(user.getId());
updUser.setName("okong-upd");
updUser.updateById();
log.info("更新結束");
}
@Test
public void testDelete() {
User user = new User();
user.setCode("101");
user.setName("oKong-delete");
user.insert();
//刪除
user.deleteById();
log.info("刪除結束");
}
@Test
public void testSelect() {
User user = new User();
user.setCode("201");
user.setName("oKong-selecdt");
user.insert();
log.info("查詢:{}",user.selectById());
}
}
以上就列舉了常用的,官方提供了很多的通用方法:
注意控制台的sql輸出,對比下就知道各方法之間的區別了。
對於通用代碼如何注入的,可查看com.baomidou.mybatisplus.mapper.AutoSqlInjector
類,這個就是注入通用的CURD方法的類。
條件構造器
在通用的CURD無法滿足時,這個時候 強大的條件構造器就排上用場了。主要提供了實體包裝器,用於處理 sql 拼接,排序,實體參數查詢等!
這里需要注意:使用的是數據庫字段,不是Java屬性!,原來使用另一款通用mapper
時記得使用的是JAVA屬性。
sql條件拼接
這也是條件構造器最靈活的地方了。
ConditionTest.java
@RunWith(SpringRunner.class)
//SpringBootTest 是springboot 用於測試的注解,可指定啟動類或者測試環境等,這里直接默認。
@SpringBootTest
@Slf4j
public class ConditionTest {
@Autowired
IUserService userService;
@Test
public void testOne() {
User user = new User();
user.setCode("701");
user.setName("okong-condition");
user.insert();
EntityWrapper<User> qryWrapper = new EntityWrapper<>();
qryWrapper.eq(User.CODE, user.getCode());
qryWrapper.eq(User.NAME, user.getName());
//也可以直接
// qryWrapper.setEntity(user);
//打印sql語句
System.out.println(qryWrapper.getSqlSegment());
//設置select 字段 即:select code,name from
qryWrapper.setSqlSelect(User.CODE,User.NAME);
System.out.println(qryWrapper.getSqlSelect());
//查詢
User qryUser = userService.selectOne(qryWrapper);
System.out.println(qryUser);
log.info("拼接一結束");
}
@Test
public void testTwo() {
User user = new User();
user.setCode("702");
user.setName("okong-condition");
user.insert();
EntityWrapper<User> qryWrapper = new EntityWrapper<>();
qryWrapper.where("code = {0}", user.getCode())
.and("name = {0}",user.getName())
.andNew("status = 0");
System.out.println(qryWrapper.getSqlSegment());
//等等很復雜的。
//復雜的建議直接寫在xml里面了,要是非動態的話 比較xml一眼看得懂呀
//查詢
User qryUser = userService.selectOne(qryWrapper);
System.out.println(qryUser);
log.info("拼接二結束");
}
}
com.baomidou.mybatisplus.mapper.Wrapper<T>
類還有很多的方法,大家可以試試。
條件參數說明
查詢方式 | 說明 |
---|---|
setSqlSelect | 設置 SELECT 查詢字段 |
where | WHERE 語句,拼接 + WHERE 條件 |
and | AND 語句,拼接 + AND 字段=值 |
andNew | AND 語句,拼接 + AND (字段=值) |
or | OR 語句,拼接 + OR 字段=值 |
orNew | OR 語句,拼接 + OR (字段=值) |
eq | 等於= |
allEq | 基於 map 內容等於= |
ne | 不等於<> |
gt | 大於> |
ge | 大於等於>= |
lt | 小於< |
le | 小於等於<= |
like | 模糊查詢 LIKE |
notLike | 模糊查詢 NOT LIKE |
in | IN 查詢 |
notIn | NOT IN 查詢 |
isNull | NULL 值查詢 |
isNotNull | IS NOT NULL |
groupBy | 分組 GROUP BY |
having | HAVING 關鍵詞 |
orderBy | 排序 ORDER BY |
orderAsc | ASC 排序 ORDER BY |
orderDesc | DESC 排序 ORDER BY |
exists | EXISTS 條件語句 |
notExists | NOT EXISTS 條件語句 |
between | BETWEEN 條件語句 |
notBetween | NOT BETWEEN 條件語句 |
addFilter | 自由拼接 SQL |
last | 拼接在最后,例如:last("LIMIT 1") |
自定義SQL使用條件構造器
UserDao.java
加入接口方法:
/**
*
* @param rowBounds 分頁對象 直接傳入page即可
* @param wrapper 條件構造器
* @return
*/
List<User> selectUserWrapper(RowBounds rowBounds, @Param("ew") Wrapper<User> wrapper);
UserMapper.xml
加入對應的xml節點:
<!-- 條件構造器形式 -->
<select id="selectUserWrapper" resultType="user">
SELECT
<include refid="Base_Column_List" />
FROM USER
<where>
${ew.sqlSegment}
</where>
</select>
測試類:
@Test
public void testCustomSql() {
User user = new User();
user.setCode("703");
user.setName("okong-condition");
user.insert();
EntityWrapper<User> qryWrapper = new EntityWrapper<>();
qryWrapper.eq(User.CODE, user.getCode());
Page<User> pageUser = new Page<>();
pageUser.setCurrent(1);
pageUser.setSize(10);
List<User> userlist = userDao.selectUserWrapper(pageUser, qryWrapper);
System.out.println(userlist.get(0));
log.info("自定義sql結束");
}
xml形式使用wrapper
UserDao.java
/**
*
* @param rowBounds 分頁對象 直接傳入page即可
* @param wrapper 條件構造器
* @return
*/
List<User> selectUserWrapper(RowBounds rowBounds, @Param("ew") Wrapper<User> wrapper);
對應的UserMapper.xml
:
<!-- 條件構造器形式 -->
<select id="selectUserWrapper" resultType="user">
SELECT
<include refid="Base_Column_List" />
FROM USER
<where>
${ew.sqlSegment}
</where>
</select>
自定義SQL語句
在一些需要多表關聯時,條件構造器和通用CURD都無法滿足時,還可以自行手寫sql語句進行擴展。注意:這都是
mybatis
的用法。
以下兩種方式都是改造UserDao
接口。
注解形式
@Select("SELECT * FROM USER WHERE CODE = #{userCode}")
List<User> selectUserCustomParamsByAnno(@Param("userCode")String userCode);
xml形式
List<User> selectUserCustomParamsByXml(@Param("userCode")String userCode);
同時,UserMapper.xml
新增一個節點:
<!-- 由於設置了別名:typeAliasesPackage=cn.lqdev.learning.mybatisplus.samples.biz.entity,所以resultType可以不寫全路徑了。 -->
<select id="selectUserCustomParamsByXml" resultType="user">
SELECT
<include refid="Base_Column_List"/>
FROM USER
WHERE CODE = #{userCode}
</select>
測試類CustomSqlTest.java
:
@RunWith(SpringRunner.class)
//SpringBootTest 是springboot 用於測試的注解,可指定啟動類或者測試環境等,這里直接默認。
@SpringBootTest
@Slf4j
public class CustomSqlTest {
@Autowired
UserDao userDao;
@Test
public void testCustomAnno() {
User user = new User();
user.setCode("901");
user.setName("okong-sql");
user.insert();
List<User> userlist = userDao.selectUserCustomParamsByAnno(user.getCode());
//由於新增的 肯定不為null 故不判斷了。
System.out.println(userlist.get(0).toString());
log.info("注解形式結束------");
}
@Test
public void testCustomXml() {
User user = new User();
user.setCode("902");
user.setName("okong-sql");
user.insert();
List<User> userlist = userDao.selectUserCustomParamsByXml(user.getCode());
//由於新增的 肯定不為null 故不判斷了。
System.out.println(userlist.get(0).toString());
log.info("xml形式結束------");
}
}
注意事項
在使用spring-boot-maven-plugin
插件打包成springboot
運行jar時,需要注意下,由於springboot
的jar掃描路徑方式問題,會導致別名的包未掃描到,所以這個只需要把mybatis
默認的掃描設置為Springboot
的VFS
實現。
直接修改spring-mybatis.xml
文件:
<!--mybatis-->
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!-- 自動掃描mapper.xml文件,支持通配符 -->
<property name="mapperLocations" value="classpath:mapper/**/*.xml"/>
<!-- 配置文件,比如參數配置(是否啟動駝峰等)、插件配置等 -->
<property name="configLocation" value="classpath:mybatis/mybatis-config.xml"/>
<!-- 啟用別名,這樣就無需寫全路徑類名了,具體可自行查閱資料 -->
<property name="typeAliasesPackage" value="cn.lqdev.learning.mybatisplus.samples.biz.entity"/>
<!-- MP 全局配置注入 -->
<property name="globalConfig" ref="globalConfig"/>
<!-- 設置vfs實現,避免路徑掃描問題 -->
<property name="vfs" value="com.baomidou.mybatisplus.spring.boot.starter.SpringBootVFS"></property>
</bean>
分頁插件、性能分析插件
mybatis的插件機制使用起來是很簡單的,只需要注冊即可。
mybatis-config.xml
<plugins>
<!-- SQL 執行性能分析,開發環境使用,線上不推薦。 -->
<plugin interceptor="com.baomidou.mybatisplus.plugins.PerformanceInterceptor"></plugin>
<!-- 分頁插件配置 -->
<plugin interceptor="com.baomidou.mybatisplus.plugins.PaginationInterceptor"></plugin>
</plugins>
分頁測試類(性能分析,配置后可以輸出sql及取數時間):
@RunWith(SpringRunner.class)
//SpringBootTest 是springboot 用於測試的注解,可指定啟動類或者測試環境等,這里直接默認。
@SpringBootTest
@Slf4j
public class PluginTest {
@Autowired
IUserService userService;
@Test
public void testPagination() {
Page<User> page = new Page<>();
//每頁數
page.setSize(10);
//當前頁碼
page.setCurrent(1);
//無條件時
Page<User> pageList = userService.selectPage(page);
System.out.println(pageList.getRecords().get(0));
//新增數據 避免查詢不到數據
User user = new User();
user.setCode("801");
user.setName("okong-Pagination");
user.insert();
//加入條件構造器
EntityWrapper<User> qryWapper = new EntityWrapper<>();
//這里也能直接設置 entity 這是條件就是entity的非空字段值了
// qryWapper.setEntity(user);
//這里建議直接用 常量
// qryWapper.eq(User.CODE, user.getCode());
pageList = userService.selectPage(page, qryWapper);
System.out.println(pageList.getRecords().get(0));
log.info("分頁結束");
}
}
性能插件體現,控制台輸出:
Time:4 ms - ID:cn.lqdev.learning.mybatisplus.samples.biz.dao.UserDao.selectPage
Execute SQL: SELECT id AS id,code,`name`,`status`,gmt_create AS gmtCreate,gmt_modified AS gmtModified FROM user WHERE id=1026120705692434433 AND code='801' AND `name`='okong-Pagination' LIMIT 0,10
公共字段自動填充
通常,每個公司都有自己的表定義,在《阿里巴巴_Java_開發手冊》中,就強制規定表必備三字段:id, gmt_create, gmt_modified。所以通常我們都會寫個公共的攔截器去實現自動填充比如創建時間和更新時間的,無需開發人員手動設置。而在
MP
中就提供了這么一個公共字段自動填充功能
。
設置填充字段的填充類型
User.java
/**
* 創建時間
*/
@TableField(fill=FieldFill.INSERT)
private Date gmtCreate;
/**
* 修改時間
*/
@TableField(fill=FieldFill.INSERT_UPDATE)
private Date gmtModified;
注意這里是可以在代碼生成器
里面配置規則的,可自動配置,詳見代碼生成器類。
定義處理類
MybatisObjectHandler.java
public class MybatisObjectHandler extends MetaObjectHandler{
@Override
public void insertFill(MetaObject metaObject) {
//新增時填充的字段
setFieldValByName("gmtCreate", new Date(), metaObject);
setFieldValByName("gmtModified", new Date(), metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
//更新時 需要填充字段
setFieldValByName("gmtModified", new Date(), metaObject);
}
}
同時修改springb-mybatis.xml
文件,加入此配置:
<bean id="globalConfig" class="com.baomidou.mybatisplus.entity.GlobalConfiguration">
<!--
AUTO->`0`("數據庫ID自增")QW
INPUT->`1`(用戶輸入ID")
ID_WORKER->`2`("全局唯一ID")
UUID->`3`("全局唯一ID")
-->
<property name="idType" value="2" />
<property name="metaObjectHandler" ref="mybatisObjectHandler"></property>
</bean>
<bean id="mybatisObjectHandler" class="cn.lqdev.learning.mybatisplus.samples.config.MybatisObjectHandler"/>
這個時候再新增或者修改,對應時間就會進行更新了。
Time:31 ms - ID:cn.lqdev.learning.mybatisplus.samples.biz.dao.UserDao.insert
Execute SQL: INSERT INTO user ( id, code, `name`, gmt_create,gmt_modified ) VALUES ( 1026135016838037506, '702', 'okong-condition', '2018-08-05 23:57:07.344','2018-08-05 23:57:07.344' )
相關資料
總結
本文主要列舉了開發過程中常用的操作數據庫的方法及相關配置。應該可以應付百分之八十以上的需求了吧。之后有時間,會進行補充的,比如自定義插件、大批量數據的寫法等。
最后
若文中有錯誤或者遺漏之處,還望指出,共同進步!
老生常談
- 個人QQ:
499452441
- 微信公眾號:
lqdevOps
個人博客:http://blog.lqdev.cn
完整示例:https://gitee.com/oKong/mybatis-plus-samples
原文地址:http://blog.lqdev.cn/2018/08/06/%E6%97%A5%E5%B8%B8%E7%A7%AF%E7%B4%AF/mybatis-plus-guide-one/