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?這款開源的數據庫管理工具界面更炫酷!
- 還在從零開始搭建項目?這款升級版快速開發腳手架值得一試!