MyBatis這樣用,同事直呼哇塞,堪稱最佳實踐!
MyBatis是一款非常流行的ORM框架,相信很多小夥伴都在使用。我們經常會把它和MyBatis-Plus或者MBG一起使用,用多了之後對於其一些常規操作就不太熟悉了。最近總結了下MyBatis的實用用法和技巧,希望對大家有所幫助!
SpringBoot實戰電商專案mall(50k+star)地址:http://github.com/macrozheng/mall
MyBatis簡介
MyBatis是一款優秀的開源持久層框架,支援自定義SQL查詢、儲存過程和高階對映,目前在Github上已有17k+Star
。在MyBatis中,我們可以在XML中編寫SQL語句,然後繫結到Java方法中,通過引數和結果集的自動對映來實現複雜的查詢邏輯。MyBatis消除了幾乎所有JDBC操作和手動繫結引數操作,使用起來非常方便!
在SpringBoot中整合
下面我們來聊聊MyBatis在SpringBoot中的使用,首先我們需要整合它。
- 在
pom.xml
中新增MyBatis提供的Spring Boot Starter;
xml
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
- 然後在
application.yml
中配置好編寫SQL實現的xml
檔案路徑,這裡我們存放在resources/dao
目錄下;
yaml
mybatis:
mapper-locations:
- classpath:dao/*.xml
- 然後新增Java配置,通過
@MapperScan
配置好Dao介面路徑,這樣就可以開始使用了。
java
/**
* MyBatis配置類
* Created by macro on 2019/4/8.
*/
@Configuration
@MapperScan("com.macro.mall.tiny.dao")
public class MyBatisConfig {
}
基本使用
下面我們來聊聊MyBatis的基本使用方法,涵蓋了基本的增刪改查操作。
表結構說明
這裡將以mall專案中許可權管理模組相關表為例進行介紹,具體表結構如下。
專案結構說明
本文示例使用了mall-learning
專案中的mall-tiny-mybatis
模組程式碼,具體專案結構如下。
select
- 首先是查詢操作,這裡我們以後臺使用者表
ums_admin
為例,編寫一個根據ID查詢使用者
的方法,先建立實體類UmsAdmin
;
```java public class UmsAdmin implements Serializable { private Long id;
private String username;
private String password;
@ApiModelProperty(value = "頭像")
private String icon;
@ApiModelProperty(value = "郵箱")
private String email;
@ApiModelProperty(value = "暱稱")
private String nickName;
@ApiModelProperty(value = "備註資訊")
private String note;
@ApiModelProperty(value = "建立時間")
private Date createTime;
@ApiModelProperty(value = "最後登入時間")
private Date loginTime;
@ApiModelProperty(value = "帳號啟用狀態:0->禁用;1->啟用")
private Integer status;
} ```
- 然後建立資料操作的介面
UmsAdminDao
,再新增對應的方法;
```java /* * 自定義UmsAdmin表查詢 * Created by macro on 2022/10/20. / @Repository public interface UmsAdminDao {
/**
* 根據ID查詢使用者
*/
UmsAdmin selectByIdSimple(Long id);
} ```
- 再建立
xml
檔案UmsAdminDao.xml
,新增查詢方法的SQL實現;
xml
<select id="selectByIdSimple" resultType="com.macro.mall.tiny.model.UmsAdmin">
select * from ums_admin where id = #{id}
</select>
- 然後編寫測試類,注入Dao,呼叫Dao方法來進行測試;
```java /* * MyBatis基本操作測試 * Created by macro on 2022/10/20. / @SpringBootTest public class MyBatisBaseTest {
private static final Logger LOGGER = LoggerFactory.getLogger(MyBatisBaseTest.class);
@Autowired
private UmsAdminDao umsAdminDao;
@Test
void testSelectByIdSimple(){
UmsAdmin umsAdmin = umsAdminDao.selectByIdSimple(1L);
LOGGER.info("testSelectByIdSimple result={}",umsAdmin);
}
} ```
- 此時你會發現,對於一些資料庫表中以
下劃線
分割的返回欄位無法自動對映,可以通過對欄位取別名的方式來進行對映;
xml
<select id="selectById" resultType="com.macro.mall.tiny.model.UmsAdmin">
select username,
password,
icon,
email,
nick_name as nickName,
note,
create_time as createTime,
login_time as loginTime,
status
from ums_admin
where id = #{id}
</select>
- 如果你覺得這種方式比較麻煩,也可以通過在
application.yml
中開啟全域性下劃線自動轉駝峰功能來解決,個人習慣使用第一種。
yaml
mybatis:
configuration:
# 下劃線自動轉駝峰
map-underscore-to-camel-case: true
insert
- 接下來我們來編寫一個
插入單個使用者
的方法;
```java /* * 自定義UmsAdmin表查詢 * Created by macro on 2022/10/20. / @Repository public interface UmsAdminDao {
/**
* 插入使用者
*/
int insert(UmsAdmin entity);
} ```
- 然後在xml中編寫對應的SQL實現,這裡需要注意的是如果想返回插入後的自增ID的話,需要使用
selectKey
標籤進行配置。
xml
<insert id="insert">
insert into ums_admin(username, password, icon, email, nick_name, note, create_time, login_time)
values (#{username}, #{password}, #{icon}, #{email}, #{nickName}, #{note}, #{createTime}, #{loginTime})
<selectKey keyColumn="id" resultType="long" keyProperty="id" order="AFTER">
SELECT LAST_INSERT_ID()
</selectKey>
</insert>
update
- 接下來我們來編寫一個
根據ID修改使用者資訊
的方法;
java
/**
* 自定義UmsAdmin表查詢
* Created by macro on 2022/10/20.
*/
@Repository
public interface UmsAdminDao {
/**
* 根據ID修改使用者資訊
*/
int updateById(UmsAdmin entity);
}
- 然後在xml中編寫對應的SQL實現。
xml
<update id="updateById">
update ums_admin
set username = #{username},
password = #{password},
icon = #{icon},
email = #{email},
nick_name = #{nickName},
note = #{note},
create_time = #{createTime},
login_time = #{loginTime}
where id = #{id}
</update>
delete
- 接下來我們來編寫一個
根據ID刪除使用者
的方法;
java
/**
* 自定義UmsAdmin表查詢
* Created by macro on 2022/10/20.
*/
@Repository
public interface UmsAdminDao {
/**
* 根據ID刪除使用者
*/
int deleteById(Long id);
}
- 然後在xml中編寫對應的SQL實現。
xml
<delete id="deleteById">
delete from ums_admin where id = #{id}
</delete>
動態SQL
通過MyBatis的動態SQL功能,我們可以靈活地在xml中實現各種複雜的操作,動態SQL功能需要依賴MyBatis的各種標籤,下面我們就來學習下。
if
if
標籤可以實現判斷邏輯,這裡我們以根據使用者名稱和Email模糊查詢使用者
為例,來聊聊它的使用;
java
/**
* 自定義UmsAdmin表查詢
* Created by macro on 2022/10/20.
*/
@Repository
public interface UmsAdminDao {
/**
* 根據使用者名稱和Email模糊查詢使用者
* 不輸入查詢所有
*/
List<UmsAdmin> selectByUsernameAndEmailLike(@Param("username") String username, @Param("email") String email);
}
- xml中新增對應的SQL實現如下。
xml
<select id="selectByUsernameAndEmailLike" resultType="com.macro.mall.tiny.model.UmsAdmin">
select username,
password,
icon,
email,
nick_name as nickName,
note,
create_time as createTime,
login_time as loginTime,
status
from ums_admin
where 1=1
<if test="username!=null and username!=''">
and username like concat('%',#{username},'%')
</if>
<if test="email!=null and email!=''">
and email like concat('%',#{email},'%')
</if>
</select>
choose
choose
標籤也可以實現判斷邏輯,上面的例子中當我們不輸入使用者名稱和Email時,會查詢出全部使用者,我們如果想不查詢出使用者,可以使用它;
xml
<select id="selectByUsernameAndEmailLike2" resultType="com.macro.mall.tiny.model.UmsAdmin">
select username,
password,
icon,
email,
nick_name as nickName,
note,
create_time as createTime,
login_time as loginTime,
status
from ums_admin
where 1=1
<choose>
<when test="username!=null and username!=''">
and username like concat('%',#{username},'%')
</when>
<when test="email!=null and email!=''">
and email like concat('%',#{email},'%')
</when>
<otherwise>
and 1=2
</otherwise>
</choose>
</select>
where
- 上面的例子中我們為了SQL拼接不出錯,添加了
where 1=1
這樣的語句,其實可以使用where
標籤來實現查詢條件,當標籤內沒有內容時會自動去除where
關鍵字,同時還會去除開頭多餘的and
關鍵字。
xml
<select id="selectByUsernameAndEmailLike3" resultType="com.macro.mall.tiny.model.UmsAdmin">
select username,
password,
icon,
email,
nick_name as nickName,
note,
create_time as createTime,
login_time as loginTime,
status
from ums_admin
<where>
<if test="username!=null and username!=''">
and username like concat('%',#{username},'%')
</if>
<if test="email!=null and email!=''">
and email like concat('%',#{email},'%')
</if>
</where>
</select>
set
- 當我們拼接更新欄位的語句時,也會面臨同樣的問題,此時可以使用
set
標籤來解決,比如我們現在想寫一個根據ID選擇性修改使用者資訊
的方法;
java
/**
* 自定義UmsAdmin表查詢
* Created by macro on 2022/10/20.
*/
@Repository
public interface UmsAdminDao {
/**
* 根據ID選擇性修改使用者資訊
*/
int updateByIdSelective(UmsAdmin entity);
}
- 方法對應的SQL實現如下,這裡既避免了使用
set
關鍵字,也會將多餘的逗號去除。
xml
<update id="updateByIdSelective">
update ums_admin
<set>
<if test="username!=null and username!=''">
username = #{username},
</if>
<if test="password!=null and password!=''">
password = #{password},
</if>
<if test="icon!=null and icon!=''">
icon = #{icon},
</if>
<if test="email!=null and email!=''">
email = #{email},
</if>
<if test="nickName!=null and nickName!=''">
nick_name = #{nickName},
</if>
<if test="note!=null and note!=''">
note = #{note},
</if>
<if test="createTime!=null">
create_time = #{createTime},
</if>
<if test="loginTime!=null">
login_time = #{loginTime},
</if>
</set>
where id = #{id}
</update>
foreach
- 通過
foreach
我們可以實現一些迴圈拼接SQL的邏輯,例如我們現在需要編寫一個批量插入使用者
的方法;
java
/**
* 自定義UmsAdmin表查詢
* Created by macro on 2022/10/20.
*/
@Repository
public interface UmsAdminDao {
/**
* 批量插入使用者
*/
int insertBatch(@Param("entityList") List<UmsAdmin> adminList);
}
- 在xml中的對應SQL實現如下,在
foreach
標籤中的內容會根據傳入的集合引數進行迴圈拼接;
xml
<insert id="insertBatch">
insert into ums_admin(username, password, icon, email, nick_name, note, create_time, login_time) values
<foreach collection="entityList" separator="," item="item">
(#{item.username}, #{item.password}, #{item.icon}, #{item.email}, #{item.nickName}, #{item.note}, #{item.createTime}, #{item.loginTime})
</foreach>
</insert>
- 再例如我們現在需要編寫一個
根據使用者ID批量查詢
的方法;
java
/**
* 自定義UmsAdmin表查詢
* Created by macro on 2022/10/20.
*/
@Repository
public interface UmsAdminDao {
/**
* 根據使用者ID批量查詢
*/
List<UmsAdmin> selectByIds(@Param("ids") List<Long> ids);
}
- 在xml中的對應SQL實現如下,我們可以使用
open
、close
屬性指定拼接語句的前後綴。
xml
<select id="selectByIds" resultType="com.macro.mall.tiny.model.UmsAdmin">
select username,
password,
icon,
email,
nick_name as nickName,
note,
create_time as createTime,
login_time as loginTime,
status
from ums_admin
where id in
<foreach collection="ids" item="item" open="(" close=")" separator=",">
#{item}
</foreach>
</select>
高階查詢
介紹完MyBatis的基本操作後,我們再來介紹下MyBatis的高階查詢功能。
一對一對映
- 在我們平時進行SQL查詢時,往往會有
一對一
的情況,比如說我們這裡有資源分類ums_resource_category
和資源ums_resource
兩張表,資源和分類就是一對一的關係,如果你不想改動原實體類的話,可以編寫一個擴充套件類繼承UmsResource
,幷包含UmsResourceCategory
屬性;
```java /* * UmsResource擴充套件類 * Created by macro on 2022/10/20. / @Data public class UmsResourceExt extends UmsResource {
private UmsResourceCategory category;
} ```
- 例如我們需要編寫一個
根據資源ID獲取資源及分類資訊
的方法;
java
/**
* 自定義UmsResource表查詢
* Created by macro on 2022/10/20.
*/
@Repository
public interface UmsResourceDao {
/**
* 根據資源ID獲取資源及分類資訊
*/
UmsResourceExt selectResourceWithCategory(Long id);
}
- 在xml中的具體SQL實現如下,我們可以通過給
ums_resource_category
表中欄位取以category.xxx
的別名來自動進行自動對映;
xml
<select id="selectResourceWithCategory" resultType="com.macro.mall.tiny.domain.UmsResourceExt">
select ur.id,
ur.create_time as createTime,
ur.name,
ur.url,
ur.description,
ur.category_id as categoryId,
urc.id as "category.id",
urc.name as "category.name",
urc.sort as "category.sort",
urc.create_time as "category.createTime"
from ums_resource ur
left join ums_resource_category urc on ur.category_id = urc.id
where ur.id = #{id}
</select>
- 當然除了這種方式以外,我們還可以通過
ResultMap
+association
標籤來實現,不過在此之前我們在編寫xml檔案的時候,一般習慣於先給當前檔案寫一個BaseResultMap
,用於對當前表的欄位和物件屬性進行直接對映,例如在UmsResourceCategoryDao.xml
中這樣實現;
```xml
<resultMap id="BaseResultMap" type="com.macro.mall.tiny.model.UmsResourceCategory">
<id property="id" column="id"/>
<result property="createTime" column="create_time"/>
<result property="name" column="name"/>
<result property="sort" column="sort"/>
</resultMap>
```
- 在
UmsResourceDao.xml
中我們可以這樣實現;
```xml
<resultMap id="BaseResultMap" type="com.macro.mall.tiny.model.UmsResource">
<id property="id" column="id"/>
<result property="createTime" column="create_time"/>
<result property="name" column="name"/>
<result property="url" column="url"/>
<result property="description" column="description"/>
<result property="categoryId" column="category_id"/>
</resultMap>
```
- 編寫完成後,我們的一對一
ResultMap
實現就很簡單了,我們可以使用association
標籤進行一對一管理,配置columnPrefix
屬性將匹配到的欄位直接對映到關聯物件中去;
xml
<resultMap id="ResourceWithCategoryMap" type="com.macro.mall.tiny.domain.UmsResourceExt" extends="BaseResultMap">
<association property="category" resultMap="com.macro.mall.tiny.dao.UmsResourceCategoryDao.BaseResultMap" columnPrefix="category_"/>
</resultMap>
- 然後再編寫下Dao中方法對應SQL實現即可,這裡直接使用上面的ResultMap,同時給
ums_resource_category
表中的欄位指定了category_
字首以便於對映。
xml
<select id="selectResourceWithCategory2" resultMap="ResourceWithCategoryMap">
select ur.id,
ur.create_time,
ur.name,
ur.url,
ur.description,
ur.category_id,
urc.id as category_id,
urc.name as category_name,
urc.sort as category_sort,
urc.create_time as category_create_time
from ums_resource ur
left join ums_resource_category urc on ur.category_id = urc.id
where ur.id = #{id}
</select>
一對多對映
- 在編寫SQL查詢時,一對多的情況也比較常見,例如這裡的分類和資源就是一對多的情況;
```java /* * UmsResourceCategory擴充套件類 * Created by macro on 2022/10/20. / @Data public class UmsResourceCategoryExt extends UmsResourceCategory {
private List<UmsResource> resourceList;
} ```
- 例如我們現在需要編寫一個
根據分類ID獲取分類及對應資源
的方法;
```java /* * 自定義UmsResourceCategory表查詢 * Created by macro on 2022/10/20. / @Repository public interface UmsResourceCategoryDao {
/**
* 根據分類ID獲取分類及對應資源
*/
UmsResourceCategoryExt selectCategoryWithResource(Long id);
} ```
- 在實現具體SQL前,我們需要先在xml中配置一個ResultMap,通過
collection
標籤建立一對多關係;
xml
<resultMap id="selectCategoryWithResourceMap" type="com.macro.mall.tiny.domain.UmsResourceCategoryExt" extends="BaseResultMap">
<collection property="resourceList" columnPrefix="resource_" resultMap="com.macro.mall.tiny.dao.UmsResourceDao.BaseResultMap"/>
</resultMap>
- 然後在xml中編寫具體的SQL實現,使用該ResultMap。
xml
<select id="selectCategoryWithResource" resultMap="selectCategoryWithResourceMap">
select urc.id,
urc.create_time,
urc.name,
urc.sort,
ur.id resource_id,
ur.create_time resource_create_time,
ur.name resource_name,
ur.url resource_url,
ur.description resource_description,
ur.category_id resource_category_id
from ums_resource_category urc
left join ums_resource ur on urc.id = ur.category_id
where urc.id = #{id}
</select>
分頁外掛
- 我們平時實現查詢邏輯時,往往還會遇到分頁查詢的需求,直接使用開源的
PageHelper
外掛即可,首先在pom.xml
中新增它的Starter;
```xml
- 然後在查詢方法之前使用它的
startPage
方法傳入分頁引數即可,分頁後的得到的資料可以在PageInfo
中獲取到。
```java /* * UmsResource的Service介面實現類 * Created by macro on 2022/10/20. / @Service public class UmsResourceServiceImpl implements UmsResourceService {
@Autowired
private UmsResourceDao umsResourceDao;
@Override
public PageInfo<UmsResource> page(Integer pageNum, Integer pageSize,Long categoryId) {
PageHelper.startPage(pageNum,pageSize);
List<UmsResource> resourceList = umsResourceDao.selectListByCategoryId(categoryId);
PageInfo<UmsResource> pageInfo = new PageInfo<>(resourceList);
return pageInfo;
}
}
```
總結
本文主要介紹了MyBatis中一些比較常規的用法,涵蓋了SpringBoot整合、基本查詢、動態SQL和高階查詢,建議大家收藏起來,在對MyBatis的用法有所遺忘的時候拿出來看看。
專案原始碼地址
http://github.com/macrozheng/mall-learning/tree/master/mall-tiny-mybatis
- 還在用HttpUtil?SpringBoot 3.0全新HTTP客戶端工具來了,用起來夠優雅!
- Markdown還能這麼玩?這款開源神器絕了!
- MyBatis這樣用,同事直呼哇塞,堪稱最佳實踐!
- MyBatis-Plus同款Elasticsearch ORM框架,用起來夠優雅!
- 告別if else!試試這款輕量級流程引擎吧,自帶IDEA外掛真香!
- 堪稱一站式管理平臺,同時支援Linux、MySQL、Redis、MongoDB視覺化管理!
- 5分鐘自建資料庫視覺化平臺,線上管理資料庫也太方便了!
- 我上線了一個炫酷的專案實戰教程網站,主流技術一網打盡!
- 10 款更先進的開源命令列工具,太炫酷了!
- 看了我常用的IDEA外掛,同事也開始悄悄安裝了...
- 推薦一款微軟出品的開發神器,體驗不輸IDEA!
- 老版本Typora強制付費!試試這款開源替代品!
- 僅需一個依賴給Swagger換上新面板,既簡單又炫酷!
- 支援Nacos 2.1.0!這套Spring Cloud Gateway Oauth2 微服務許可權終極解決方案升級了!
- 還在用命令列看日誌?快用Kibana吧,視覺化日誌分析YYDS!
- Mall電商實戰專案全面升級!支援最新版SpringBoot,徹底解決迴圈依賴...
- 阿里出品!SpringBoot應用自動化部署神器,IDEA版Jenkins?
- 再見命令列!一鍵部署應用到遠端伺服器,IDEA官方Docker外掛真香!
- 還在用Navicat?這款開源的資料庫管理工具介面更炫酷!
- 還在從零開始搭建專案?這款升級版快速開發腳手架值得一試!