前言:
關(guān)于mybatis-plus的簡介以及基本使用湾揽,我在《mybatis-plus的使用 ------ 入門》一文中已做介紹,此處不再贅述宫峦。本文主要對mybatis-plus的AR模式、插件、逆向工程碉考、自定義全局操作、公共字段自動填充等知識點(diǎn)進(jìn)行講解挺身。
歡迎大家關(guān)注我的公眾號 javawebkf侯谁,目前正在慢慢地將簡書文章搬到公眾號,以后簡書和公眾號文章將同步更新章钾,且簡書上的付費(fèi)文章在公眾號上將免費(fèi)墙贱。
一、ActiveRecord:
Active Record(活動記錄)贱傀,是一種領(lǐng)域模型模式惨撇,特點(diǎn)是一個模型類對應(yīng)關(guān)系型數(shù)據(jù)庫中的一個表,而模型類的一個實(shí)例對應(yīng)表中的一行記錄府寒。ActiveRecord 一直廣受動態(tài)語言( PHP 魁衙、 Ruby 等)的喜愛,而 Java 作為準(zhǔn)靜態(tài)語言株搔,對于 ActiveRecord 往往只能感嘆其優(yōu)雅剖淀,所以 MP 也在 AR 道路上進(jìn)行了一定的探索,僅僅需要讓實(shí)體類繼承 Model 類且實(shí)現(xiàn)主鍵指定方法纤房,即可開啟 AR 之旅纵隔。接下來看具體代碼:
1、entity:
@Data
public class User extends Model<User> {
private Integer id;
private String name;
private Integer age;
private Integer gender;
//重寫這個方法炮姨,return當(dāng)前類的主鍵
@Override
protected Serializable pkVal() {
return id;
}
}
注:實(shí)體類繼承Model類巨朦,重寫pkVal方法。
2剑令、mapper:
public interface UserDao extends BaseMapper<User> {
}
注:雖然AR模式用不到該接口糊啡,但是一定要定義,否則使用AR時會報(bào)空指針異常吁津。
3棚蓄、使用AR:
(1)、AR插入操作:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:spring/spring-dao.xml"})
public class TestAR {
@Test
public void testArInsert(){
User user = new User();
user.setName("林青霞");
user.setAge(22);
user.setGender(1);
boolean result = user.insert();
System.out.println(result);
}
}
注:可以看到我們并不需要注入mapper接口碍脏,不過正如剛才所說梭依,不使用但還是要定義,否則會報(bào)錯典尾。AR操作是通過對象本身調(diào)用相關(guān)方法役拴,比如要insert一個user,那就用這個user調(diào)用insert方法即可钾埂。返回值為布爾類型河闰,由上圖可看到返回了true科平,是操作成功的。
(2)姜性、AR更新操作:
@Test
public void testArUpdate(){
User user = new User();
user.setId(1);
user.setName("劉亦菲");
boolean result = user.updateById();
System.out.println(result);
}
注:user調(diào)用updateById方法瞪慧,將id為1的用戶進(jìn)行更新。
(3)部念、AR查詢操作:
@Test
public void testArSelect(){
User user = new User();
//1弃酌、根據(jù)id查詢
//user = user.selectById(1);
//或者這樣用
//user.setId(1);
//user = user.selectById();
//2、查詢所有
//List<User> users = user.selectAll();
//3儡炼、根據(jù)條件查詢
//List<User> users = user.selectList(new EntityWrapper<User>().like("name","劉"));
//4妓湘、查詢符合條件的總數(shù)
int result = user.selectCount(new EntityWrapper<User>().eq("gender",1));
System.out.println(result);
}
注:上面的代碼涉及到了四個不同的查詢操作,其實(shí)用法與MP的BaseMapper提供的方法的用法差不多乌询,只不過這里是實(shí)體對象調(diào)用榜贴。
(4)、AR刪除操作:
@Test
public void testArDelete(){
User user = new User();
//刪除數(shù)據(jù)庫中不存在的數(shù)據(jù)也是返回true
//1楣责、根據(jù)id刪除數(shù)據(jù)
//boolean result = user.deleteById(1);
//或者這樣寫
//user.setId(1);
//boolean result = user.deleteById();
//2、根據(jù)條件刪除
boolean result = user.delete(new EntityWrapper<User>().like("name","玲"));
System.out.println(result);
}
注:這里介紹了兩個刪除方法聂沙,代碼中已有注釋說明秆麸。需要注意的是,刪除數(shù)據(jù)庫中不存在的數(shù)據(jù)及汉,結(jié)果也是true沮趣。
(5)、AR分頁操作:
@Test
public void testArPage(){
User user = new User();
Page<User> page =
user.selectPage(new Page<>(1,4),
new EntityWrapper<User>().like("name","劉"));
List<User> users = page.getRecords();
System.out.println(users);
}
注:這個分頁方法和BaseMapper提供的分頁一樣都是內(nèi)存分頁坷随,并非物理分頁房铭,因?yàn)閟ql語句中沒用limit,和BaseMapper的selectPage方法一樣温眉,配置了分頁插件后就可以實(shí)現(xiàn)真正的物理分頁缸匪。AR的分頁方法與BaseMapper提供的分頁方法不同的是,BaseMapper的selectPage方法返回值是查詢到的記錄的list集合类溢,而AR的selectPage方法返回的是page對象凌蔬,該page對象封裝了查詢到的信息,可以通過getRecords方法獲取信息闯冷。
二砂心、插件的配置:
MP提供了很多好用的插件,而且配置簡單蛇耀,使用方便辩诞。接下來一起看看MP的插件如何使用。
1纺涤、分頁插件:
之前就有說到译暂,BaseMapper的selectPage方法和AR提供的selectPage方法都不是物理分頁抠忘,需要配置分頁插件后才是物理分頁,那么現(xiàn)在就來看看如何配置這個插件秧秉。
<!-- 3褐桌、配置mybatisplus的sqlSessionFactory -->
<bean id="sqlSessionFactory" class=
"com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="typeAliasesPackage" value="com.zhu.mybatisplus.entity"/>
<!-- 注入全局配置 -->
<property name="globalConfig" ref="globalConfiguration"/>
<!-- 配置插件 -->
<property name="plugins">
<list>
<!-- 分頁插件 -->
<bean class="com.baomidou.mybatisplus.plugins.PaginationInterceptor"/>
</list>
</property>
</bean>
注:在sqlSessionFactory這個bean中,通過<property name="plugins">
配置插件象迎,接下來的所有插件都配置在這個list中荧嵌。
@Test
public void testPage() {
//配置了分頁插件后,還是和以前一樣的使用selectpage方法砾淌,
//但是現(xiàn)在就是真正的物理分頁了啦撮,sql語句中有l(wèi)imit了
Page<Employee> page = new Page<>(1, 2);
List<Employee> employeeList =
emplopyeeDao.selectPage(page, null);
System.out.println(employeeList);
System.out.println("================= 相關(guān)的分頁信息 ==================");
System.out.println("總條數(shù):" + page.getTotal());
System.out.println("當(dāng)前頁碼:" + page.getCurrent());
System.out.println("總頁數(shù):" + page.getPages());
System.out.println("每頁顯示條數(shù):" + page.getSize());
System.out.println("是否有上一頁:" + page.hasPrevious());
System.out.println("是否有下一頁:" + page.hasNext());
//還可以將查詢到的結(jié)果set進(jìn)page對象中
page.setRecords(employeeList);
}
由圖可知,sql語句中已經(jīng)有了limit汪厨,是物理分頁了赃春。
也可以通過page調(diào)用相關(guān)方法獲取到相關(guān)的分頁信息,而且還可以把查詢到的結(jié)果set回page對象中劫乱,方便前端使用织中。
2、性能分析插件:
在plugin的list中添加如下bean即可開啟性能分析插件:
<!-- 輸出每條SQL語句及其執(zhí)行時間衷戈,生產(chǎn)環(huán)境不建議使用該插件 -->
<bean class="com.baomidou.mybatisplus.plugins.PerformanceInterceptor">
<property name="format" value="true"/><!-- 格式化SQL語句 -->
<property name="maxTime" value="1000"/><!-- sql執(zhí)行時間超過value值就會停止執(zhí)行狭吼,
單位是毫秒 -->
</bean>
注:這個性能分析插件配置了兩個屬性,第一個是格式化sql語句殖妇,設(shè)置為true后刁笙,sql語句格式就像上面的截圖中的一樣;第二個屬性是sql語句執(zhí)行的最大時間,超過value值就會報(bào)錯,這里表示超過1000毫秒就會停止執(zhí)行sql語句指蚁。
3哎壳、執(zhí)行分析插件:
<!-- 如果是對全表的刪除或更新操作,就會終止該操作 -->
<bean class="com.baomidou.mybatisplus.plugins.SqlExplainInterceptor">
<property name="stopProceed" value="true"/>
</bean>
注:這個插件配置了一個屬性,stopProceed設(shè)置為true后,如果執(zhí)行的是刪除表中全部內(nèi)容,那就會拋出異常烦租,終止該操作。該插件主要是防止手抖誤刪數(shù)據(jù)除盏。
@Test
public void testSqlExplain(){
//條件為null叉橱,就是刪除全表,執(zhí)行分析插件會終止該操作
emplopyeeDao.delete(null);
}
運(yùn)行該junit測試者蠕,可以看到報(bào)如下錯誤窃祝,說明該插件生效了。
三踱侣、MP的逆向工程:
MyBatis 的代碼生成器基于xml文件進(jìn)行生成粪小,可生成: 實(shí)體類大磺、Mapper 接口、Mapper 映射文件探膊。
MP 的代碼生成器基于Java代碼進(jìn)行生成杠愧,可生成: 實(shí)體類(可以選擇是否支持 AR)、Mapper 接口逞壁、Mapper 映射文件流济、 Service 層、Controller 層腌闯。
1绳瘟、添加依賴:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.37</version>
</dependency>
<!-- mp 依賴 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>2.3</version>
</dependency>
<!-- mybatisplus逆向工程需要模板引擎,用freemaker也行 -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.0</version>
</dependency>
注:上面是必須的三個依賴姿骏,為了可以在控制臺直觀的看到生成情況糖声,可以添加日志包(slf4j-api和slf4j-log4j2),為了讓生成的代碼不會報(bào)錯分瘦,還可以根據(jù)情況添加spring相關(guān)的依賴蘸泻、lombok依賴等。
2嘲玫、生成器示例代碼:
/**
* @author: zhu
* @date: 2018/8/20 11:17
* mybatis-plus逆向工程示例代碼
*/
public class test {
@Test
public void testGenerator(){
//1悦施、全局配置
GlobalConfig config = new GlobalConfig();
config.setActiveRecord(true)//開啟AR模式
.setAuthor("zhu")//設(shè)置作者
//生成路徑(一般都是生成在此項(xiàng)目的src/main/java下面)
.setOutputDir("E:\\develop\\Java\\workspace\\ideaworkspace\\mpg\\src\\main\\java")
.setFileOverride(true)//第二次生成會把第一次生成的覆蓋掉
.setIdType(IdType.AUTO)//主鍵策略
.setServiceName("%sService")//生成的service接口名字首字母是否為I,這樣設(shè)置就沒有I
.setBaseResultMap(true)//生成resultMap
.setBaseColumnList(true);//在xml中生成基礎(chǔ)列
//2趁冈、數(shù)據(jù)源配置
DataSourceConfig dataSourceConfig = new DataSourceConfig();
dataSourceConfig.setDbType(DbType.MYSQL)//數(shù)據(jù)庫類型
.setDriverName("com.mysql.jdbc.Driver")
.setUrl("jdbc:mysql:///數(shù)據(jù)庫名")
.setUsername("數(shù)據(jù)庫用戶名")
.setPassword("數(shù)據(jù)庫密碼");
//3歼争、策略配置
StrategyConfig strategyConfig = new StrategyConfig();
strategyConfig.setCapitalMode(true)//開啟全局大寫命名
.setDbColumnUnderline(true)//表名字段名使用下劃線
.setNaming(NamingStrategy.underline_to_camel)//下劃線到駝峰的命名方式
.setTablePrefix("tb_")//表名前綴
.setEntityLombokModel(true)//使用lombok
.setInclude("表1","表2");//逆向工程使用的表
//4拜马、包名策略配置
PackageConfig packageConfig = new PackageConfig();
packageConfig.setParent("com.zhu.mpg")//設(shè)置包名的parent
.setMapper("mapper")
.setService("service")
.setController("controller")
.setEntity("entity")
.setXml("mapper");//設(shè)置xml文件的目錄
//5渗勘、整合配置
AutoGenerator autoGenerator = new AutoGenerator();
autoGenerator.setGlobalConfig(config)
.setDataSource(dataSourceConfig)
.setStrategy(strategyConfig)
.setPackageInfo(packageConfig);
//6、執(zhí)行
autoGenerator.execute();
}
}
注:以上便是示例代碼俩莽,只要運(yùn)行該junit測試旺坠,就會生成entity、mapper接口扮超、mapper的xml文件取刃、service、serviceImpl出刷、controller代碼璧疗。每一個設(shè)置代碼中均有詳細(xì)注釋,此處不再贅述馁龟。
四崩侠、自定義全局操作:
(一)、AutoSqlInjector :
BaseMapper提供了17個常用方法坷檩,但是有些需求這些方法還是不能很好的實(shí)現(xiàn)却音,那么怎么辦呢改抡?大家肯定會想到是在xml文件中寫sql語句解決。這樣確實(shí)可以系瓢,因?yàn)镸P是只做增強(qiáng)不做改變阿纤,我們完全可以按照mybatis的原來的方式來解決。不過MP也提供了另一種解決辦法夷陋,那就是自定義全局操作欠拾。所謂自定義全局操作,也就是我們可以在mapper中自定義一些方法肌稻,然后通過某些操作清蚀,讓自定義的這個方法也能像BaseMapper的內(nèi)置方法,供全局調(diào)用爹谭。接下來就看看如何實(shí)現(xiàn)(以deleteAll方法為例)枷邪。
1、在mapper接口中定義方法:
public interface EmplopyeeDao extends BaseMapper<Employee> {
int deleteAll();
}
public interface UserDao extends BaseMapper<User> {
int deleteAll();
}
在這兩個mapper接口中都定義了deleteAll方法诺凡。
2东揣、編寫自定義注入類:
public class MySqlInjector extends AutoSqlInjector {
@Override
public void inject(Configuration configuration, MapperBuilderAssistant builderAssistant,
Class<?> mapperClass, Class<?> modelClass, TableInfo table) {
/* 添加一個自定義方法 */
deleteAllUser(mapperClass, modelClass, table);
System.out.println(table.getTableName());
}
public void deleteAllUser(Class<?> mapperClass, Class<?> modelClass, TableInfo table) {
/* 執(zhí)行 SQL ,動態(tài) SQL 參考類 SqlMethod */
String sql = "delete from " + table.getTableName();
/* mapper 接口方法名一致 */
String method = "deleteAll";
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
this.addDeleteMappedStatement(mapperClass, method, sqlSource);
}
}
注:該類繼承AutoSqlInjector腹泌,重寫inject方法嘶卧。然后編寫sql語句,指定mapper接口中的方法凉袱,最后調(diào)用addDeleteMappedStatement方法即可芥吟。
3、在spring配置文件中配置:
<!-- 定義自定義注入器 -->
<bean class="com.zhu.mybatisplus.injector.MySqlInjector" id="mySqlInjector"/>
<!-- 5专甩、mybatisplus的全局策略配置 -->
<bean id="globalConfiguration" class="com.baomidou.mybatisplus.entity.GlobalConfiguration">
<property name="idType" value="0"/>
<property name="tablePrefix" value="tb_"/>
<!-- 注入自定義全局操作 -->
<property name="sqlInjector" ref="mySqlInjector"/>
</bean>
注:先把剛才自定義的類注冊成bean钟鸵,然后在全局策略配置的bean中引用自定義類的bean即可。
4涤躲、測試:
@Test
public void testMySqlInjector(){
Integer result = userDao.deleteAll();
System.out.println(result);
}
@Test
public void testMySqlInjector2(){
Integer result = emplopyeeDao.deleteAll();
System.out.println(result);
}
注:經(jīng)測試棺耍,當(dāng)userDao調(diào)用deleteAll方法時,會刪除tb_user表的所有數(shù)據(jù)种樱,employeeDao調(diào)用deleteAll方法時蒙袍,會刪除tb_employee表的所有數(shù)據(jù)。說明deleteAll方法是有效的嫩挤。不過在運(yùn)行這兩個測試時害幅,由于是全表刪除操作,所有要先把執(zhí)行分析插件關(guān)了岂昭。
(二)以现、邏輯刪除:
其實(shí)數(shù)據(jù)并不會輕易的刪除掉,畢竟數(shù)據(jù)收集不易,所以就有了邏輯刪除叼风。邏輯刪除: 并不會真正的從數(shù)據(jù)庫中將數(shù)據(jù)刪除掉取董,而是將當(dāng)前被刪除的這條數(shù)據(jù)中的一個邏輯刪除字段置為刪除狀態(tài),比如該數(shù)據(jù)有一個字段logic_flag无宿,當(dāng)其值為1表示未刪除茵汰,值為-1表示刪除,那么邏輯刪除就是將1變成-1孽鸡。
1蹂午、數(shù)據(jù)表:
在數(shù)據(jù)表中需要添加邏輯刪除字段(logic_flag)。
2彬碱、實(shí)體類:
@Data
public class User{
private Integer id;
private String name;
private Integer age;
private Integer gender;
@TableLogic //標(biāo)記邏輯刪除屬性
private Integer logicFlag;
}
注:數(shù)據(jù)庫中邏輯刪除字段是logic_flag豆胸,所以實(shí)體類中的logicFlag需要用@TableLogic注解標(biāo)記。
3巷疼、mapper:
public interface UserDao extends BaseMapper<User> {
}
4晚胡、配置邏輯刪除:
需要在spring-dao.xml中做如下配置:
首先定義邏輯刪除的bean:
<!-- 邏輯刪除 -->
<bean class="com.baomidou.mybatisplus.mapper.LogicSqlInjector" id="logicSqlInjector"/>
再在全局配置的bean中注入邏輯刪除以及邏輯刪除值:
<!-- 5、mybatisplus的全局策略配置 -->
<bean id="globalConfiguration" class="com.baomidou.mybatisplus.entity.GlobalConfiguration">
<!-- 此處省略其他全局配置 -->
<!-- 注入自定義全局操作嚼沿,做邏輯刪除時需要先注釋掉 -->
<!--<property name="sqlInjector" ref="mySqlInjector"/>-->
<!-- 注入邏輯刪除估盘,先要把自定義的注釋掉 -->
<property name="sqlInjector" ref="logicSqlInjector"/>
<!-- 注入邏輯刪除值 -->
<property name="logicDeleteValue" value="-1"/><!-- -1是刪除狀態(tài) -->
<property name="logicNotDeleteValue" value="1"/><!-- 1是未刪除狀態(tài) -->
</bean>
注:因?yàn)檫壿媱h除實(shí)際上也是一個sqlInjector,所以先要把剛才做自定義全局操作時注入的自定義全局操作注釋掉骡尽,上面代碼中已有詳細(xì)注釋說明遣妥。
6、測試:
@Test
public void testLogicDelete(){
Integer result = userDao.deleteById(1);
System.out.println(result);
//User user = userDao.selectById(1);
//System.out.println(user);
}
注:運(yùn)行該測試攀细,執(zhí)行刪除操作的時候箫踩,真正執(zhí)行的sql語句是UPDATE tb_user SET logic_flag=-1 WHERE id=?
,就是把邏輯刪除字段的值設(shè)置為-1谭贪;當(dāng)邏輯刪除字段的值是-1時再執(zhí)行查詢操作境钟,sql是SELECT ... FROM tb_user WHERE id=? AND logic_flag=1
,所以查詢結(jié)果是null故河。
五吱韭、公共字段自動填充:
我們知道吆豹,當(dāng)我們進(jìn)行插入或者更新操作時鱼的,沒有設(shè)置值的屬性,那么在數(shù)據(jù)表中要么是為null痘煤,要么是保留原來的值凑阶。有的時候我們我們沒有賦值但是卻不想讓其為空,比如name屬性衷快,我們插入時會默認(rèn)賦上“林志玲”宙橱,更新時會默認(rèn)賦值上“朱茵”,那么就可以用公共字段自動填充。
1师郑、使用@TableField注解標(biāo)記填充字段
@TableField(fill = FieldFill.INSERT_UPDATE)//插入和更新時填充
private String name;
2环葵、編寫公共字段填充處理器類:
public class MyMetaObjectHandler extends MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
Object fieldValue = getFieldValByName("name",metaObject); //獲取需要填充的字段
if(fieldValue == null){ //如果該字段沒有設(shè)置值
setFieldValByName("name","林志玲",metaObject); //那就將其設(shè)置為"林志玲"
}
}
@Override
public void updateFill(MetaObject metaObject) {
Object fieldValue = getFieldValByName("name",metaObject);//獲取需要填充的字段
if(fieldValue == null){ //如果該字段沒有設(shè)置值
setFieldValByName("name","朱茵",metaObject); //那就將其設(shè)置為"朱茵"
}
}
}
注:該類繼承了MetaObjectHandler類,重寫了insertFill和updateFill方法宝冕,在這兩個方法獲取需要填充的字段以及默認(rèn)填充的值张遭。
3、在spring-dao.xml中配置:
<!-- 公共字段填充處理器 -->
<bean class="com.zhu.mybatisplus.handler.MyMetaObjectHandler" id="myMetaObjectHandler"/>
<!-- 5地梨、mybatisplus的全局策略配置 -->
<bean id="globalConfiguration" class="com.baomidou.mybatisplus.entity.GlobalConfiguration">
<!-- 此處省略其他配置 -->
<!-- 注入公共字段填充處理器 -->
<property name="metaObjectHandler" ref="myMetaObjectHandler"/>
</bean>
注:和配置邏輯刪除一樣菊卷,都是先將自定義的類注冊成bean,再在全局策略配置中引用這個bean即可宝剖。
4洁闰、測試:
@Test
public void testHandlerInsert() {
User user = new User();
user.setGender(1);
user.setAge(22);
user.setLogicFlag(1);
userDao.insert(user);
}
注:可以看到,雖然我們并沒有給name賦值万细,但是已經(jīng)自動把“林志玲”傳進(jìn)去了扑眉。更新時也一樣有效,此處就不將測試代碼貼出來了赖钞。
總結(jié):
mybatis-plus的大部分用法都在《mybatis-plus的使用 ------ 入門》和本文中講解到了襟雷,總的來說包括但不限于以下知識點(diǎn):
通用crud、全局策略配置仁烹、條件構(gòu)造器耸弄、AR模式、插件配置卓缰、代碼生成器计呈、自定義全局操作、公共字段自動填充等功能征唬。熟練使用這些功能捌显,定能讓你寫代碼時感覺游刃有余!