IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 【后端】MyBatis-Plus 体系(三) -> 正文阅读

[Java知识库]【后端】MyBatis-Plus 体系(三)

MyBatis-Plus

六、插件

1. 分页插件

  • MyBatis-Plus自带分页插件,只要简单的配置即可实现分页功能

1.1 添加配置类

@Configuration 
@MapperScan("com.alex.mybatisplus.mapper") 
//可以将主类中的注解移到此处 
public class MybatisPlusConfig { 
	@Bean 
	public MybatisPlusInterceptor mybatisPlusInterceptor() { 
		MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); 
		interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); 
		return interceptor; 
	} 
}

1.2 测试

@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()); 
}

2. xml自定义分页

2.1 UserMapper中定义接口方法

/**
 * 根据年龄查询用户列表,分页显示 
 * @param page 分页对象,xml中可以从里面进行取值,传递参数 Page 即自动分页,必须放在第一位 
 * @param age 年龄 
 * @return 
*/
	Page<User> selectPageVo(@Param("page") Page<User> page, 
		@Param("age") Integer age);

2.2 UserMapper.xml中编写SQL

<!--SQL片段,记录基础字段--> 
<sql id="BaseColumns">id,username,age,email</sql> 
<!--Page<User> selectPageVo(Page<User> page, Integer age);--> 
<select id="selectPageVo" resultType="User"> 
	SELECT <include refid="BaseColumns"></include> 
	FROM t_user 
	WHERE age > # {age} 
</select>

2.3 测试

@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()); 
}

3. 乐观锁

3.1 场景

  • 一件商品,成本价是80元,售价是100元。老板先是通知小李,说你去把商品价格增加50元。小李正在玩游戏,耽搁了一个小时。正好一个小时后,老板觉得商品价格增加到150元,价格太高,可能会影响销量。又通知小王,你把商品价格降低30元。
  • 此时,小李和小王同时操作商品后台系统。小李操作的时候,系统先取出商品价格100元;小王也在操作,取出的商品价格也是100元。小李将价格加了50元,并将100+50=150元存入了数据库;小王将商品减了30元,并将100-30=70元存入了数据库。是的,如果没有锁,小李的操作就
    完全被小王的覆盖了。
  • 现在商品价格是70元,比成本价低10元。几分钟后,这个商品很快出售了1千多件商品,老板亏1万多。

3.2 乐观锁与悲观锁

  • 上面的故事,如果是乐观锁,小王保存价格前,会检查下价格是否被人修改过了。如果被修改过了,则重新取出的被修改后的价格,150元,这样他会将120元存入数据库。
  • 如果是悲观锁,小李取出数据后,小王只能等小李操作完之后,才能对价格进行操作,也会保证最终的价格是120元。

3.3 模拟修改冲突

  • 数据库中增加商品表
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) 
);
  • 添加数据
INSERT INTO t_product (id, NAME, price) VALUES (1, '外星人笔记本', 100);
  • 添加实体
package com.alex.mybatisplus.entity; 
import lombok.Data; 
@Data 
public class Product { 
	private Long id; 
	private String name; 
	private Integer price; 
	private Integer version; 
}
  • 添加mapper
public interface ProductMapper extends BaseMapper<Product> { }
  • 测试
@Test 
public void testConcurrentUpdate() { 
	//1、小李 
	Product p1 = productMapper.selectById(1L); 
	System.out.println("小李取出的价格:" + p1.getPrice()); 
	//2、小王 
	Product p2 = productMapper.selectById(1L); 
	System.out.println("小王取出的价格:" + p2.getPrice());
	//3、小李将价格加了50元,存入了数据库 
	p1.setPrice(p1.getPrice() + 50); 
	int result1 = productMapper.updateById(p1); 
	System.out.println("小李修改结果:" + result1); 
	//4、小王将商品减了30元,存入了数据库 
	p2.setPrice(p2.getPrice() - 30); 
	int result2 = productMapper.updateById(p2); 
	System.out.println("小王修改结果:" + result2); 
	//最后的结果 
	Product p3 = productMapper.selectById(1L); 
	//价格覆盖,最后的结果:70 
	System.out.println("最后的结果:" + p3.getPrice()); 
}

3.4 乐观锁实现流程

  • 数据库中添加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

3.5 Mybatis-Plus实现乐观锁

  • 修改实体类
package com.alex.mybatisplus.entity; 
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; 
}
  • 添加乐观锁插件配置
@Bean 
public MybatisPlusInterceptor mybatisPlusInterceptor(){ 
	MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); 
	//添加分页插件 
	interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); 
	//添加乐观锁插件 
	interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); 
	return interceptor; 
}
  • 测试修改冲突
小李查询商品信息:
SELECT id,name,price,version FROM t_product WHERE id=?
小王查询商品信息:
SELECT id,name,price,version FROM t_product WHERE id=?
小李修改商品价格,自动将version+1
UPDATE t_product SET name=?, price=?, version=? WHERE id=? AND version=?
Parameters: 外星人笔记本(String), 150(Integer), 1(Integer), 1(Long), 0(Integer)
小王修改商品价格,此时version已更新,条件不成立,修改失败
UPDATE t_product SET name=?, price=?, version=? WHERE id=? AND version=?
Parameters: 外星人笔记本(String), 70(Integer), 1(Integer), 1(Long), 0(Integer)
最终,小王修改失败,查询价格:150
SELECT id,name,price,version FROM t_product WHERE id=?
  • 优化流程
@Test 
public void testConcurrentVersionUpdate() { 
	//小李取数据 
	Product p1 = productMapper.selectById(1L); 
	//小王取数据 
	Product p2 = productMapper.selectById(1L); 
	//小李修改 + 50 
	p1.setPrice(p1.getPrice() + 50); 
	int result1 = productMapper.updateById(p1); 
	System.out.println("小李修改的结果:" + result1); 
	//小王修改 - 30 
	p2.setPrice(p2.getPrice() - 30); 
	int result2 = productMapper.updateById(p2); 
	System.out.println("小王修改的结果:" + result2); 
	if(result2 == 0){ 
		//失败重试,重新获取version并更新 
		p2 = productMapper.selectById(1L);
		p2.setPrice(p2.getPrice() - 30); 
		result2 = productMapper.updateById(p2); 	
	}
	System.out.println("小王修改重试的结果:" + result2); 
	//老板看价格 
	Product p3 = productMapper.selectById(1L); 
	System.out.println("老板看价格:" + p3.getPrice()); 
}

七、通用枚举

  • 表中的有些字段值是固定的,例如性别(男或女),此时我们可以使用MyBatis-Plus的通用枚举来实现

1. 数据库表添加字段sex

在这里插入图片描述

2. 创建通用枚举类型

package com.alex.mp.enums; 
import com.baomidou.mybatisplus.annotation.EnumValue; 
import lombok.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;
	} 
}

3. 配置扫描通用枚举

mybatis-plus: 
  configuration: 
    # 配置MyBatis日志 
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl 
  global-config: 
    db-config: 
      # 配置MyBatis-Plus操作表的默认前缀 
      table-prefix: t_ 
      # 配置MyBatis-Plus的主键策略 
      id-type: auto 
  # 配置扫描通用枚举 
  type-enums-package: com.alex.mybatisplus.enums

4. 测试

@Test 
public void testSexEnum(){ 
	User user = new User(); 
	user.setName("Enum"); 
	user.setAge(20); 
	//设置性别信息为枚举项,会将@EnumValue注解所标识的属性值存储到数据库 
	user.setSex(SexEnum.MALE); 
	//INSERT INTO t_user ( username, age, sex ) VALUES ( ?, ?, ? ) 
	//Parameters: Enum(String), 20(Integer), 1(Integer) 
	userMapper.insert(user); 
}

八、代码生成器

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>

2. 快速生成

public class FastAutoGeneratorTest {
	public static void main(String[] args) { 		
		FastAutoGenerator.create("jdbc:mysql://127.0.0.1:3306/mybatis_plus? characterEncoding=utf-8&userSSL=false", "root", "123456") 
			.globalConfig(builder -> { 
				builder.author("alex") // 设置作者 
				//.enableSwagger() // 开启 swagger 模式 
				.fileOverride() // 覆盖已生成文件 
				.outputDir("D://mybatis_plus"); // 指定输出目录 
			})
			.packageConfig(builder -> { 
				builder.parent("com.alex") // 设置父包名 
					.moduleName("mybatisplus") // 设置父包模块名 
	.pathInfo(Collections.singletonMap(OutputFile.mapperXml, "D://mybatis_plus")); 
	// 设置mapperXml生成路径 })
			.strategyConfig(builder -> { 
				builder.addInclude("t_user") // 设置需要生成的表名 
				.addTablePrefix("t_", "c_"); // 设置过滤表前缀 
			})
			.templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker 引擎模板,默认的是Velocity引擎模板 
			.execute();
	}
}

九、多数据源

  • 适用于多种场景:纯粹多库、 读写分离、 一主多从、 混合模式等
  • 目前我们就来模拟一个纯粹多库的一个场景,其他场景类似
  • 场景说明:
    • 我们创建两个库,分别为:mybatis_plus(以前的库不动)与mybatis_plus_1(新建),将mybatis_plus库的product表移动到mybatis_plus_1库,这样每个库一张表,通过一个测试用例,分别获取用户数据与商品数据,如果获取到说明多库模拟成功

1. 创建数据库及表

  • 创建数据库mybatis_plus_1和表product
CREATE DATABASE `mybatis_plus_1` /*!40100 DEFAULT CHARACTER SET utf8mb4 */; 
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库product表
use mybatis_plus; 
DROP TABLE IF EXISTS product;

2. 引入依赖

<dependency> 
	<groupId>com.baomidou</groupId> 
	<artifactId>dynamic-datasource-spring-boot-starter</artifactId> 
	<version>3.5.0</version> 
</dependency>

3. 配置多数据源

  • 说明:注释掉之前的数据库连接,添加新配置
spring: 
  # 配置数据源信息 
  datasource: 
    dynamic: 
    # 设置默认的数据源或者数据源组,默认值即为master 
    primary: master 
    # 严格匹配数据源,默认false.true未匹配到指定数据源时抛异常,false使用默认数据源 
    strict: false 
    datasource: 
      master: 
        url: jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf- 8&useSSL=false 
        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&useSSL=false 
        driver-class-name: com.mysql.cj.jdbc.Driver 
        username: root 
        password: 123456

4. 创建用户service

public interface UserService extends IService<User> { }
@DS("master") //指定所操作的数据源 
@Service 
public class UserServiceImpl extends ServiceImpl<UserMapper, User> 
implements UserService { }

5. 创建商品service

public interface ProductService extends IService<Product> { }
@DS("slave_1") 
@Service 
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> 
implements ProductService { }

6. 测试

@Autowired 
private UserService userService; 

@Autowired 
private ProductService productService; 

@Test 
public void testDynamicDataSource(){ 
	System.out.println(userService.getById(1L)); 
	System.out.println(productService.getById(1L)); 
}
  • 结果:
    • 都能顺利获取对象,则测试成功

十、MyBatisX 插件

1. MyBatisX 代码生成器

  • 安装:Settings -> Plugins -> MyBatisX
  • idea里Database 加入自己的数据库
  • MySQL -> schemas -> mybatis_plus -> t_user

请添加图片描述

请添加图片描述
请添加图片描述

2. 快速生成 CRUD

请添加图片描述

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-03-08 22:14:58  更:2022-03-08 22:16:15 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 11:11:38-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码