一、分页
1、分页插件
分页的本质就是需要设置一个拦截器,通过拦截器拦截了 SQL,通过在 SQL 语句的结尾添加 limit 关键字,来实现分页的效果。
2、通过配置类来指定一个具体数据库的分页插件
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
3、分页查询的实现
@Test
void selectPage(){
//1.创建 QueryWrapper 对象
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
//2.创建分页查询对象,指定当前页和每页显示条数
IPage<User> page = new Page<>(1,2;
//3.执行分页查询
userMapper.selectPage(page, lambdaQueryWrapper);
//4.查看分页查询的结果
System.out.println("当前页码值:"+page.getCurrent());
System.out.println("每页显示数:"+page.getSize());
System.out.println("总页数:"+page.getPages());
System.out.println("总条数:"+page.getTotal());
System.out.println("当前页数据:"+page.getRecords());
}
二、自定义分页
1、在 UserMapper.xml 映射配置文件中提供查询语句
<?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.islunatic.mapper.UserMapper">
<select id="selectByName" resultType="com.islunatic.pojo.User">
select * from user where name = #{name}
</select>
</mapper>
2、在 Mapper 接口中提供对应的方法,方法中将 IPage 对象作为参数传入
@Mapper
public interface UserMapper extends BaseMapper<User> {
IPage<User> selectByName(IPage<User> page, String name);
}
三、ActiveRecord 模式
AR是一种领域模型模式,
特点:
是一个模型类对应关系型数据库中的一个表,而模型类的一个实例对应表中的一行记录。
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User extends Model<User> {
private Long id;
private String name;
private Integer age;
private String email;
}
Model 类中提供了一些增删改查方法,可以直接使用实体类对象调用这增删改查方法,简化了操作的语法,但是底层是需要UserMapper的,所以持久层接口不能省略。
具体使用方式
增
@Test
void activeRecordAdd(){
User user = new User();
user.setName("wang");
user.setAge(35);
user.setEmail("123456789@qq.com");
user.insert();
}
删
@Test
void activeRecordDelete(){
User user = new User();
user.setId(8L);
user.deleteById();
}
改
@Test
void activeRecordUpdate(){
User user = new User();
user.setId(6L);
user.setAge(50);
user.updateById();
}
查
@Test
void activeRecordSelect(){
User user = new User();
user.setId(1L);
User user1= user.selectById();
System.out.println(user1);
}
四、SimpleQuery
SimpleQuery可以对selectList查询后的结果用 Stream流进行了一些封装,可以返回一些指定果
1、list
@Test
void testList1(){
List<Long> ids = SimpleQuery.list(newLambdaQueryWrapper<User>().eq(User::getName, "Mary"), User::getId);
System.out.println(ids);
}
②对于封装后的字段进行 lambda 操作
@Test
void testList2(){
List<String> names = SimpleQuery.list(new
LambdaQueryWrapper<User>().eq(User::getName, "Mary"),User::getName,e ->
Optional.of(e.getName()).map(String::toLowerCase).ifPresent(e::setName));
System.out.println(names);
}
2、map
@Test
void testMap(){
//将所有元素封装为 Map 形式
Map<Long, User> Map1 = SimpleQuery.keyMap(new LambdaQueryWrapper<>(), User::getId);
System.out.println(Map1 );
}
3、group
分组效果
@Test
void testGroup(){
Map<String, List<User>> maps = SimpleQuery.group(new
LambdaQueryWrapper<>(), User::getName);
System.out.println(maps );
}
五、锁
常见的数据库锁类型有两种,悲观锁和乐观锁。
一次完成的修改操作是,先查询数据,然后修改数据。
1、悲观锁
悲观锁是在查询的时候就锁定数据,在这次请求未完成之前,不会释放锁。等到 这次请求完毕以后,再释放锁,释放了锁以后,其他请求才可以对于这条数据完成读写。
这样做的操作能够保证读取到的信息就是当前的信息,保证了信息的正确性,但是并发效率很低,在实际开发中使用悲观锁的场景很少,因为在并发时我们是要保证效率。
2、乐观锁
乐观锁是通过表字段完成设计的,他的核心思想是在读取的时候不加锁,其他请求依然可以读取到这个数据,在修改的时候判断一个数据是否有被修改过,如果有被修改过, 那本次请求的修改操作失效。
Update 表 set 字段 = 新值,version = version + 1 where version = 1
这样做的操作是不会对于数据读取产生影响,并发的效率较高。但是可能目前看到的数据并不是真实信息数据,是被修改之前的,但是在很多场景下是可以容忍的,并不是产生很大影响,例如很多时候我们看到的是有库存,或者都加入到购物车了,但是点进去以后库存没有了。
在实体类字段上,添加对应的属性,并使用@Version 标注为这是一个乐观锁字段信息。
通过配置拦截器,对每条修改语句完成语句的增强,让每条修改的sql语句在执行的时候,都加版本控制的功能。
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
}
六、代码生成器
1、导入依赖
<!--代码生成器依赖-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.3</version>
</dependency>
<!--freemarker 模板依赖-->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.31</version>
</dependency>
2、编写代码生成器
@SpringBootTest
public class GeneratorApplicationTests {
public static void main(String[] args) {
FastAutoGenerator.create("jdbc:mysql://localhost:3306/mp?serverTimezone=UTC&cha
racterEncoding=utf8&useUnicode=true&useSSL=false", "root", "root")
.globalConfig(builder -> {
builder.author("phb") // 设置作者
//.enableSwagger() // 开启 swagger 模式
.fileOverride() // 覆盖已生成文件
.outputDir("D://"); // 指定输出目录
})
.packageConfig(builder -> {
builder.parent("com.islunatic") // 设置父包名
.moduleName("mp") // 设置父包模块名
//设置 mapperXml 生成路径
.pathInfo(Collections.singletonMap(OutputFile.xml, "D://"));
})
.strategyConfig(builder -> {
builder.addInclude("user") // 设置需要生成的表名
.addTablePrefix(""); // 设置过滤表前缀
})
.templateEngine(new FreemarkerTemplateEngine()) // 使用 Freemarker 引擎模板,
默认的是 Velocity 引擎模板
.execute();
}
}
七、配置多数据源
1、导入依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.1.0</version>
</dependency>
2、编写数据源信息
spring:
datasource:
dynamic:
primary: master
strict: false
datasource:
master:
username: root
password: root
url:jdbc:mysql://localhost:3306/mp?serverTimezone=UTC
driver-class-name: com.mysql.cj.jdbc.Driver
slave_1:
username: root
password: root
url:jdbc:mysql://localhost:3306/mp2?serverTimezone=UTC
driver-class-name: com.mysql.cj.jdbc.Driver
3、@DS注解描述不同的数据源信息
八、自动填充
1、在需要自动填充的实现类字段上添加注解
@TableField(fill = FieldFill.INSERT)
private Long createUser;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Long updateUser;
2、自定义实现类
package com.islunatic.common;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
metaObject.setValue("createTime",LocalDateTime.now());
metaObject.setValue("updateTime",LocalDateTime.now());
metaObject.setValue("createUser",BaseContext.getCurrentId());
metaObject.setValue("updateUser",BaseContext.getCurrentId());
}
@Override
public void updateFill(MetaObject metaObject) {
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
metaObject.setValue("updateUser",BaseContext.getCurrentId());
}
}
MetaObjectHandler
提供的默认方法的策略均为:如果属性有值则不覆盖,如果填充值为null
则不填充
字段必须声明TableField
注解,属性fill
选择对应策略,该声明告知Mybatis-Plus
要预留注入SQL
字段
填充处理器MyMetaObjectHandler
在 Spring Boot 中需要声明@Component
或@Bean
注入
3、工具类,设置自动填充创建用户的名称
package com.islunatic.common;
/**
* 基于ThreadLocal封装工具类,用户保存和获取当前登录用户id
*/
public class BaseContext {
private static ThreadLocal<Long> threadLocal = new ThreadLocal<>();
//设置值
public static void setCurrentId(Long id){
threadLocal.set(id);
}
//获取值
public static Long getCurrentId(){
return threadLocal.get();
}
}