一、Mybatis-Plus
持久化框架
程序分为了持久化和非持久化的两个层次
持久层负责的是数据的持久化处理,将数据存储起来,例如Mybatis,hibernate
非持久层负责的是业务上的处理,流程处理,用户界面等,例如springFramwork等
1 . 快速使用
官网有快速开始实例,导入依赖,配置类开启MapperScanner配置类并设置扫描包路径,编写yml配置文件,导入database,以及mybatis-plus的sql显示配置
mybatis-plus:
configuration:
# 想在运行框查看sql语句结果需要添加的配置文件
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
创建Mapper接口,继承BaseMapper,基础的BaseMapper就包含了基础的CRUD操作。在需要与数据库映射的包上面添加**@TableName等注解**
2. 条件构造器(Wrapper)
当自带的CRUD不能满足操作的时候,需要进行一些条件操作的时候,所以需要条件构造器。条件构造器有一个公共的抽象接口------AbstractWrapper类。下面有三个实现大类,QueryWrapper,UpdateWrapper,AbstractLambdaWrapper,分别对查询,更新等条件进行补充。
条件表达式中的条件连接都是可以使用链式进行表示
a. 自定义sql
有时候sql的前半部分语句比较复杂,而且不推荐写在service层面,所以需要自定义sql进行书写
首先自定义的sql有一个参数名param,要么叫ew
,要么加上注解**@Param(Constants.WRAPPER)**
三种方式:
1)注解SQL
直接使用mybatis的注解方式进行
2)Wrapper传参 + 注解SQL
不适合多表关联查询,查询字段过多
@Select("select * from car ${ew.customSqlSegment}")
Page<Car> selectByPrimaryKey(Page<Car> page, @Param(Constants.WRAPPER) QueryWrapper<Car> queryWrapper);
**优点:**可以动态的查询SQL,然后根据传输的数据动态进行查询操作,boolean condition
**缺点:**不适用于多表关联查询,查询字段过多代码比较凌乱
3)Wrapper传参+xml文件SQL
//(推荐方式)
1. 将条件部分通过Wrapper进行设设置,然后进行传递,后面xml文件中使用{ew.customerSqlSegment}进行添加
2. 设置xxxMapper对xxxXML文件进行绑定,最后通过XXXMapper实体进行调用
3. Lambda表达式
本质就是匿名方法,将函数方法作为参数传递到方法中,可以简化我们的代码,使代码更加灵活简洁
表达式分为两个部分: 左侧 参数部分 -> 右侧 逻辑方法部分
参数部分:要求和实现接口中的方法参数一致,包括参数的数量和类型
方法体:注意是否有返回值
a. 函数式接口
如果接口Interface中需要实现的抽象方法只有一个,这样的接口就是函数式接口.
//有且只有一个实现类必须要实现的抽象方法,所以是函数式接口
@FunctionalInterface //可以判断是否是一个函数式子接口
interface Test{
public void test();
}
@FunctionalInterface //这个也算函数式接口, 没有override注解
public interface IFunctionMulti<T extends Number> {
void multi(List<T> numbers); // 抽象方法
boolean equals(Object obj); // Object中的方法,不算在这个接口里面
}
b. 参数和方法体的使用:
- 当接口中参数只有一个的时候,可以不加小括号,如果没有参数或者多个参数必须加小括号
- 当方法体只有一句的时候,可以省略大括号
4. IsService接口
a. 接口的方法图
ServiceImpl类的使用
1)使用步骤
//创建接口,继承Iservice方法,填写泛型
public interface UserService {
// 添加一些 客制化 的方法
}
//创建一个service接口实现类,实现接口,并且继承ServiceImpl类,并填写泛型
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
b.lambda方法
// 1. service中的lambda快速查询
@Test
public void lambdaQueryTest() {
String s = service.lambdaQuery()
.eq(User::getId, 1)
.one() // 最后需要添加的执行语句,有了它才会执行
.toString();
System.out.println(s);
}
//2. service中的lambda的更新方法
@Test
public void lambdaUpdateTest() {
boolean flag = true;
service.lambdaUpdate()
.set(flag, User::getName, "lgz")
.eq(User::getId, 1)
.update(); // 前面的都是条件和设置语句,这一句才是执行语句
}
c. 程序中的乐观锁和悲观锁
乐观锁就是compare,在修改的时候查询一下现在的值是不是和上一次的值一样
悲观锁就是直接在方法上加@transactional
// 增加年纪
this.lambdaUpdate()
.set(user.getEmail() != null, User::getEmail, newEmail)
.eq(User::getEmail, user.getEmail()) //乐观锁,修改之前判断一下现在的和刚刚查到的是否一致
.update();
d. 批处理的三种方式
-
第一种就是for遍历一条一条插入的方式
-
第二种使用batch自带的批处理,然后分成几次提交
-
第三种利用sql的预编译机制,通过将所有的语句拼接起来,然后一次提交就可以完成插入
- 可以在service层中,使用for循环,将sql 的语句代码进行拼接起来,或者使用mysql的参数配置
- 特别还是网络中,将所有的语句拼接起来,然后统一的 返回到了后端,一次的请求就可以完成插入操作
//这个需要在数据库中开启允许拼接sql的功能,在yaml文件的url后面加上一条属性 &rewriteBatchedStatements=true @Test public void batchInsertTest() { long start = System.currentTimeMillis(); ArrayList<User> list = new ArrayList<>(); for (int i = 6; i < 10000; i++) { User user = new User(Long.parseLong(i+""), "name-i"+i, i, null); list.add(user); if ( i % 1000 == 0) { System.out.println(list.size()); service.saveBatch(list); list.clear(); } } long end = System.currentTimeMillis(); System.out.println(start - end); }
5. 代码生成
使用工具自动生成我们对应数据库表中的entity,mapper,Service,xml文件
使用步骤:
下载Mybatisplus插件,然后填写mysql的url等信息,最后直接点击自动生成代码工具
6. 静态工具
其实就是静态生成的代码块,功能就是和前面的IService一样,但是就是需要多传一个参数泛型过去进行实现。
为什么使用:因为会涉及到多个表之间循环依赖,所以用这个就可以直接进行查询,避免循环依赖
//使用:
Db.method() 使用
7.逻辑删除
a. 使用
重要的信息都不是真正的将数据进行删除,而是将表中多加一个字段isDeleted,然后删除Delete -> Update,查询需要加一个条件 isDeleted == 1
- 配置yaml文件
- 可以在bean中设置**@TableLogic**注解,设置删除的信号等
b.逻辑删除失效的场景
逻辑删除在直接操作数据库的时候会失效,比如直接在mapper中定义Sql查询则会失效,或者直接在XML文件设置的查询操作
@Select("select * from user")
List<User> selectAllUser();
**避免:**应该避免直接操作数据库的操作,mybatis类似于在代码层面给平时的sql语句进行切片操作以后在末尾加上了逻辑删除的条件
8. 枚举处理器
数据表中的多种状态用数字表示缺乏可读性,不便于观看,所以定义一个枚举处理器来处理
在BaseTypeHandler的类中有很多继承类,然后可以对数据库和PO类进行映射
使用:
-
配置类configuration开启default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
// 1. 创建枚举类 public enum UserStatus { NNORMAL(1, "正常"), FREZEE(0, "冻结"); @EnumValue private final int value; private final String des; UserStatus(int value, String des) { this.value = value; this.des = des; } } // 2.修改User的bean类,修改对应的属性为UserStatus类 // 3.可以直接使用 @Test public void testLogicDeleted() { service.lambdaQuery().eq(User::getIsDeleted, UserStatus.NNORMAL).list().forEach(System.out::println); }
9.JSON处理器
前端或者数据库中的字段有些是JSON格式存储的,如果想获取某一部分的的时候则可以定义一个UserInfo类去获取JSON的数据,然后进行使用
第一步:开启JSON映射
第二步:做一个resultMap映射
10. 分页插件
使用
a . 创建配置类,添加分页插件
b.通用的分页实体
1 . 编写UserQuery 实体,保存用户的信息
@Data
@ApiModel(description = "用户查询实体")
public class UserQuery extends PageQuery{
@ApiModelProperty("用户名关键字")
private String name;
@ApiModelProperty("用户状态")
private UserStatus status;
}
2 . 定义一个统一共同的分页查询语句,包括了页码,页码大小,排序字段,是否升序
@Data
@ApiModel(description = "分页查询实体")
@AllArgsConstructor
@NoArgsConstructor
public class PageQuery {
@ApiModelProperty("页码")
private Integer pageNo;
@ApiModelProperty("页码大小")
private Integer pageSize;
@ApiModelProperty("排序字段")
private String sortBy;
@ApiModelProperty("是否升序")
private boolean isAsc;
//然后UserQuery进行继承扩展
3 . 创建一个分页的信息DTO
@Data
@ApiModel(description = "分页结果")
public class PageDTO<T> {
@ApiModelProperty("总条数")
private Integer total;
@ApiModelProperty("总页数")
private Integer pages;
@ApiModelProperty("集合")
private List<T> list;
}
4 .分页
@Test
public void testPage() {
UserQuery query = new UserQuery("lgz", UserStatus.NNORMAL);
query.setPageNo(3); //查询第几页
query.setPageSize(2); //页面大小
String name = query.getName();
Page<User> page = Page.of(query.getPageNo(), query.getPageSize()); //设置分页参数
Page<User> p = service.lambdaQuery() //查询
.ge(User::getId, 1)
.page(page);
System.out.println("records total nums :" + p.getTotal());
System.out.println("pages nums:" + p.getPages());
List<User> user = page.getRecords();
List<UserDTO> dtos = BeanUtil.copyToList(user, UserDTO.class);
dtos.forEach(System.out::println);
user.forEach(System.out::println);
}
c. lambda分页查询
可以使用LambdaQuery表达式来直接进行page查询。
d .创建通用的方法
- 创建QueryToPage方法,将前端传过来的Query对象转换为Page对象
//放到PageQuery类中
public <T> Page<T> toPage(OrderItem... items) {
Page<T> page = Page.of(pageNo, pageSize);
if (StrUtil.isNotBlank(sortBy)) {
page.addOrder(new OrderItem(sortBy, isAsc));
} else if (items != null) {
page.addOrder(items);
}
return page;
}
public <T> Page<T> toPageDefaultSortByCreatedTime() {
return toPage(new OrderItem("cre_time", false));
}
- Page对象转化为DTO对象发给前端
public static <VO, PO> PageDTO<VO> of(Page<PO> p, Class<VO> clazz) {
PageDTO<VO> dto = new PageDTO<>();
dto.setPages(p.getPages());
dto.setTotal(p.getTotal());
List<PO> list = p.getRecords(); //设置PageDto对象返回
if (CollUtil.isEmpty(list)) { //糊涂包的功能
dto.setList(Collections.emptyList());
return dto;
}
dto.setList(BeanUtil.copyToList(list, clazz)); //hutool包转化为list对象
return dto;
}
11 .乐观锁
a. 添加乐观锁插件
@Configuration
public class MybatisConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 注册乐观锁插件
OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor = new OptimisticLockerInnerInterceptor();
interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor);
return interceptor;
}
}
b. 配置entity
添加@Version注解
如果需要修改的时候顺带修改其他的表单项,需要实现一个meta类
@Component
public class MetaObjectHandler implements com.baomidou.mybatisplus.core.handlers.MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "version", ()->1, Integer.class);
}
@Override
public void updateFill(MetaObject metaObject) {
}
}