马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
一、弁言
MyBatis-Plus 是一个在 MyBatis 基础上进行增强的工具,它在保留 MyBatis 原有功能的同时,极大地简化了开辟流程,进步了开辟效率。本教学文档旨在帮助开辟者全面把握 MyBatis-Plus,从基础入门到醒目其焦点、扩展及插件功能,让开辟者能够熟练运用它进行高效的数据库操纵开辟。
二、快速入门
2.1 入门案例
2.1.1 需求
基于给定项目,实现新增用户、根据 id 查询用户、根据 id 批量查询用户、根据 id 更新用户、根据 id 删除用户的功能。
2.1.2 步骤
- 引入 MyBatis-Plus 的起步依赖:利用 MyBatis-Plus 官方提供的mybatis-plus-boot-starter,取代 MyBatis 的 starter。在pom.xml文件中添加如下依赖:
- <dependency>
- <groupId>com.baomidou</groupId>
- <artifactId>mybatis-plus-boot-starter</artifactId>
- <version>3.5.3.1</version>
- </dependency>
复制代码
- 界说 Mapper:自界说的 Mapper 继续 MyBatis-Plus 提供的BaseMapper接口。例如:
- public interface UserMapper extends BaseMapper<User> {
- }
复制代码 BaseMapper接口提供了丰富的 CRUD 方法,如insert(新增)、updateById(根据 id 更新)、selectById(根据 id 查询)、deleteById(根据 id 删除)等,涵盖了基本的数据库操纵。
2.2 常见注解
- 实体类与数据库表的映射规则:MyBatis-Plus 默认以类名驼峰转下划线作为表名,把名为id的字段作为主键,变量名驼峰转下划线作为表的字段名。
- 常用注解先容
- @TableName:用于指定表名。若实体类名与数据库表名不一致,可通过此注解进行指定,如@TableName("tb_user")。
- @TableId:指定表中的主键字段信息。可设置value指定主键字段名,type指定主键天生策略。例如@TableId(value = "id", type = IdType.AUTO),表示主键字段为id,接纳数据库自增长策略。IdType枚举类型常见值有:
- AUTO:数据库自增长。
- INPUT:通过set方法自行输入。
- ASSIGN_ID:分配 ID,利用接口IdentifierGenerator的方法nextId来天生 id,默认实现类为DefaultIdentifierGenerator(雪花算法) 。
- @TableField:指定表中的平凡字段信息。在成员变量名与数据库字段名不一致、成员变量名以is开头且为布尔值、成员变量名与数据库关键字冲突、成员变量不是数据库字段等场景下利用。例如@TableField("username")用于指定命据库字段名为username。
2.3 常见配置
MyBatis-Plus 的配置项继续了 MyBatis 原生配置并增加了一些特有配置。在application.yml文件中常见配置如下:
- mybatis-plus:
- type-aliases-package: com.itheima.mp.domain.po # 别名扫描包
- mapper-locations: "classpath*:/mapper/**/*.xml" # Mapper.xml文件地址,默认值
- configuration:
- map-underscore-to-camel-case: true # 是否开启下划线和驼峰的映射
- cache-enabled: false # 是否开启二级缓存
- global-config:
- db-config:
- id-type: assign_id # id为雪花算法生成
- update-strategy: not_null # 更新策略:只更新非空字段
复制代码
- type-aliases-package:指定实体类所在包,方便在配置文件中利用别名。
- mapper-locations:指定Mapper.xml文件位置,若利用注解开辟,此配置可省略。
- configuration:包罗一些 MyBatis 的焦点配置,如开启下划线和驼峰的映射可方便实体类与数据库字段的转换。
- global-config.db-config:用于配置全局的数据库相关属性,如id-type指定 id 天生策略,update-strategy指定更新策略。
2.4 总结
MyBatis-Plus 利用的基本流程:
- 引入起步依赖mybatis-plus-boot-starter。
- 自界说 Mapper 继续BaseMapper接口。
- 在实体类上添加注解声明表信息。
- 在application.yml中根据须要添加配置。
三、焦点功能
3.1 条件构造器
3.1.1 功能先容
MyBatis-Plus 支持各种复杂的where条件,QueryWrapper和LambdaQueryWrapper通常用来构建select、delete、update的where条件部分;UpdateWrapper和LambdaUpdateWrapper通常在set语句比较特殊时利用。尽量利用LambdaQueryWrapper和LambdaUpdateWrapper,避免硬编码。
3.1.2 案例
- 基于 QueryWrapper 的查询:查询名字中带o,存款大于便是 1000 元的人的id、username、info、balance字段。
- QueryWrapper<User> wrapper = new QueryWrapper<>();
- wrapper.like("username", "o").ge("balance", 1000);
- List<User> userList = userMapper.selectList(wrapper);
复制代码
- 基于 UpdateWrapper 的更新:更新id为 1,2,4 的用户的余额,扣 200。
- UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
- updateWrapper.setSql("balance = balance - 200").in("id", 1, 2, 4);
- userMapper.update(null, updateWrapper);
复制代码 3.2 自界说 SQL
3.2.1 实现方式
- 基于Wrapper构建where
- 在mapper方法参数中用@Param注解声明wrapper变量名称,必须是ew。
- 自界说 SQL,并利用Wrapper条件。
3.2.2 案例
- 自界说更新 SQL:将id在指定范围的用户(例如 1、2、4 )的余额扣减指定值。
在mapper接口中界说方法:
- void updateBalanceByIds(@Param("ew") LambdaQueryWrapper<User> wrapper, @Param("amount") int amount);
复制代码 在Mapper.xml文件中编写 SQL:
- <update id="updateBalanceByIds">
- UPDATE tb_user SET balance = balance - #{amount} ${ew.customSqlSegment}
- </update>
复制代码 在测试方法中调用:
- List<Long> ids = List.of(1L, 2L, 4L);
- int amount = 200;
- LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>().in(User::getId, ids);
- userMapper.updateBalanceByIds(wrapper, amount);
复制代码
- 基于 Wrapper 的多表关联查询:查询出所有收货地点在北京的并且用户id在指定范围的用户。
在mapper接口中界说方法:
- List<User> queryUserByIdAndAddr(@Param("ids") List<Long> ids, @Param("city") String city);
复制代码 在Mapper.xml文件中编写 SQL:
- <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>
复制代码 3.3 Service 接口
3.3.1 利用流程
- 自界说 Service 接口继续IService接口。
- 自界说 Service 实现类,实现自界说接口并继续ServiceImpl类。
- public interface IUserService extends IService<User> {
- // 自定义方法声明
- List<User> listByName(String name);
- }
- public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
- @Override
- public List<User> listByName(String name) {
- // 具体实现逻辑
- QueryWrapper<User> wrapper = new QueryWrapper<>();
- wrapper.like("username", name);
- return list(wrapper);
- }
- }
复制代码 3.3.2 案例
- 基于 Restful 风格实现接口:实现新增用户、删除用户、根据 id 查询用户、根据 id 批量查询、根据 id 扣减余额等接口。
- @RestController
- @RequestMapping("/users")
- public class UserController {
- @Autowired
- private IUserService userService;
- @PostMapping
- public void addUser(@RequestBody User user) {
- userService.save(user);
- }
- @DeleteMapping("/{id}")
- public void deleteUser(@PathVariable Long id) {
- userService.removeById(id);
- }
- @GetMapping("/{id}")
- public User getUserById(@PathVariable Long id) {
- return userService.getById(id);
- }
- @GetMapping
- public List<User> listUsersByIds(@RequestParam List<Long> ids) {
- return userService.listByIds(ids);
- }
- @PutMapping("/{id}/deduction/{money}")
- public void deductionBalance(@PathVariable Long id, @PathVariable Integer money) {
- User user = userService.getById(id);
- user.setBalance(user.getBalance() - money);
- userService.updateById(user);
- }
- }
复制代码
- IService 的 Lambda 查询:实现根据复杂条件查询用户的接口,查询条件包罗用户名关键字、用户状态、最小余额、最大余额。
- @Override
- public List<User> queryUsers(String name, Integer status, Integer minBalance, Integer maxBalance) {
- LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
- if (name != null) {
- wrapper.like(User::getUsername, name);
- }
- if (status != null) {
- wrapper.eq(User::getStatus, status);
- }
- if (minBalance != null && maxBalance != null) {
- wrapper.between(User::getBalance, minBalance, maxBalance);
- }
- return list(wrapper);
- }
复制代码
- IService 的 Lambda 更新:改造根据 id 修改用户余额的接口,完成用户状态校验、余额校验,若扣减后余额为 0,则将用户status修改为冻结状态(2)。
- @Override
- public void updateBalanceById(Long id, Integer money) {
- User user = getById(id);
- if (user == null) {
- throw new RuntimeException("用户不存在");
- }
- if (user.getStatus() == 2) {
- throw new RuntimeException("用户已冻结,无法操作");
- }
- if (user.getBalance() < money) {
- throw new RuntimeException("余额不足");
- }
- user.setBalance(user.getBalance() - money);
- if (user.getBalance() == 0) {
- user.setStatus(2);
- }
- updateById(user);
- }
复制代码
- IService 批量新增:批量插入 10 万条用户数据,并对比平凡 for 循环插入、IService 的批量插入、开启rewriteBatchedStatements=true参数的性能。
- @Test
- void testBatchInsert() {
- List<User> userList = new ArrayList<>();
- for (int i = 0; i < 100000; i++) {
- User user = new User();
- user.setUsername("user" + i);
- user.setPassword("123456");
- userList.add(user);
- }
- long startTime = System.currentTimeMillis();
- // 普通for循环插入
- for (User user : userList) {
- userService.save(user);
- }
- long endTime = System.currentTimeMillis();
- System.out.println("普通for循环插入耗时:" + (endTime - startTime) + "ms");
- startTime = System.currentTimeMillis();
- // IService的批量插入
- userService.saveBatch(userList);
- endTime = System.currentTimeMillis();
- System.out.println("IService的批量插入耗时:" + (endTime - startTime) + "ms");
- // 开启rewriteBatchedStatements=true参数
- // 在jdbc url中添加rewriteBatchedStatements=true
- startTime = System.currentTimeMillis();
- userService.saveBatch(userList);
- endTime = System.currentTimeMillis();
- System.out.println("开启rewriteBatchedStatements=true参数后批量插入耗时:" + (endTime - startTime) + "ms");
- }
复制代码 四、扩展功能
4.1 代码天生
4.1.1 插件安装
在IntelliJ IDEA的Marketplace中搜索并安装MyBatisPlus插件,安装完成后重启IDEA。
4.1.2 代码天生配置
- 打开MyBatis-Plus代码天生器,配置数据库毗连信息,如dbUrl、jdbcDriver、user、password,并测试毗连。
- 选择须要天生代码的表,填写代码天生的父级包路径、Id策略、作者等信息。
- 选择天生的文件类型,如Mapper、Controller、Service、ServiceImpl、实体类等,并指定对应的包路径。
- 配置完成后点击提交,天生对应的代码文件。
4.2 静态工具
Db类提供了一系列静态方法,用于简化数据库操纵,如save(保存)、removeById(根据 id 删除)、listByIds(根据 id 列表查询)等。例如,改造根据 id 查询用户的接口,查询用户的同时查询出用户对应的所有地点:
- @Override
- public User getUserWithAddressesById(Long id) {
- User user = Db.getById(User.class, id);
- List<Address> addresses = Db.listByMap(Address.class, Collections.singletonMap("user_id", id));
- user.setAddresses(addresses);
- return user;
- }
复制代码 4.3 逻辑删除
4.3.1 实现原理
逻辑删除是基于代码逻辑模拟删除效果,在表中添加一个字段(如deleted)标志数据是否被删除。当删除数据时把标志置为 1,查询时只查询标志为 0 的数据。
4.3.2 配置方法
在application.yaml文件中配置逻辑删除的字段名称和值:
- mybatis-plus:
- global-config:
- db-config:
- logic-delete-field: flag # 全局逻辑删除的实体字段名,字段类型可以是boolean、integer
- logic-delete-value: 1 # 逻辑已删除值(默认为1)
- logic-not-delete-value: 0 # 逻辑未删除值(默认为0)
复制代码 在实体类中添加对应的字段:
- public class User {
- private Long id;
- // 其他字段
- @TableField("flag")
- private Integer deleted;
- }
复制代码 MyBatis-Plus 会自动修改 CRUD 语句,实现逻辑删除功能。但逻辑删除会导致数据库表垃圾数据增多,影响查询效率,SQL 中都须要对逻辑删除字段做判断,因此在某些场景下可考虑将数据迁移到其它表。
4.4 枚举处置处罚器
4.4.1 实现 PO 类中的枚举类型变量与数据库字段的转换
- 界说枚举类,给枚举中的与数据库对应value值添加@EnumValue注解。例如:
- @Getter
- public enum UserStatus {
- NORMAL(1, "正常"),
- FREEZE(2, "冻结");
- @EnumValue
- private final int value;
- private final String desc;
- UserStatus(int value, String desc) {
- this.value = value;
- this.desc = desc;
- }
- }
复制代码
- 在配置文件中配置同一的枚举处置处罚器,实现类型转换:
- mybatis-plus:
- configuration:
- default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
复制代码 4.5 JSON 处置处罚器
4.5.1 处置处罚数据库中的 JSON 类型字段
当数据库表中有 JSON 类型字段时,在实体类中利用@TableField(typeHandler = JacksonTypeHandler.class)注解进行处置处罚。例如:
- @Data
- public class User {
- private Long id;
- private String username;
- @TableField(typeHandler = JacksonTypeHandler.class)
- private UserInfo info;
- }
- @Data
- public class UserInfo {
- private Integer age;
- private String intro;
- private String gender;
- }
复制代码 这样在进行数据库操纵时,MyBatis-Plus 会自动处置处罚 JSON 字段的序列化和反序列化。
4.6 配置加密
4.6.1 利用 AES 算法加密配置中的敏感信息
- @Test
- void contextLoads() {
- // 1.生成16位随机AES密钥
- String randomKey = AES.generateRandomKey();
- System.out.println("randomKey = " + random
复制代码 4.6 配置加密
4.6.1 利用 AES 算法加密配置中的敏感信息
- @Test
- void contextLoads() {
- // 1.生成16位随机AES密钥
- String randomKey = AES.generateRandomKey();
- System.out.println("randomKey = " + random
- Key); // 2.利用密钥对用户名加密 String username = AES.encrypt("root", randomKey); System.out.println("username = " + username); // 3.利用密钥对暗码加密 String password = AES.encrypt("MySQL123", randomKey); System.out.println("password = " + password);}
复制代码
- 在application.yaml文件中利用加密后的信息取代明文的用户名和暗码:
- spring:
- datasource:
- url: jdbc:mysql://127.0.0.1:3306/mp
- driver-class-name: com.mysql.cj.jdbc.Driver
- username: mpw:QWWVnk1Oal3258x5rVhaeQ==
- password: mpw:EUFmeH3cNAzdRGdOQcabWg==
复制代码
- 在项目启动时添加 AES 密钥,使 MyBatis-Plus 能够解密数据:
- Jar 包启动时,在启动命令中添加--mpw.key=d1104d7c3b616f0b 。
- 在单位测试中,利用@SpringBootTest(args = "--mpw.key=6234633a66fb399f")指定密钥。
- 在 Idea 中,通过Edit Configuration,在Program arguments中配置--mpw.key=6234633a66fb399f。
五、插件功能
5.1 插件概述
MyBatis-Plus 基于 MyBatis 的 Interceptor 实现了MybatisPlusInterceptor基础拦截器,内部保存了多个内置拦截器集合,这些拦截器可以在 SQL 实行的差别阶段进行干预,实现各种增强功能。
5.2 内置拦截器先容
序号拦截器描述1TenantLineInnerInterceptor多租户插件,用于实现多租户功能,在 SQL 中自动添加租户相关的过滤条件2DynamicTableNameInnerInterceptor动态表名插件,可根据业务需求动态切换表名3PaginationInnerInterceptor分页插件,实现对查询结果的分页功能4OptimisticLockerInnerInterceptor乐观锁插件,用于办理多用户并发操纵时的数据冲突题目5IllegalSQLInnerInterceptorSQL 性能规范插件,检测并拦截不符合性能规范的垃圾 SQL6BlockAttackInnerInterceptor防止全表更新和删除的插件,避免误操纵导致数据大量丢失 5.3 分页插件详细利用
5.3.1 配置分页插件
在配置类中注册 MyBatis-Plus 的焦点插件,并添加分页插件:
- @Configuration
- public class MybatisConfig {
- @Bean
- public MybatisPlusInterceptor mybatisPlusInterceptor() {
- // 1.初始化核心插件
- MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
- // 2.添加分页插件
- PaginationInnerInterceptor pageInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
- pageInterceptor.setMaxLimit(1000L); // 设置分页上限
- interceptor.addInnerInterceptor(pageInterceptor);
- return interceptor;
- }
- }
复制代码 5.3.2 利用分页 API
在 Service 接口中,IService提供了分页查询的方法,如page(E page, Wrapper<T> queryWrapper)和page(E page)。在测试方法中利用分页 API 进行查询:
- @Test
- void testPageQuery() {
- // 1.查询
- int pageNo = 1, pageSize = 5;
- // 1.1.分页参数
- Page<User> page = Page.of(pageNo, pageSize);
- // 1.2.排序参数,通过OrderItem来指定
- page.addOrder(new OrderItem("balance", false));
- // 1.3.分页查询
- Page<User> p = userService.page(page);
- // 2.总条数
- System.out.println("total = " + p.getTotal());
- // 3.总页数
- System.out.println("pages = " + p.getPages());
- // 4.分页数据
- List<User> records = p.getRecords();
- records.forEach(System.out::println);
- }
复制代码 5.3.3 分页查询案例
实现一个UserController接口,遵循特定接口规范实现 User 的分页查询。接口规范如下:
参数阐明哀求方式GET哀求路径/users/page哀求参数pageNo(页码,默认 1)、pageSize(每页数量,默认 5)、sortBy(排序字段,默认updateTime)、isAsc(是否升序,默认 true)、name(用户名关键字,可选)、status(用户状态,可选)返回值包罗总条数total、总页数pages、数据列表list的 JSON 对象特殊阐明如果排序字段为空,默认按照更新时间排序;排序字段不为空,则按照排序字段排序- @RestController
- @RequestMapping("/users")
- public class UserController {
- @Autowired
- private IUserService userService;
- @GetMapping("/page")
- public PageDTO<User> pageQuery(
- @RequestParam(defaultValue = "1") int pageNo,
- @RequestParam(defaultValue = "5") int pageSize,
- @RequestParam(required = false) String sortBy,
- @RequestParam(defaultValue = "true") boolean isAsc,
- @RequestParam(required = false) String name,
- @RequestParam(required = false) Integer status) {
- Page<User> page = new Page<>(pageNo, pageSize);
- LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
- if (name != null) {
- wrapper.like(User::getUsername, name);
- }
- if (status != null) {
- wrapper.eq(User::getStatus, status);
- }
- if (sortBy != null) {
- if (isAsc) {
- wrapper.orderByAsc(sortBy);
- } else {
- wrapper.orderByDesc(sortBy);
- }
- } else {
- wrapper.orderByDesc(User::getUpdateTime);
- }
- Page<User> resultPage = userService.page(page, wrapper);
- return PageDTO.of(resultPage);
- }
- }
复制代码 同时,须要在PageQuery和PageDTO中界说方法,实现PageQuery对象与 MyBatis-Plus 中的Page对象转换,以及Page结果到PageDTO结果的转换:
- public class PageQuery {
- public static Page of(PageQuery pageQuery) {
- return new Page<>(pageQuery.getPageNo(), pageQuery.getPageSize());
- }
- }
- public class PageDTO<T> {
- private long total;
- private int pages;
- private List<T> list;
- public static <T> PageDTO<T> of(Page<T> page) {
- PageDTO<T> pageDTO = new PageDTO<>();
- pageDTO.total = page.getTotal();
- pageDTO.pages = page.getPages();
- pageDTO.list = page.getRecords();
- return pageDTO;
- }
- }
复制代码 六、总结
MyBatis-Plus 在 MyBatis 的基础上提供了丰富的功能和便捷的开辟体验。通过本教程,开辟者学习了从快速入门到焦点功能、扩展功能以及插件功能的利用。把握这些知识后,开辟者能够在项目中机动运用 MyBatis-Plus,高效地完成数据库操纵相关的开辟任务,进步项目开辟效率和质量。在实际应用中,可根据项目需求合理选择和配置 MyBatis-Plus 的各项功能,不绝优化代码和性能。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |