MyBatis是一款非常流行的ORM架構,相信很多小夥伴都在使用。我們經常會把它和MyBatis-Plus或者MBG一起使用,用多了之後對于其一些正常操作就不太熟悉了。最近總結了下MyBatis的實用用法和技巧,希望對大家有所幫助!
MyBatis簡介
MyBatis是一款優秀的開源持久層架構,支援自定義SQL查詢、存儲過程和進階映射,目前在Github上已有17k+Star。在MyBatis中,我們可以在XML中編寫SQL語句,然後綁定到Java方法中,通過參數和結果集的自動映射來實作複雜的查詢邏輯。MyBatis消除了幾乎所有JDBC操作和手動綁定參數操作,使用起來非常友善!
在SpringBoot中內建
下面我們來聊聊MyBatis在SpringBoot中的使用,首先我們需要內建它。
- 在pom.xml中添加MyBatis提供的Spring Boot Starter;
<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目錄下;
mybatis:
mapper-locations:
- classpath:dao/*.xml
- 然後添加Java配置,通過@MapperScan配置好Dao接口路徑,這樣就可以開始使用了。
/**
* 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;
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,再添加對應的方法;
/**
* 自定義UmsAdmin表查詢
* Created by macro on 2022/10/20.
*/
@Repository
public interface UmsAdminDao {
/**
* 根據ID查詢使用者
*/
UmsAdmin selectByIdSimple(Long id);
}
- 再建立xml檔案UmsAdminDao.xml,添加查詢方法的SQL實作;
<select id="selectByIdSimple" resultType="com.macro.mall.tiny.model.UmsAdmin">
select * from ums_admin where id = #{id}
</select>
- 然後編寫測試類,注入Dao,調用Dao方法來進行測試;
/**
* 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);
}
}
- 此時你會發現,對于一些資料庫表中以下劃線分割的傳回字段無法自動映射,可以通過對字段取别名的方式來進行映射;
<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中開啟全局下劃線自動轉駝峰功能來解決,個人習慣使用第一種。
mybatis:
configuration:
# 下劃線自動轉駝峰
map-underscore-to-camel-case: true
insert
- 接下來我們來編寫一個插入單個使用者的方法;
/**
* 自定義UmsAdmin表查詢
* Created by macro on 2022/10/20.
*/
@Repository
public interface UmsAdminDao {
/**
* 插入使用者
*/
int insert(UmsAdmin entity);
}
- 然後在xml中編寫對應的SQL實作,這裡需要注意的是如果想傳回插入後的自增ID的話,需要使用selectKey标簽進行配置。
<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修改使用者資訊的方法;
/**
* 自定義UmsAdmin表查詢
* Created by macro on 2022/10/20.
*/
@Repository
public interface UmsAdminDao {
/**
* 根據ID修改使用者資訊
*/
int updateById(UmsAdmin entity);
}
- 然後在xml中編寫對應的SQL實作。
<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删除使用者的方法;
/**
* 自定義UmsAdmin表查詢
* Created by macro on 2022/10/20.
*/
@Repository
public interface UmsAdminDao {
/**
* 根據ID删除使用者
*/
int deleteById(Long id);
}
- 然後在xml中編寫對應的SQL實作。
<delete id="deleteById">
delete from ums_admin where id = #{id}
</delete>
動态SQL
通過MyBatis的動态SQL功能,我們可以靈活地在xml中實作各種複雜的操作,動态SQL功能需要依賴MyBatis的各種标簽,下面我們就來學習下。
if
- if标簽可以實作判斷邏輯,這裡我們以根據使用者名和Email模糊查詢使用者為例,來聊聊它的使用;
/**
* 自定義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實作如下。
<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時,會查詢出全部使用者,我們如果想不查詢出使用者,可以使用它;
<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關鍵字。
<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選擇性修改使用者資訊的方法;
/**
* 自定義UmsAdmin表查詢
* Created by macro on 2022/10/20.
*/
@Repository
public interface UmsAdminDao {
/**
* 根據ID選擇性修改使用者資訊
*/
int updateByIdSelective(UmsAdmin entity);
}
- 方法對應的SQL實作如下,這裡既避免了使用set關鍵字,也會将多餘的逗号去除。
<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的邏輯,例如我們現在需要編寫一個批量插入使用者的方法;
/**
* 自定義UmsAdmin表查詢
* Created by macro on 2022/10/20.
*/
@Repository
public interface UmsAdminDao {
/**
* 批量插入使用者
*/
int insertBatch(@Param("entityList") List<UmsAdmin> adminList);
}
- 在xml中的對應SQL實作如下,在foreach标簽中的内容會根據傳入的集合參數進行循環拼接;
<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批量查詢的方法;
/**
* 自定義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屬性指定拼接語句的前字尾。
<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屬性;
/**
* UmsResource擴充類
* Created by macro on 2022/10/20.
*/
@Data
public class UmsResourceExt extends UmsResource {
private UmsResourceCategory category;
}
- 例如我們需要編寫一個根據資源ID擷取資源及分類資訊的方法;
/**
* 自定義UmsResource表查詢
* Created by macro on 2022/10/20.
*/
@Repository
public interface UmsResourceDao {
/**
* 根據資源ID擷取資源及分類資訊
*/
UmsResourceExt selectResourceWithCategory(Long id);
}
- 在xml中的具體SQL實作如下,我們可以通過給ums_resource_category表中字段取以category.xxx的别名來自動進行自動映射;
<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 version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.macro.mall.tiny.dao.UmsResourceCategoryDao">
<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>
</mapper>
- 在UmsResourceDao.xml中我們可以這樣實作;
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.macro.mall.tiny.dao.UmsResourceDao">
<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>
</mapper>
- 編寫完成後,我們的一對一ResultMap實作就很簡單了,我們可以使用association标簽進行一對一管理,配置columnPrefix屬性将比對到的字段直接映射到關聯對象中去;
<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_字首以便于映射。
<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查詢時,一對多的情況也比較常見,例如這裡的分類和資源就是一對多的情況;
/**
* UmsResourceCategory擴充類
* Created by macro on 2022/10/20.
*/
@Data
public class UmsResourceCategoryExt extends UmsResourceCategory {
private List<UmsResource> resourceList;
}
- 例如我們現在需要編寫一個根據分類ID擷取分類及對應資源的方法;
/**
* 自定義UmsResourceCategory表查詢
* Created by macro on 2022/10/20.
*/
@Repository
public interface UmsResourceCategoryDao {
/**
* 根據分類ID擷取分類及對應資源
*/
UmsResourceCategoryExt selectCategoryWithResource(Long id);
}
- 在實作具體SQL前,我們需要先在xml中配置一個ResultMap,通過collection标簽建立一對多關系;
<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。
<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;
<!--MyBatis分頁插件-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.2</version>
</dependency>
- 然後在查詢方法之前使用它的startPage方法傳入分頁參數即可,分頁後的得到的資料可以在PageInfo中擷取到。
/**
* 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的用法有所遺忘的時候拿出來看看。
項目源碼位址
https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-mybatis
來源:https://mp.weixin.qq.com/s/X11OCZSEh2k5K6uYG768yQ