d41:MyBatisPlus入门,注解,配置,条件构造器,自定义SQL,IService
MyBatisPlus入门与实践指南
导航
- 学习内容:MyBatisPlus入门,注解,配置,条件构造器,自定义SQL,IService
1. MyBatisPlus入门
MyBatisPlus(MP)是MyBatis的增强工具,提供了更便捷的CRUD操作。以下是入门步骤:
引入依赖
首先,在项目中引入MyBatisPlus的依赖:
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.1</version>
</dependency>
如果项目中已经使用了MyBatis,可以移除原有的MyBatis依赖。
继承MP的Mapper接口
创建一个Mapper接口,继承BaseMapper
:
public interface UserMapper extends BaseMapper<User> {
}
这样就可以直接使用MP提供的CRUD方法,无需手动编写SQL语句。
示例代码
@Test
void testSelectById() {User user = userMapper.queryUserById(5L);System.out.println("user = " + user);
}
注意:如果定义了与BaseMapper
方法同名的方法,BaseMapper
的方法将被优先使用。
2. 常用注解和配置
MyBatisPlus通过实体类的反射来映射数据库表信息,支持多种注解和配置。
实体类注解
- 指定表名:
@TableName("tb_user") public class User { }
- 指定主键及其策略:
@TableId(value = "id", type = IdType.AUTO) private Long id;
IdType.AUTO
:自增IDIdType.INPUT
:手动输入IdType.ASSIGN_ID
:雪花算法(随机),也是默认策略
- 指定字段名:
@TableField("username") private String username;
- 布尔值字段,
is
前缀会被自动去除:@TableField("is_right") private Boolean isRight;
- 避免与数据库的关键字段冲突:
@TableField("`other`") private Integer other;
- 布尔值字段,
全局配置
MyBatisPlus支持MyBatis的原生配置,并提供了全局策略配置:
mybatis-plus:mapper-locations: classpath*:mapper/**/*.xmltype-aliases-package: com.itheima.mp.domain.poconfiguration:map-underscore-to-camel-case: true # 开启驼峰命名cache-enabled: false # 禁用二级缓存global-config:db-config:id-type: auto # 全局主键策略update-strategy: not_null # 默认更新策略
3. 条件构造器和自定义SQL
MyBatisPlus提供了强大的条件构造器,用于动态构造SQL语句。
QueryWrapper
@Test
void testQueryWrapper() {QueryWrapper<User> queryWrapper = new QueryWrapper<User>().select("id", "username", "phone", "info", "balance").like("username", "o").ge("balance", 1000);List<User> users = userMapper.selectList(queryWrapper);users.forEach(System.out::println);
}
LambdaWrapper
通过反射获取字段名称,更加类型安全:
@Test
void testLambdaWrapper() {LambdaQueryWrapper<User> queryWrapper = new QueryWrapper<User>().lambda().select(User::getId, User::getUsername, User::getPhone, User::getInfo, User::getBalance).like(User::getUsername, "o").ge(User::getBalance, 1000);List<User> users = userMapper.selectList(queryWrapper);users.forEach(System.out::println);
}
自定义SQL
可以直接编写SQL语句:
@Test
void testUpdateWrapper() {List<Long> ids = List.of(1L, 2L, 4L);UpdateWrapper<User> updateWrapper = new UpdateWrapper<User>().setSql("balance = balance - 100").in("id", ids);userMapper.update(null, updateWrapper);
}
条件比较函数
gt
:大于(>
)ge
:大于等于(>=
)lt
:小于(<
)le
:小于等于(<=
)eq
:等于(=
)ne
:不等于(!=
或<>
)
条件构造器与Mapper混合使用
@Test
void testCustomSql() {List<Long> ids = List.of(1L, 2L, 4L);int amount = 200;LambdaQueryWrapper<User> queryWrapper = new QueryWrapper<User>().lambda().in(User::getId, ids);userMapper.updateBalance(queryWrapper, amount);
}
注意:构造器固定以"ew"
为参数值:
void updateBalance(@Param("ew") LambdaQueryWrapper<User> queryWrapper, int amount);
在Mapper XML中:
<update id="updateBalance">update user set balance = balance + #{amount}${ew.customSqlSegment}
</update>
4. IService
通过IService
进一步简化操作。
继承IService
在接口中继承IService
:
public interface IUserService extends IService<User> {
}
实现类
实现类继承ServiceImpl
:
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
}
使用IService方法
可以直接使用IService
的内置方法:
@ApiOperation("新增用户")
@PostMapping
public void saveUser(@RequestBody UserFormDTO userFormDTO) {User user = BeanUtil.copyProperties(userFormDTO, User.class);userService.save(user);
}
业务处理
在ServiceImpl
中,可以直接使用IService
和Mapper
层的方法:
public void deductBalance(Long id, Integer money) {User user = this.getById(id);baseMapper.deductBalance(id, money);
}
也可以使用Lambda构造业务逻辑:
@Override
@Transactional
public void deductBalance(Long id, Integer money) {User user = this.getById(id);if (user == null || user.getStatus() == 2) {throw new RuntimeException("用户不存在或已冻结");}if (user.getBalance() < money) {throw new RuntimeException("余额不足");}int balance = user.getBalance() - money;lambdaUpdate().set(User::getBalance, balance).set(balance < 0, User::getStatus, 2).eq(User::getId, id).eq(User::getBalance, user.getBalance()) // 乐观锁.update();
}
Lambda查询
@Override
public List<User> queryUsers(UserQuery userQuery) {String name = userQuery.getName();Integer status = userQuery.getStatus();Integer minBalance = userQuery.getMinBalance();Integer maxBalance = userQuery.getMaxBalance();return lambdaQuery().like(name != null, User::getUsername, name).eq(status != null, User::getStatus, status).ge(minBalance != null, User::getBalance, minBalance).le(maxBalance != null, User::getBalance, maxBalance).list();
}
5. 批量操作
批量新增
采取分组发送的方式,避免通信传输和内存占用过大:
@Test
void testSaveBatch() {List<User> list = new ArrayList<>(1000);long b = System.currentTimeMillis();for (int i = 1; i < 100000; i++) {list.add(buildUser(i));if (i % 1000 == 0) {userService.saveBatch(list);list.clear();}}long e = System.currentTimeMillis();System.out.println("耗时:" + (e - b));
}
注意:尽管开启了批量保存,但MySQL默认会逐条插入。需要额外开启rewriteBatchedStatements=true
。
6. 小技巧
Lombok注解
使用Lombok的@RequiredArgsConstructor
注解可以自动生成构造器,简化依赖注入:
@RequiredArgsConstructor
public class UserController {private final IUserService userService;
}