共计 12492 个字符,预计需要花费 32 分钟才能阅读完成。
MyBatis 是一款十分风行的 ORM 框架,置信很多小伙伴都在应用。咱们常常会把它和 MyBatis-Plus 或者 MBG 一起应用,用多了之后对于其一些惯例操作就不太熟悉了。最近总结了下 MyBatis 的实用用法和技巧,心愿对大家有所帮忙!
SpringBoot 实战电商我的项目 mall(50k+star)地址:https://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;
<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…