1.快速入门
1.1简介
- 强大的 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.2依赖配置
依赖( 引入 MyBatis-Plus
之后请不要再次引入 MyBatis
)
<dependency>
<groupId>com.baomidou</groupId>
<!--下面坐标根据自己使用的SpringBoot版本二选一-->
<!--SpringBoot2使用此版本-->
<artifactId>mybatis-plus-boot-starter</artifactId>
<!--3.5.4开始,支持SpringBoot3使用此版本-->
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>最新版本</version>
</dependency>
//这里是h2数据库的导入,使用mysql我们可以在项目配置时勾选
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
配置
spring:
datasource:
username: root
password: 654321
url: jdbc:mysql://localhost:3306/ssmbuild? serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
# url: jdbc:mysql://localhost:3306/ssmbuild? //这个也能使用,特别注意后面这些参数
driver-class-name: com.mysql.cj.jdbc.Driver
server:
port: 9999
logging:
level:
com.porter: debug
mybatis-plus:
type-aliases-package: com.porter.pojo
configuration:
map-underscore-to-camel-case: true
mapper-locations: classpath:/mapper/*.xml
global-config:
db-config:
logic-delete-field: deleted
logic-not-delete-value: 0
logic-delete-value: 1
1.3Basemapper
编写 Mapper 包下的 BookMapper
接口 ,记得要在主程序上添加@mapperscan,这样才能扫描到mapper包
public interface BookMapper extends BaseMapper<Book> {
}
只需要编写pojo还有具体的mapper接口,
只需要在mapper中去继承Basemapper〈Book〉这样之后我们就可以去拿到父类中的crud方法。
pojo类中**@TableName时对应数据库中的表名,@TableField则对应表中属性名**
@Data
@TableName("books")
public class Book {
@TableField("bookId")
private Integer bookId;
// 在数据库表中对应的列名为 book_name
@TableField("bookName")
private String bookName;
// 在数据库表中对应的列名为 book_counts
@TableField("bookCounts")
private Integer bookCounts;
// 在数据库表中对应的列名为 detail
@TableField("detail")
private String detail;
}
测试
@SpringBootTest
public class SampleTest {
@Autowired
BookMapper bookMapper;
@Test
void test1(){
//查询条件无就写null,实际上封装了wrapper
List<Book> books = bookMapper.selectList(null);
books.forEach(System.out::println);
}
}
1.4注解
@TableName: 表名注解,标识实体类对应的表 ;加在实体类上与表对应。
@TableId:主键注解,这里如果没加在主键上,使用关于selectById会报错。
@TableField:value与数据库字段名对应,exist设置是否为数据库表字段,select表是否进行select查询。
@TableLogic: 表字段逻辑处理注解,但是也可以通过在yml中配置 。
@Version:乐观锁注解。
2.扩展功能
2.1主键策略
@TableId(type = IdType.INPUT) INPUT可以替换为AUTO NONE…
这个加在实体类属性上,用来表示唯一主键,还有设置其自增方式或者手动输入id等。
2.2自动填充
1.@TableField(fill = FieldFill.INSERET) 字段添加填充内容
@TableField(fill = FieldFill.INSERT)
private Data creatTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Data updateTime;
2.新建handler包,并且新建MyMetaObjectHandler处理器来处理上面的注解信息(继承后重写对应方法)
@Slf4j
@Component //把处理器加到IOC容器中
public class MyMetaObjectHandler implements MetaObjectHandler {
//插入时的填充策略
@Override
public void insertFill(MetaObject metaObject) {
log.info("Start insert fill.... ");
this.setFieldValByName("createTime",new Date(),metaObject);
this.setFieldValByName("updateTime",new Date(),metaObject);
}
//更新时的填充策略
@Override
public void updateFill(MetaObject metaObject) {
log.info("start update fill ....");
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐)
// 或者
this.strictUpdateFill(metaObject, "updateTime", () -> LocalDateTime.now(), LocalDateTime.class); // 起始版本 3.3.3(推荐)
// 或者
this.fillStrategy(metaObject, "updateTime", LocalDateTime.now()); // 也可以使用(3.3.0 该方法有bug)
}
}
3.测试更新与插入,观察这两个属性的变化
2.3乐观锁插件
实现方式:即每次使用数据时都会带上where version = ?,执行时更新时也会设置version + 1 这样通过观察version,判断是否更新。
2.3.1配置插件
spring:在xml文件中注册组件。
springboot:在配置包下使用注解。
@Bean
public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor() {
return new OptimisticLockerInnerInterceptor();//乐观锁插件
}
2.3.2实体类
在实体类字段上加@Version
@Version
private Integer version;
2.3.3单元测试
@Test
void testUpdate(){
Book book1 = bookMapper.selectById(1);
book1.setBookName("理财呀");
//线程2插队进行,最后结果不会被覆盖,还是理财2
Book book2 = bookMapper.selectById(1);
book2.setBookName("理财2");
bookMapper.updateById(book2);
bookMapper.updateById(book1);
}
2.4分页查询插件
2.4.1配置插件
@Bean
public PaginationInnerInterceptor paginationInnerInterceptor() {
//如果配置多个插件,切记分页最后添加
return new PaginationInnerInterceptor(DbType.MYSQL);
}
2.4.2page对象
// 测试分页查询
@Test
public void testPage(){
// 参数一:当前页
// 参数二:页面大小
// 使用了分页插件之后,所有的分页操作也变得简单的!
Page<User> page = new Page<>(2,5);
userMapper.selectPage(page,null);
}
2.5逻辑删除
含义:在数据库中没有被移除,而是通过一个变量来让他失效! deleted = 0 => deleted = 1
- 删除:
update user set deleted=1 where id = 1 and deleted=0
- 查找:
select id,name,deleted from user where deleted=0
2.5.1yml配置
步骤1
mybatis-plus:
global-config:
db-config:
logic-delete-field: flag #全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
步骤2:实体类字段上加上@TableLogic
注解(可省略)
@TableLogic
private Integer deleted;
2.5.2单元测试
@Test
void testDelteBatchId(){
bookMapper.deleteBatchIds(Arrays.asList(1,2));
}
2.6条件构造器
直接新建wrapper
然后通过wrapper对象去链式编程
例子一
@Test
void testWrapper1() {
QueryWrapper<Book> wrapper = new QueryWrapper<>();
wrapper
.notLike("bookName", "e")
.likeRight("detail", "从");
List<Book> books = bookMapper.selectList(wrapper);
books.forEach(System.out::println);
}
SELECT ** FROM books* WHERE deleted=0 AND (bookName NOT LIKE %e% AND detail LIKE 从%)
例子二
@Test
void testWrapper2() {
QueryWrapper<Book> wrapper = new QueryWrapper<>();
wrapper
.orderByDesc("bookId")
.inSql("bookCounts","select bookCounts from books where bookCounts > 9");
List<Book> books = bookMapper.selectList(wrapper);
books.forEach(System.out::println);
}
SELECT ** FROM books* WHERE deleted=0 AND (bookCounts IN (select bookCounts from books where bookCounts > 9)) ORDER BY bookId DESC
例子三
@Test
void testWrapper3(){
QueryWrapper<Book> wrapper = new QueryWrapper<>();
wrapper
.isNotNull("bookName")
.ge("bookCounts",50);
List<Map<String, Object>> maps = bookMapper.selectMaps(wrapper);
maps.forEach(System.out::println);
}
SELECT * FROM books WHERE deleted=0 AND (bookName IS NOT NULL AND bookCounts >= ?)
例子四
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper
.lt(User::getAge,25)
.likeRight(User::getName,"赵")
.orderByDesc(User::getId);
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
2.7代码生成器
使用代码生成器除了开启mybatisplus之外,还要引入依赖generate及freemarker
还有生成之后对应实体类的@ApiModel,共三个依赖
<!--配置生成器-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.5</version>
</dependency>
<!--与上面配套使用-->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.28</version>
</dependency>
<!--配置ApiModel在实体类中不生效-->
<dependency>
<groupId>com.spring4all</groupId>
<artifactId>spring-boot-starter-swagger</artifactId>
<version>1.5.1.RELEASE</version>
</dependency>
我们不希望代码生成器打包进项目中,所以我们可以放在test目录中,并新建一个CodeGenerate类
每次我们只需要修改对应生成的表即可,即对应tableName;
public class CodeGenerate {
public static void main(String[] args) {
//抽取
String url = "jdbc:mysql://localhost:3306/score_manage?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8";
String username = "root";
String password = "654321";
String author = "porter";
String outputDir = "D:\\211110173\\itheima\\mybatisplus-generate\\src\\main\\java";
String mapperLocation = "D:\\211110173\\itheima\\mybatisplus-generate\\src\\main\\resources\\mapper";
String basePackage = "com.porter";
String moduleName = "system";
String tableName = "teacher";//数据库中对应表名
String tablePrefix = "s_";
FastAutoGenerator.create(url,username,password)
//全局配置
.globalConfig(builder -> {
builder.author(author) // 设置作者
.enableSwagger() // 开启 swagger 模式
// .fileOverride() // 覆盖已生成文件
.outputDir(outputDir); // 指定输出目录
})
//设置数据源
.dataSourceConfig(builder -> builder.typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> {
int typeCode = metaInfo.getJdbcType().TYPE_CODE;
if (typeCode == Types.SMALLINT) {
// 自定义类型转换
return DbColumnType.INTEGER;
}
return typeRegistry.getColumnType(metaInfo);
}))
//包配置
.packageConfig(builder -> {
builder.parent(basePackage) // 设置父包名
.moduleName(moduleName) // 设置父包模块名
.pathInfo(Collections.singletonMap(OutputFile.xml, mapperLocation)); // 设置mapperXml生成路径
})
//策略配置
.strategyConfig(builder -> {
builder.addInclude(tableName);// 设置需要生成的表名
// .addTablePrefix("t_", "c_"); // 设置过滤表前缀
})
.templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
.execute();
}
}