1 mybatis-plus概念
1.1 Mybatis-Plus简介
Mybatis-Plus是一个Mybatis的增强工具,在Mybatis的基础上只做增强不做改变,为简化开发、提高效率而生。Mybatis-Plus提供了通用的mapper和service,可以在不编写任何SQL语句的情况下,快速的实现对单表的CRUD、批量、逻辑删除分页等操作。
1.2 mybatis-plus 特性
- 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
- 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
- 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
- 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
- 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
- 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
- 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
- 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
- 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
- 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
- 内置性能分析插件:可输出 SQL 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
- 内置全局拦截插件:提供全表delete、update操作智能分析阻断,也可自定义拦截规则,预防误操作
1.3 结构
2 mybatis-plus 基础使用
2.1 基本使用配置
在pom文件中引入一下依赖:
<!-- mybatis-plus启动器 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
配置基本的application.yml
spring:
datasource:
type: com.zaxxer.hikari.HikariDataSource # 使用mybatis-plus需要配置该类型
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&serverTimezone=UTC&allowMultiQueries=true
username: root
password: root
# 日志,根据需要配置即可
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 配置数据库表名前缀,这样实体类名就可以跟表名不一致:如表t_user --> 实体类: User
global-config:
db-config:
table-prefix: t_
# 设置统一的主键生成策略
id-type: auto
测试使用
2.1 创建一张user表
CREATE TABLE `user` (
`id` BIGINT(20) NOT NULL COMMENT '主键ID',
`name` VARCHAR(30) DEFAULT NULL COMMENT '姓名',
`age` INT(11) DEFAULT NULL COMMENT '年龄',
`email` VARCHAR(50) DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=UTF8;
2.1.2 创建实体类User以及UserMapper接口
package com.yamei.mybatisplus.pojo;
import lombok.Data;
@Data
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
package com.yamei.mybatisplus.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yamei.mybatisplus.pojo.User;
import org.springframework.stereotype.Repository;
// 使mapper永久有效,不写也没关系,只不过注入该mapper时候,编辑器会显示红色错误,不影响使用
@Repository
public interface UserMapper extends BaseMapper<User> {
}
/**
* BaseMapper 包含了单表操作的各种基础接口,mybatis-plus根据我们传入的
* 实体类去操作对应的数据表
*/
2.1.3 测试调用
package com.yamei.mybatisplus;
import com.yamei.mybatisplus.mapper.UserMapper;
import com.yamei.mybatisplus.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest
public class MybatisPlusTest {
@Autowired
private UserMapper userMapper;
@Test
public void testUser() {
List<User> users = userMapper.selectList(null);
System.out.println(users.toString());
}
@Test
public void testDelById() {
int i = userMapper.deleteById(1735561917386240002L);
System.out.println("-------删除成功--------" + i);
}
}
2.2 通用service
创建一个service接口文件
package com.yamei.mybatisplus.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.yamei.mybatisplus.pojo.User;
// 继承mybatis-plus的通用service: IService
public interface UserService extends IService<User> {
// 自定义接口
}
创建一个实现类文件
package com.yamei.mybatisplus.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yamei.mybatisplus.mapper.UserMapper;
import com.yamei.mybatisplus.pojo.User;
import com.yamei.mybatisplus.service.UserService;
/**
* UserServiceImpl 实现类不需要实现mybatis-plus的所有service接口,
* 我们只需要继承mybatis-plus的基础实现类(ServiceImpl<对应的Mapper, 实体类>)即可
*/
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
// 自定义实现类
}
注意:自定义SQL和接口、service等按照之前的mybatis使用方式即可
2.3 通用注解
2.3.1 @TableName:用来指定表名
package com.yamei.mybatisplus.pojo;
import lombok.Data;
@Data
@TableName("t_user") // 指定该实体类使用的表名, 当实体类名与表名不一致的时候可用
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
2.3.2 @TableId:指定数据表的主键
package com.yamei.mybatisplus.pojo;
import lombok.Data;
@Data
public class User {
// 将属性所对应的字段指定为主键
// 该注解的value属性用于指定主键的字段@TableId(value="uid")==@TableId("uid")
/** @TableId(value="uid", type=IdType.AUTO)
* 该注解的type属性为主键生成类型,默认是IdType。ASSIGN_ID(基于雪花算法生成),
* IdType.AUTO 则是使用数据库id自增策略(这个需要建表时候设置了id自增才可使用),
* 其它类型可自行查看文档或源码
*/
@TableId("uid")
private Long id;
private String name;
private Integer age;
private String email;
}
2.3.3 @TableLogic
a. 逻辑删除
- 物理删除:真实删除,将对应数据从数据库中删除,之后查询不到此条被删除的数据
- 逻辑删除:假删除,将对应数据中代表是否被删除的字段的状态修改为“被删除状态”,之后在数据库中仍旧能看到此条数据记录。
- 使用场景:可以进行数据恢复
b. 实现逻辑删除
step1:数据库中创建逻辑删除状态列,设置默认值为0
package com.yamei.mybatisplus.pojo;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
@Data
public class User {
private Long id;
private String name;
private Integer age;
private String email;
@TableLogic // 逻辑删除
private Integer isDeleted;
}
3 Wrapper 介绍
- Wrapper:条件构造抽象类,最顶端父类
- AbstractWrapper:用于查询条件封装,生成sql的where条件
- QueryWrapper:查询条件封装
- UpdateWrapper:update条件封装
- AbstractLambdaWrapper:使用Lambda语法
- LambdaQueryWrapper:用于Lambda语法使用的Wrapper
- LambdaUpdateWrapper:Lambda更新封装Wrapper
- AbstractWrapper:用于查询条件封装,生成sql的where条件
3.1 QueryWrapper
a.例1:组装查询条件
@Test
public void queryTest01() {
// 查询用户名包含a,年龄在20到30之间,邮箱信息不为null的用户信息
// sql:SELECT id,name,age,email FROM user WHERE (name LIKE ? AND age BETWEEN ? AND ? AND email IS NOT NULL);
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.like("name", "a")
.between("age", 20, 30)
.isNotNull("email");
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
b.例2:组装排序条件
@Test
public void queryTest02() {
// 查询用户信息,按照年龄的降序排序,若年龄相同,则按照id升序排序
// SELECT id,name,age,email FROM user ORDER BY age DESC,id ASC
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.orderByDesc("age").orderByAsc("id");
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
c.例3:组装删除条件
@Test
public void queryTest03() {
// 删除邮箱地址为null的用户信息
// UPDATE user SET is_deleted=1 WHERE is_deleted=0 AND (email IS NULL); User实体类中使用了逻辑删除
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.isNull("email");
int delete = userMapper.delete(queryWrapper);
System.out.println("====delete====" + delete);
}
d.例4:使用QueryWrapper实现修改功能
@Test
public void queryTest04() {
// 将 (年龄大于20并且用户名中包含有a) 或邮箱为null的用户信息修改
// UPDATE user SET name=?, email=? WHERE is_deleted=0 AND (age > ? AND name LIKE ? OR email IS NULL)
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.gt("age", 20)
.like("name", "a")
.or()
.isNull("email");
User user = new User();
user.setName("update");
user.setEmail("test04@qq.com");
int update = userMapper.update(user, queryWrapper);
System.out.println("===update===" + update);
}
e.例5:条件的优先级
@Test
public void queryTest05() {
// 将用户名中包含有a并且(年龄大于25或邮箱为null)的用户信息修改
// UPDATE user SET name=?, email=? WHERE is_deleted=0 AND (name LIKE ? AND (age > ? OR email IS NULL))
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.like("name", "a")
.and(i -> i.gt("age", 25).or().isNull("email"));
User user = new User();
user.setName("小红");
user.setEmail("xiaohong@qq.com");
int update = userMapper.update(user, queryWrapper);
System.out.println("===update===" + update);
}
f.例6:组装select子句
@Test
public void queryTest06() {
// 查询用户的用户名、年龄、邮箱信息
// SELECT name,age,email FROM user WHERE is_deleted=0
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.select("name", "age", "email");
// List<User> users = userMapper.selectList(queryWrapper);
List<Map<String, Object>> users = userMapper.selectMaps(queryWrapper);
users.forEach(System.out::println);
}
g.例7:组装子查询
@Test
public void queryTest07() {
// 使用子查询:查询id小于等于5的用户信息
// SELECT id,name,age,email,is_deleted FROM user WHERE is_deleted=0 AND (id IN (select id from user where id <=5))
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.inSql("id", "select id from user where id <=5");
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
h.例8:使用condition组装条件
@Test
public void queryTest08() {
String name = "a";
Integer ageBegin = null;
Integer ageEnd = 30;
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.like(StringUtils.isNotBlank(name), "name", name)
.ge(ageBegin != null, "age", ageBegin)
.le(ageEnd != null, "age", ageEnd);
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
3.2 UpdateWrapper
a.例1:实现修改功能
@Test
public void updateTest01() {
// 将用户名中包含有a并且(年龄大于25或邮箱为null)的用户信息修改
// UPDATE user SET name=?,email=? WHERE is_deleted=0 AND (name LIKE ? AND (age > ? OR email IS NULL))
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.like("name", "a")
.and(i -> i.gt("age", 25).or().isNull("email"));
updateWrapper.set("name", "小黑").set("email", "xiaohei@qq.com");
int update = userMapper.update(null, updateWrapper);
System.out.println("===update===" + update);
}
3.3 LambdaQueryWrapper
a.例1
@Test
public void lambdaTest01() {
// 查询用户名字包含a并且年龄大于等于ageBegin且小于等于ageEnd的信息
// User::getName 相当于对应的数据表字段name,这种写法可以防止写错字段
// SELECT id,name,age,email,is_deleted FROM user WHERE is_deleted=0 AND (name LIKE ? AND age <= ?)
String name = "a";
Integer ageBegin = null;
Integer ageEnd = 30;
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.like(StringUtils.isNotBlank(name), User::getName, name)
.ge(ageBegin != null, User::getAge, ageBegin)
.le(ageEnd != null, User::getAge, ageEnd);
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
3.4 LambdaUpdateWrapper
a.例子
@Test
public void updateTest02() {
// 将用户名中包含有a并且(年龄大于25或邮箱为null)的用户信息修改
// UPDATE user SET name=?,email=? WHERE is_deleted=0 AND (name LIKE ? AND (age > ? OR email IS NULL))
LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.like(User::getName, "a")
.and(i -> i.gt(User::getAge, 25).or().isNull(User::getEmail));
updateWrapper.set(User::getName, "小黑").set(User::getEmail, "xiaohei@qq.com");
int update = userMapper.update(null, updateWrapper);
System.out.println("===update===" + update);
}
4 插件
4.1 分页插件
mybatis-plus自带分页插件,只要简单的配置即可实现分页功能
a.添加配置类
package com.yamei.mybatisplus.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
//@MapperScan("com.yamei.mybatisplus.mapper") // 可以将主类中的注解移到此处
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
测试例子:
@Test
public void test01() {
// Page 接收两个参数:参数1:当前页码,参数2:每页多少条
// SELECT id,name,age,email,is_deleted FROM user WHERE is_deleted=0 LIMIT ?
Page<User> page = new Page<>(1, 3);
userMapper.selectPage(page, null);
System.out.println(page.toString());
// 总页数
System.out.println(page.getPages());
// 总条数
System.out.println(page.getTotal());
}
自定义接口使用分页功能
a.接口定义
public interface UserMapper extends BaseMapper<User> {
/**
* 通过年龄查询用户信息并分页
* 接口中使用@Param设置别名,需要到配置文件设置一下,否则报错
* @param page mybatis-plus 所提供的分页对象,必须位于第一个参数的位置
* @param age
* @return
*/
Page<User> selectPageVo(@Param("page") Page<User> page, @Param("age") Integer age);
}
b.SQL语句编写
<?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.yamei.mybatisplus.mapper.UserMapper">
<!-- Page<User> selectPageVo(@Param("page") Page<User> page, @Param("age") Integer age); -->
<select id="selectPageVo" resultType="User">
select id,`name`,age,email from user where age > #{age}
</select>
</mapper>
c.测试调用
@Test
public void test02() {
Page<User> page = new Page<>(1, 3);
userMapper.selectPageVo(page, 20);
System.out.println(page);
}
4.2 乐观锁
a.场景
一件商品,成本价是80元,售价是100元。老板先是通知小李,说你去把商品价格增加50元。小李正在玩游戏,耽搁了一个小时。正好一个小时后,老板觉得商品价格增加到150元,价格太高,可能会影响销量。又通知小王,你把商品价格降低30元。
此时,小李和小王同时操作商品后台系统。小李操作的时候,系统先取出商品价格100元;小王也在操作,取出的商品价格也是100元。小李将价格加了50元,并将100+50=150元存入了数据库;小王将商品减了30元,并将100-30=70元存入了数据库。是的,如果没有锁,小李的操作就完全被小王的覆盖了。
现在的商品价格是70元,比成本价低了10元。几分钟后,这个商品很快出售了1千多件商品,老板亏损1万多。
b.乐观锁与悲观锁
上面的场景,如果是乐观锁,小王保存价格前,会检查一下价格是否被人修改过了。如果被修改过了,则重新取出被修改过后的价格,150元,这样他会将120元存入数据库。
如果是悲观锁,小李取出数据后,小王只能等小李操作完之后,才能对价格进行操作,也会保证最终的价格是120元。
c.例子:测试修改冲突
创建一张商品表
create table t_product (
id bigint(20) not null comment '主键Id',
`name` varchar(30) null default null comment '商品名称',
price int(11) default 0 comment '价格',
version int(11) default 0 comment '乐观锁版本号',
primary key (id)
);
实体类
package com.yamei.mybatisplus.pojo;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("t_product")
public class Product {
private Long id;
private String name;
private Integer price;
private Integer version;
}
模拟修改冲突过程:
@Test
public void productTest01() {
// 小李查询商品价格
Product productLi = productMapper.selectById(1);
System.out.println("小李查询商品价格:" + productLi.getPrice());
// 小王查询商品价格
Product productWang = productMapper.selectById(1);
System.out.println("小王查询商品价格:" + productWang.getPrice());
// 小李设置商品价格
productLi.setPrice(productLi.getPrice() + 50);
productMapper.updateById(productLi);
// 小王设置商品价格
productWang.setPrice(productWang.getPrice() - 30);
productMapper.updateById(productWang);
// 老板查询商品价格
Product productBoss = productMapper.selectById(1);
System.out.println("老板查询商品价格:" + productBoss.getPrice());
}
d.乐观锁实现流程
数据库中添加version字段
取出记录时,获取当前version
select id, `name`, price, version from t_product where id = 1
更新时,version + 1,如果where语句中的version版本不对,则更新失败
update t_product set price = price + 50, version = version + 1 where id = 1 and version = 1;
e.Mybatis-Plus实现乐观锁
修改实体类:添加@Version注解
@Data
@TableName("t_product")
public class Product {
private Long id;
private String name;
private Integer price;
@Version // 标识乐观锁版本号字段
private Integer version;
}
修改config文件,添加乐观锁插件
@Configuration
//@MapperScan("com.yamei.mybatisplus.mapper") // 可以将主类中的注解移到此处
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// ...
// 添加乐观锁插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
}
模拟乐观锁测试:
@Test
public void productTest01() {
// 小李查询商品价格
Product productLi = productMapper.selectById(1);
System.out.println("小李查询商品价格:" + productLi.getPrice());
// 小王查询商品价格
Product productWang = productMapper.selectById(1);
System.out.println("小王查询商品价格:" + productWang.getPrice());
// 小李设置商品价格
productLi.setPrice(productLi.getPrice() + 50);
productMapper.updateById(productLi);
// 小王设置商品价格
productWang.setPrice(productWang.getPrice() - 30);
int result = productMapper.updateById(productWang);
if (result == 0) {
// 更新失败则重试
Product productNew = productMapper.selectById(1);
productNew.setPrice(productNew.getPrice() - 30);
productMapper.updateById(productNew);
}
// 老板查询商品价格
Product productBoss = productMapper.selectById(1);
System.out.println("老板查询商品价格:" + productBoss.getPrice());
}
5 通用枚举
表中的有些字段值是固定的,例如性别(男或女),此时我们可以使用Mybatis-plus的通用枚举来实现
a.数据库表添加字段sex
b.新建枚举类以及修改实体类
package com.yamei.mybatisplus.enums;
import com.baomidou.mybatisplus.annotation.EnumValue;
import lombok.Getter;
@Getter // 该枚举类是常量,只设置getter
public enum SexEnum {
MALE(1, "男"),
FEMALE(2, "女");
@EnumValue // 将注解所标识的属性的值存储到数据库
private Integer sex;
private String sexName;
SexEnum(Integer sex, String sexName) {
this.sex = sex;
this.sexName = sexName;
}
}
package com.yamei.mybatisplus.pojo;
import com.baomidou.mybatisplus.annotation.*;
import com.yamei.mybatisplus.enums.SexEnum;
import lombok.Data;
@Data
public class User {
private String name;
private Integer age;
private String email;
// 添加sex 类型为枚举类
private SexEnum sex;
@TableLogic
private Integer isDeleted;
}
c.修改application.yml配置文件
添加以下配置
mybatis-plus:
# ...
# 扫描通用枚举的包
type-enums-package: com.yamei.mybatisplus.enums
d.测试
@Test
public void testEnum01() {
// INSERT INTO user ( id, name, age, email, sex ) VALUES ( ?, ?, ?, ?, ? )
User user = new User();
user.setName("enum1");
user.setAge(22);
user.setEmail("123456@qq.com");
user.setSex(SexEnum.MALE);
userMapper.insert(user);
}
6 代码生成器
6.1 引入依赖
<!-- 引入代码生成器 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.31</version>
</dependency>
6.2 快速生成代码
package com.yamei.mybatisplus;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.rules.DbColumnType;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import java.sql.Types;
import java.util.Collections;
public class FastAutoGeneratorTest {
public static void main(String[] args) {
FastAutoGenerator.create("jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&serverTimezone=UTC&allowMultiQueries=true", "root", "root")
.globalConfig(builder -> {
builder.author("yamei") // 设置作者
// .enableSwagger() // 开启 swagger 模式
.fileOverride() // 覆盖已生成文件
.outputDir("D://mybatis_plus"); // 指定输出目录
})
.packageConfig(builder -> {
builder.parent("com.yamei") // 设置父包名
.moduleName("mybatisplus") // 设置父包模块名 // parent和moduleName 意思是生成的代码都在com.yamei.mybatisplus 目录下
.pathInfo(Collections.singletonMap(OutputFile.mapperXml, "D://mybatis_plus")); // 设置mapperXml 映射文件生成路径
})
.strategyConfig(builder -> {
builder.addInclude("t_menu") // 设置需要生成的表名
.addTablePrefix("t_", "c_"); // 设置过滤表前缀
})
.templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
.execute();
}
}
生成结果:具体内容自己生成查看
7 多数据源
适用于多种场景:纯粹多库、读写分离、一主多从、混合模式等
目前我们就模拟一个纯粹多库的一个场景,其他场景类似
场景说明:
我们创建两个库,分别为:mybatis_plus与mybatis_plus_1,将mybatis_plus库的product表移动到mybatis_plus_1库,这样每个库一张表,通过一个测试用例分别获取用户数据与商品数据,如果获取到说明多库模拟成功。
7.1 创建数据库以及表
创建数据库mybatis_plus_1以及product表
create database `mybatis_plus_1`;
USE mybatis_plus_1;
CREATE TABLE product (
id BIGINT(20) NOT NULL COMMENT '主键ID',
`name` VARCHAR(30) NULL DEFAULT NULL COMMENT '商品名称',
price INT(11) DEFAULT 0 COMMENT '价格',
`version` INT(11) DEFAULT 0 COMMENT '乐观锁版本号',
PRIMARY KEY (id)
);
insert into product (id, `name`, price) values(1, '外星人笔记本', 100);
删除mybatis_plus库的t_product 表
use mybatis_plus;
drop table if exists t_product;
7.2 引入依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.5.0</version>
</dependency>
7.3 配置多数据源
说明:注释掉之前的数据库连接,添加新配置
spring:
datasource:
dynamic:
# 设置默认的数据源或者数据源组,默认值即为master
primary: master
# 严格匹配数据源,默认false,true未匹配到指定数据源时抛异常,false则使用默认数据源
strict: false
datasource:
master:
url: jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&serverTimezone=UTC&allowMultiQueries=true
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
slave_1:
url: jdbc:mysql://localhost:3306/mybatis_plus_1?characterEncoding=utf-8&serverTimezone=UTC&allowMultiQueries=true
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 配置数据库表名前缀,这样实体类名就可以跟表名不一致:如表t_user --> 实体类: User
#global-config:
#db-config:
#table-prefix: t_
type-aliases-package: com.yamei.mybatisplus.pojo
# 扫描通用枚举的包
type-enums-package: com.yamei.mybatisplus.enums
7.4 创建用户service
public interface UserService extends Iservice<User> {
}
@DS("master") // 指定所操作的数据源
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
7.5 创建商品service
public interface ProductService extends IService<Product> {
}
@DS("slave_1")
@Service
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements ProductService {
}
7.6 测试
@Test
public void test01() {
System.out.println(userService.getById(1));
System.out.println("===================================");
System.out.println(productService.getById(1));
}
8 MyBatisX 插件
Mybatis-Plus为我们提供了强大的mapper和service模板,能够大大的提高开发效率。但是在真正的开发过程中,Mybatis-Plus并不能为我们解决所有问题,例如一些复杂的SQL,多表联查等,我们该如何快速解决这个问题呢?这个时候可以使用MyBatisX插件。
MyBatisX是一款基于IDEA的快速开发插件,为效率而生。
MyBatisX插件的具体用法:https://baomidou.com/pages/ba5b24/
下面我们介绍一下MyBatisX的简单使用
8.1 安装MyBatisX插件
如果你的IDEA编辑工具还没有安装MyBatisX插件,那么请打开file->settings,找到plugins,搜索并安装该插件,然后重启编辑器即可。(注意:该插件目前兼容idea2019.3以上版本)
8.2 使用MyBatisX快速生成代码
- 步骤1:在idea上连接项目使用的数据库
- 步骤2:选择相关表,右键选择
这样我们就生成了基本的代码了。
8.3 使用示例
我们进入UserMapper.java文件
输入你的方法,mybatisx会根据你输入的显示相关方法(前提是mybatisx有)
然后选择方法,alt + enter,如下图:选中第一个,mybatisx会帮你补全接口并在对应的mapper.xml文件中生成SQL语句
这就是一个简单的使用例子,具体的可以去参考官网。
有什么不合理的地方请指出,谢谢!!