课程链接https://b11et3un53m.feishu.cn/wiki/PsyawI04ei2FQykqfcPcmd7Dnsc
只需要继承BaseMapper就能省去所有的单表CRUD
为了简化单表CRUD,MybatisPlus提供了一个基础的BaseMapper
接口,其中已经实现了单表的CRUD
因此我们自定义的Mapper只要实现了这个BaseMapper
,就无需自己实现单表CRUD了
常用注解
如何确定使用哪张表 ?
BaseMapper泛型中的User就是与数据库对应的PO.
@TableName(“user”)
@TableId标识实体类中的主键字段
@TableField(“isMarried”)普通字段注解
一般情况下我们并不需要给字段添加@TableField
注解,一些特殊情况除外:
- 成员变量名与数据库字段名不一致
- 成员变量是以
isXXX
命名,按照JavaBean
的规范,MybatisPlus
识别字段时会把is
去除,这就导致与数据库不符。 - 成员变量名与数据库一致,但是与数据库的关键字冲突。使用
@TableField
注解给字段名添加转义字符:````
@TableName("user")
public class User {
@TableId
private Long id;
private String name;
private Integer age;
@TableField("isMarried")
private Boolean isMarried;
@TableField("concat")
private String concat;
}
支持手写yaml
mybatis-plus:
mapper-locations: "classpath*:/mapper/**/*.xml" # Mapper.xml文件地址,当前这个是默认值。
条件查询
无论是修改、删除、查询,都可以使用QueryWrapper来构建查询条件。
@Test
void testQueryWrapper() {
// 1.构建查询条件 where name like "%o%" AND balance >= 1000
QueryWrapper<User> wrapper = new QueryWrapper<User>()
.select("id", "username", "info", "balance")
.like("username", "o")
.ge("balance", 1000);
// 2.查询数据
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}
@Test
void testUpdateByQueryWrapper() {
// 1.构建查询条件 where name = "Jack"
QueryWrapper<User> wrapper = new QueryWrapper<User>().eq("username", "Jack");
// 2.更新数据,user中非null字段都会作为set语句
User user = new User();
user.setBalance(2000);
userMapper.update(user, wrapper);
}
基于BaseMapper中的update方法更新时只能直接赋值,对于一些复杂的需求就难以实现。
@Test
void testUpdateWrapper() {
List<Long> ids = List.of(1L, 2L, 4L);
// 1.生成SQL
UpdateWrapper<User> wrapper = new UpdateWrapper<User>()
.setSql("balance = balance - 200") // SET balance = balance - 200
.in("id", ids); // WHERE id in (1, 2, 4)
// 2.更新,注意第一个参数可以给null,也就是不填更新字段和数据,
// 而是基于UpdateWrapper中的setSQL来更新
userMapper.update(null, wrapper);
}
论是QueryWrapper还是UpdateWrapper在构造条件的时候都需要写死字段名称,会出现字符串魔法值
。这在编程规范中显然是不推荐的。 那怎么样才能不写字段名,又能知道字段名呢?
其中一种办法是基于变量的gettter
方法结合反射技术。因此我们只要将条件对应的字段的getter
方法传递给MybatisPlus,它就能计算出对应的变量名了。而传递方法可以使用JDK8中的方法引用
和Lambda
表达式。 因此MybatisPlus又提供了一套基于Lambda的Wrapper,包含两个:
- LambdaQueryWrapper
- LambdaUpdateWrapper
@Test
void testLambdaQueryWrapper() {
// 1.构建条件 WHERE username LIKE "%o%" AND balance >= 1000
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.lambda()
.select(User::getId, User::getUsername, User::getInfo, User::getBalance)
.like(User::getUsername, "o")
.ge(User::getBalance, 1000);
// 2.查询
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}
自定义SQL
@Test
void testCustomWrapper() {
// 1.准备自定义查询条件
List<Long> ids = List.of(1L, 2L, 4L);
QueryWrapper<User> wrapper = new QueryWrapper<User>().in("id", ids);
// 2.调用mapper的自定义方法,直接传递Wrapper
userMapper.deductBalanceByIds(200, wrapper);
}
public interface UserMapper extends BaseMapper<User> {
@Select("UPDATE user SET balance = balance - #{money} ${ew.customSqlSegment}")
void deductBalanceByIds(@Param("money") int money, @Param("ew") QueryWrapper<User> wrapper);
}
理论上来讲MyBatisPlus是不支持多表查询的,不过我们可以利用Wrapper中自定义条件结合自定义SQL来实现多表查询的效果。 例如,我们要查询出所有收货地址在北京的并且用户id在1、2、4之中的用户
多表关联
Service接口
MybatisPlus不仅提供了BaseMapper,还提供了通用的Service接口及默认实现,封装了一些常用的service模板方法。
用接口为IService
,默认实现为ServiceImpl
public interface IUserService extends IService<User> {
// 拓展自定义方法
}
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
implements IUserService {
}
@GetMapping("/list")
@ApiOperation("根据id集合查询用户")
public List<UserVO> queryUsers(UserQuery query){
// 1.组织条件
String username = query.getName();
Integer status = query.getStatus();
Integer minBalance = query.getMinBalance();
Integer maxBalance = query.getMaxBalance();
LambdaQueryWrapper<User> wrapper = new QueryWrapper<User>().lambda()
.like(username != null, User::getUsername, username)
.eq(status != null, User::getStatus, status)
.ge(minBalance != null, User::getBalance, minBalance)
.le(maxBalance != null, User::getBalance, maxBalance);
// 2.查询用户
List<User> users = userService.list(wrapper);
// 3.处理vo
return BeanUtil.copyToList(users, UserVO.class);
}
在组织查询条件的时候,我们加入了 username != null
这样的参数,意思就是当条件成立时才会添加这个查询条件,类似Mybatis的mapper.xml文件中的<if>
标签。这样就实现了动态查询条件效果了。
不过,上述条件构建的代码太麻烦了。 因此Service中对LambdaQueryWrapper
和LambdaUpdateWrapper
的用法进一步做了简化。我们无需自己通过new
的方式来创建Wrapper
,而是直接调用lambdaQuery
和lambdaUpdate
方法:
@GetMapping("/list")
@ApiOperation("根据id集合查询用户")
public List<UserVO> queryUsers(UserQuery query){
// 1.组织条件
String username = query.getName();
Integer status = query.getStatus();
Integer minBalance = query.getMinBalance();
Integer maxBalance = query.getMaxBalance();
// 2.查询用户
List<User> users = userService.lambdaQuery()
.like(username != null, User::getUsername, username)
.eq(status != null, User::getStatus, status)
.ge(minBalance != null, User::getBalance, minBalance)
.le(maxBalance != null, User::getBalance, maxBalance)
.list();
// 3.处理vo
return BeanUtil.copyToList(users, UserVO.class);
}
可以发现lambdaQuery方法中除了可以构建条件,还需要在链式编程的最后添加一个list()
,这是在告诉MP我们的调用结果需要是一个list集合。这里不仅可以用list()
,可选的方法有:
.one()
:最多1个结果.list()
:返回集合结果.count()
:返回计数结果
MybatisPlus会根据链式编程的最后一个方法来判断最终的返回结果。
与lambdaQuery方法类似,IService中的lambdaUpdate方法可以非常方便的实现复杂更新业务。
批量新增
IService中的批量新增功能使用起来非常方便
savebatch 想改语句,直接修改yaml 的一个开关
扩展功能
代码生成
MybatisPlus
的插件
静态工具
有的时候Service之间也会相互调用,为了避免出现循环依赖问题,MybatisPlus提供一个静态工具类:Db
,其中的一些静态方法与IService
中方法签名基本一致,也可以帮助我们实现CRUD功能:
逻辑删除
通用枚举
数据库中是0,1
可以在java代码中变成枚举类
JSON类型处理器
数据库的user表中有一个info
字段,是JSON类型而目前User
实体类中却是String
类型
插件功能
分页插件
在未引入分页插件的情况下,MybatisPlus
是不支持分页功能的,IService
和BaseMapper
中的分页方法都无法正常起效。 所以,我们必须配置分页插件。
VSMybatis
mybatis
<select id="queryUserByIdAndAddr" resultType="com.itheima.mp.domain.po.User">
SELECT *
FROM user u
INNER JOIN address a ON u.id = a.user_id
WHERE u.id
<foreach collection="ids" separator="," item="id" open="IN (" close=")">
#{id}
</foreach>
AND a.city = #{city}
</select>
Mybatisplus
MyBatis和MyBatis-Plus(MyBatis Plus)都是流行的ORM(Object-Relational Mapping)框架,用于在Java应用程序中进行数据库操作。虽然MyBatis和MyBatis-Plus都提供了强大的数据库操作功能,但它们之间存在一些显著的差异。以下是对比和介绍:
MyBatis
MyBatis 是一个持久层框架,主要特点如下:
- 手动编写SQL:MyBatis需要开发者手动编写SQL语句,灵活性高,可以充分利用SQL的功能。
- XML映射文件:MyBatis使用XML文件来配置SQL语句和映射关系,也支持注解方式。
- 动态SQL支持:MyBatis提供了丰富的动态SQL支持,通过
<if>
,<choose>
,<where>
,<set>
等标签,可以根据条件动态生成SQL语句。 - 多种缓存机制:MyBatis支持一级缓存(Session级别)和二级缓存(Mapper级别或全局级别)。
- 插件机制:MyBatis提供了插件机制,可以编写插件来拦截执行的核心方法(如Executor、StatementHandler等)。
优点:
- 灵活性高,适用于复杂查询。
- SQL语句直观明确,便于优化和调试。
缺点:
- 需要大量手动编写SQL和映射配置文件,开发效率相对较低。
- 对简单的CRUD操作来说,配置较为繁琐。
MyBatis-Plus
MyBatis-Plus 是基于MyBatis的增强工具包,主要特点如下:
- 简化CRUD操作:MyBatis-Plus提供了大量预定义的CRUD方法,极大地减少了手动编写SQL的工作量。
- 代码生成器:MyBatis-Plus提供了代码生成器,可以自动生成Mapper、Entity、Service、Controller等代码,提高开发效率。
- 条件构造器:MyBatis-Plus提供了条件构造器(
Wrapper
类),可以通过链式调用方式构建复杂的查询条件。 - 多种插件支持:MyBatis-Plus内置分页插件、性能分析插件、乐观锁插件、SQL注入器等,增强了MyBatis的功能。
- 元数据扫描:MyBatis-Plus可以自动扫描数据库表结构,生成相应的实体类和Mapper接口。
优点:
- 提高开发效率,简化常见的CRUD操作。
- 内置了多种增强功能插件,减少了重复工作。
- 通过条件构造器可以优雅地构建复杂的查询条件。
缺点:
- 灵活性不如手写SQL,对复杂查询可能需要回退到MyBatis的原始方式。
- 与MyBatis相比,学习曲线稍微陡峭一些,需要熟悉MyBatis-Plus的扩展功能和用法。
对比总结
特性 | MyBatis | MyBatis-Plus |
---|---|---|
SQL编写 | 手动编写SQL,灵活性高 | 自动生成CRUD,简化操作 |
动态SQL支持 | <if> , <choose> , <where> 等 | 条件构造器,链式调用 |
映射配置 | XML映射文件或注解 | 自动扫描数据库,代码生成器 |
插件机制 | 提供插件机制,需手动编写 | 内置分页、乐观锁等多种插件 |
缓存支持 | 一级缓存、二级缓存 | 继承MyBatis的缓存机制 |
开发效率 | 需要手动编写,效率较低 | 提高效率,减少重复工作 |
复杂查询 | 灵活,适用于复杂查询 | 灵活性较低,复杂查询需手动编写 |
示例代码对比
MyBatis 示例:
Mapper接口:
public interface UserMapper {
User selectById(Long id);
List<User> selectByName(String name);
void insertUser(User user);
void updateUser(User user);
void deleteUser(Long id);
}
XML映射文件:
<mapper namespace="com.example.mapper.UserMapper">
<select id="selectById" resultType="User">
SELECT * FROM users WHERE id = #{id}
</select>
<select id="selectByName" resultType="User">
SELECT * FROM users WHERE name = #{name}
</select>
<insert id="insertUser">
INSERT INTO users (name, age) VALUES (#{name}, #{age})
</insert>
<update id="updateUser">
UPDATE users SET name = #{name}, age = #{age} WHERE id = #{id}
</update>
<delete id="deleteUser">
DELETE FROM users WHERE id = #{id}
</delete>
</mapper>
注解版
@Select("SELECT * FROM users WHERE id = #{id}")
User selectById(Long id);
@Select("SELECT * FROM users WHERE name = #{name}")
List<User> selectByName(String name);
@Insert("INSERT INTO users (name, age) VALUES (#{name}, #{age})")
@Options(useGeneratedKeys = true, keyProperty = "id")
void insertUser(User user);
@Update("UPDATE users SET name = #{name}, age = #{age} WHERE id = #{id}")
void updateUser(User user);
@Delete("DELETE FROM users WHERE id = #{id}")
void deleteUser(Long id);
MyBatis-Plus 示例:
实体类:
@Data
@TableName("users")
public class User {
private Long id;
private String name;
private Integer age;
}
Mapper接口(继承BaseMapper):
public interface UserMapper extends BaseMapper<User> {
}
Service类:
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public User getById(Long id) {
return userMapper.selectById(id);
}
public List<User> getByName(String name) {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("name", name);
return userMapper.selectList(queryWrapper);
}
public void saveUser(User user) {
userMapper.insert(user);
}
public void updateUser(User user) {
userMapper.updateById(user);
}
public void deleteUser(Long id) {
userMapper.deleteById(id);
}
}
通过上述对比,可以看到MyBatis-Plus简化了很多重复的工作,提供了更加便捷的CRUD操作,同时保留了MyBatis灵活的SQL编写能力。根据具体需求,可以选择使用MyBatis或MyBatis-Plus来进行项目开发。