一、分页插件
1.自带的
启动类
在启动类里配置分页相关内容
java
package com.qcby;
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.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
@MapperScan("com.qcby.mybatisPlus.mapper")
public class MybatisPlusApplication {
public static void main(String[] args) {
//springboot应用启动起来
SpringApplication.run(MybatisPlusApplication.class, args);
}
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new
PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
测试类
java
package com.qcby.mybatisPlus;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.qcby.mybatisPlus.mapper.UserMapper;
import com.qcby.mybatisPlus.model.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 PageTest {
@Autowired
private UserMapper userMapper;
/*
* 自带的
* */
@Test
public void testPage(){
//设置分页参数
Page<User> page = new Page<>(1, 5);//当前页,每页展示数量
userMapper.selectPage(page, null);
//获取分页数据
List<User> list = page.getRecords();
list.forEach(System.out::println);
System.out.println("当前页:"+page.getCurrent());
System.out.println("每页显示的条数:"+page.getSize());
System.out.println("总记录数:"+page.getTotal());
System.out.println("总页数:"+page.getPages());
System.out.println("是否有上一页:"+page.hasPrevious());
System.out.println("是否有下一页:"+page.hasNext());
/*
* SELECT COUNT(*) AS total FROM t_user WHERE is_deleted = 0 total=18
* SELECT uid AS id,name,age,email,is_deleted FROM t_user WHERE is_deleted=0 LIMIT ?
* 当前页:1
* 每页显示的条数:5
* 总记录数:18
* 总页数:4
* 是否有上一页:false
* 是否有下一页:true
* */
}
}
测试结果
User(uid=1, name=qcby, age=18, email=qcby@qq.com, isDeleted=0)
User(uid=2, name=张三, age=20, email=test2@baomidou.com, isDeleted=0)
User(uid=3, name=Tom, age=28, email=test3@baomidou.com, isDeleted=0)
User(uid=4, name=Sandy, age=21, email=test4@baomidou.com, isDeleted=0)
User(uid=5, name=Billie, age=24, email=test5@baomidou.com, isDeleted=0)
当前页:1
每页显示的条数:5
总记录数:18
总页数:4
是否有上一页:false
是否有下一页:true
2.自定义分页
UserMapper接口
UserMapper中定义接口方法
java
package com.qcby.mybatisPlus.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.qcby.mybatisPlus.model.User;
import org.apache.ibatis.annotations.Param;
/*
* mybatisPlus的持久层接口
* 继承BaseMapper<T>---框架提供好的
* 向上提取的思想
* */
public interface UserMapper extends BaseMapper<User> {
/**
* 根据年龄查询用户列表,分页显示
* @param page 分页对象 ,xml中可以从里面进行取值 ,传递参数 Page 即自动分页 ,必须放在第一位
* @param age 年龄
* @return */
Page<User> selectPageVo(@Param("page") Page<User> page, @Param("age") Integer age);
}
UserMapper.xml
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.qcby.mybatisPlus.mapper.UserMapper"> <!--对谁进行操作就写谁-->
<!--SQL片段,记录基础字段-->
<sql id="BaseColumns">uid,name,age,email</sql>
<!--IPage<User> selectPageVo(Page<User> page, Integer age);-->
<select id="selectPageVo" resultType="user">
SELECT <include refid="BaseColumns"></include> FROM t_user WHERE age > # {age}
</select>
</mapper>
application.yml配置文件
在配置文件里配置xml文件所在位置
yaml
# 配置MyBatis日志
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
# 配置mybatis_plus操作表的默认前缀
table-prefix: t_
#id-type: auto #配置MyBatis-Plus的主键策略
mapper-locations: classpath:mapper/*.xml #对应mapper映射文件xml文件所在位置
type-aliases-package: com.qcby.mybatisPlus.model #对应实体类所在位置
测试类
java
/*
* 自定义的
* */
@Test
public void testSelectPageVo(){
//设置分页参数
Page<User> page=new Page<>(1,5);//当前页,每页展示数量
userMapper.selectPageVo(page,20);//自己写的方法
//获取分页数据
List<User> list = page.getRecords();
list.forEach(System.out::println);
System.out.println("当前页:"+page.getCurrent());
System.out.println("每页显示的条数:"+page.getSize());
System.out.println("总记录数:"+page.getTotal());
System.out.println("总页数:"+page.getPages());
System.out.println("是否有上一页:"+page.hasPrevious());
System.out.println("是否有下一页:"+page.hasNext());
/*
* SELECT COUNT(*) AS total FROM t_user WHERE age > ?(20) total=9
* SELECT uid,name,age,email FROM t_user WHERE age > ? LIMIT ?
* 当前页:1
* 每页显示的条数:5
* 总记录数:9
* 总页数:2
* 是否有上一页:false
* 是否有下一页:true
* */
}
测试结果
User(uid=3, name=Tom, age=28, email=test3@baomidou.com, isDeleted=null)
User(uid=4, name=Sandy, age=21, email=test4@baomidou.com, isDeleted=null)
User(uid=5, name=Billie, age=24, email=test5@baomidou.com, isDeleted=null)
User(uid=10, name=szy0, age=28, email=user111@qcby.com, isDeleted=null)
User(uid=11, name=szy1, age=28, email=user111@qcby.com, isDeleted=null)
当前页:1
每页显示的条数:5
总记录数:9
总页数:2
是否有上一页:false
是否有下一页:true
二、乐观锁
场景
一件商品,成本价是80元,售价是100元。老板先通知小李,把商品价格增加50元。但是小李正在玩游戏,耽搁了一个小时。正好一个小时后,老板觉得商品价格增加到150元,价格太高,可能会影响销量。又通知小王把商品价格降低30元。
此时,小李和小王同时操作商品后台系统。小李操作的时候,系统先取出商品价格100元;小王也在操作,取出的商品价格也是100元。小李将价格加了50元,并将100+50=150元存入了数据 库;小王将商品减了30元,并将100-30=70元存入了数据库。那么,如果没有锁,小李的操作就完全被小王的覆盖了。
乐观锁与悲观锁
上面的故事,如果是乐观锁,小王保存价格前,会检查下价格是否被人修改过了。如果被修改过 了,则重新取出的被修改后的价格, 150元,这样他会将120元存入数据库。
如果是悲观锁,小李取出数据后,小王只能等小李操作完之后,才能对价格进行操作,也会保证 最终的价格是120元。
模拟修改冲突
数据库中增加商品表
sql
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)
);
添加数据
sql
INSERT INTO t_product (id, NAME, price) VALUES (1, '外星人笔记本 ', 100);
添加实体类Product
因为在配置文件配置了mybatis_plus操作表的默认前缀,所以这里不用加@TableName注解
java
package com.qcby.mybatisPlus.model;
import lombok.Data;
@Data
public class Product {
private Long id;
private String name;
private Integer price;
private Integer version;
}
添加Mapper接口
java
package com.qcby.mybatisPlus.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.qcby.mybatisPlus.model.Product;
public interface ProductMapper extends BaseMapper<Product> {
}
测试类
java
/*
* 模拟修改冲突
* */
@Test
public void testConcurrentUpdate(){
//1.小李
Product p1=productMapper.selectById(1);//SELECT id,name,price,version FROM t_product WHERE id=?
System.out.println("小李取出的价格:"+p1.getPrice());//小李取出的价格:100
//2.小王
Product p2=productMapper.selectById(1);//SELECT id,name,price,version FROM t_product WHERE id=?
System.out.println("小王取出的价格:"+p2.getPrice());//小王取出的价格:100
//3.小李将价格加了50元
p1.setPrice(p1.getPrice()+50);
int result1=productMapper.updateById(p1);//UPDATE t_product SET name=?, price=?, version=? WHERE id=?
System.out.println("小李修改结果:"+result1);//小李修改结果:1
//4.小王将价格减了30元
p2.setPrice(p2.getPrice()-30);
int result2=productMapper.updateById(p2);//UPDATE t_product SET name=?, price=?, version=? WHERE id=?
System.out.println("小王修改结果:"+result2);//小王修改结果:1
//5.最后的结果
Product p3=productMapper.selectById(1);//SELECT id,name,price,version FROM t_product WHERE id=?
//价格覆盖,最后的结果是70
System.out.println("最后的结果:"+p3.getPrice());//最后的结果:70
}
乐观锁实现流程
数据库中添加version字段,取出记录时,获取当前的version:SELECT id,name
,price,version
FROM product WHERE id=1
更新时,version+1,如果where语句中的version版本不对,则更新失败:
UPDATE product SET price=price+50, version
=version
+ 1 WHERE id=1 AND version
=1
mybatis_plus实现乐观锁
修改实体类
java
package com.qcby.mybatisPlus.model;
import com.baomidou.mybatisplus.annotation.Version;
import lombok.Data;
@Data
public class Product {
private Long id;
private String name;
private Integer price;
@Version
private Integer version;
}
添加乐观锁插件配置---启动类
java
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//添加分页插件
interceptor.addInnerInterceptor(new
PaginationInnerInterceptor(DbType.MYSQL));
//添加乐观锁插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
测试类优化流程
java
/*
* 优化流程
* */
@Test
public void testConcurrentUpdate2(){
//1.小李取数据
Product p1=productMapper.selectById(1);
/*
* SELECT id,name,price,version FROM t_product WHERE id=?
* Parameters: 1(Integer)
* 1, 外星人笔记本 , 100, 0
* */
//2.小王取数据
Product p2=productMapper.selectById(1);
/*
* SELECT id,name,price,version FROM t_product WHERE id=?
* Parameters: 1(Integer)
* 1, 外星人笔记本 , 100, 0
* */
//3.小李修改+50
p1.setPrice(p1.getPrice()+50);
int result1=productMapper.updateById(p1);
System.out.println("小李修改结果:"+result1);
/*
* UPDATE t_product SET name=?, price=?, version=? WHERE id=? AND version=?
* Parameters: 外星人笔记本 (String), 150(Integer), 1(Integer), 1(Long), 0(Integer)
* 小李修改结果:1
* */
//4.小王修改-30
p2.setPrice(p2.getPrice()-30);
int result2=productMapper.updateById(p2);
System.out.println("小王修改结果:"+result2);
if (result2==0){
//失败,重试
p2=productMapper.selectById(1);
p2.setPrice(p2.getPrice()-30);
result2=productMapper.updateById(p2);
}
System.out.println("小王修改重试结果:"+result2);
/*
* UPDATE t_product SET name=?, price=?, version=? WHERE id=? AND version=?
* 外星人笔记本 (String), 70(Integer), 1(Integer), 1(Long), 0(Integer)
* 小王修改结果:0
* 失败重试:---------------------------------------------------------------
* SELECT id,name,price,version FROM t_product WHERE id=?
* Parameters: 1(Integer)
* 1, 外星人笔记本 , 150, 1
* UPDATE t_product SET name=?, price=?, version=? WHERE id=? AND version=?
* 外星人笔记本 (String), 120(Integer), 2(Integer), 1(Long), 1(Integer)
* 小王修改重试结果:1
* */
//5.查看结果
Product p3=productMapper.selectById(1);
System.out.println("最后的结果:"+p3.getPrice());
/*
* SELECT id,name,price,version FROM t_product WHERE id=?
* Parameters: 1(Integer)
* 1, 外星人笔记本 , 120, 2
* 最后的结果:120
* */
}