MyBatisPlus(Spring Boot版)的基本使用

1. 初始化项目

1.1. 配置application.yml

spring:
	# 配置数据源信息
	datasource:
		# 配置数据源类型
		type: com.zaxxer.hikari.HikariDataSource
		# 配置连接数据库信息
		driver-class-name: com.mysql.cj.jdbc.Driver
		url: jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&useSSL=false
		username: root
		password: 123456

注意:

  1. 驱动类driver-class-name
  • spring boot 2.0(内置jdbc5驱动),驱动类使用:driver-class-name: com.mysql.jdbc.Driver
  • spring boot 2.1及以上(内置jdbc8驱动),驱动类使用:driver-class-name: com.mysql.cj.jdbc.Driver

否则运行测试用例的时候会有 WARN 信息
2. 连接地址url

  • MySQL5.7版本的url:jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&useSSL=false
  • MySQL8.0版本的url:jdbc:mysql://localhost:3306/mybatis_plus?serverTimezone=GMT%2B8&characterEncoding=utf-8&useSSL=false

否则运行测试用例报告如下错误:java.sql.SQLException: The server time zone value ‘Öйú±ê׼ʱ¼ä’ is unrecognized or represents more

1.2. 启动类

在Spring Boot启动类中添加@MapperScan注解,扫描mapper包

@SpringBootApplication
@MapperScan("com.wyb.mybatisplus.mapper")
public class MybatisplusApplication {
	public static void main(String[] args) {
		SpringApplication.run(MybatisplusApplication.class, args);
	}
}

1.3. 添加实体

@Data //lombok注解
public class User {
	private Long id;
	private String name;
	private Integer age;
	private String email;
}

1.4. 添加mapper

BaseMapper是MyBatis-Plus提供的模板mapper,其中包含了基本的CRUD方法,泛型为操作的实体类型

public interface UserMapper extends BaseMapper<User> {
}

1.5. 测试

@SpringBootTest
public class MybatisPlusTest {
	@Autowired
	private UserMapper userMapper;
	@Test
	public void testSelectList(){
		//selectList()根据MP内置的条件构造器查询一个list集合,null表示没有条件,即查询所有
		userMapper.selectList(null).forEach(System.out::println);
	}
}

1.6. 添加日志

在application.yml中配置日志输出

# 配置MyBatis日志
mybatis-plus:
	configuration:
		log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

2. 基本CRUD

2.1. BaseMapper

MyBatis-Plus中的基本CRUD在内置的BaseMapper中都已得到了实现,可以直接使用

2.2. 插入

@Test
public void testInsert(){
	User user = new User(null, "张三", 23, "zhangsan@163.com");
	//INSERT INTO user ( id, name, age, email ) VALUES ( ?, ?, ?, ? )
	int result = userMapper.insert(user);
	System.out.println("受影响行数:"+result);
	//1475754982694199298
	System.out.println("id自动获取:"+user.getId());
}

2.3. 删除

  1. 通过id删除记录
@Test
public void testDeleteById(){
	//通过id删除用户信息
	//DELETE FROM user WHERE id=?
	int result = userMapper.deleteById(1475754982694199298L);
	System.out.println("受影响行数:"+result);
}
  1. 通过id批量删除记录
@Test
public void testDeleteBatchIds(){
	//通过多个id批量删除
	//DELETE FROM user WHERE id IN ( ? , ? , ? )
	List<Long> idList = Arrays.asList(1L, 2L, 3L);
	int result = userMapper.deleteBatchIds(idList);
	System.out.println("受影响行数:"+result);
}
  1. 通过map条件删除记录
@Test
public void testDeleteByMap(){
	//根据map集合中所设置的条件删除记录
	//DELETE FROM user WHERE name = ? AND age = ?
	Map<String, Object> map = new HashMap<>();
	map.put("age", 23);
	map.put("name", "张三");
	int result = userMapper.deleteByMap(map);
	System.out.println("受影响行数:"+result);
}

2.4. 修改

@Test
public void testUpdateById(){
	User user = new User(4L, "admin", 22, null);
	//UPDATE user SET name=?, age=? WHERE id=?
	int result = userMapper.updateById(user);
	System.out.println("受影响行数:"+result);
}

2.5. 查询

  1. 根据id查询用户信息
@Test
public void testSelectById(){
	//根据id查询用户信息
	//SELECT id,name,age,email FROM user WHERE id=?
	User user = userMapper.selectById(4L);
	System.out.println(user);
}
  1. 根据多个id查询多个用户信息
@Test
public void testSelectBatchIds(){
	//根据多个id查询多个用户信息
	//SELECT id,name,age,email FROM user WHERE id IN ( ? , ? )
	List<Long> idList = Arrays.asList(4L, 5L);
	List<User> list = userMapper.selectBatchIds(idList);
	list.forEach(System.out::println);
}
  1. 通过map条件查询用户信息
@Test
public void testSelectByMap(){
	//通过map条件查询用户信息
	//SELECT id,name,age,email FROM user WHERE name = ? AND age = ?
	Map<String, Object> map = new HashMap<>();
	map.put("age", 22);
	map.put("name", "admin");
	List<User> list = userMapper.selectByMap(map);
	list.forEach(System.out::println);
}
  1. 查询所有数据
@Test
public void testSelectList(){
	//查询所有用户信息
	//SELECT id,name,age,email FROM user
	List<User> list = userMapper.selectList(null);
	list.forEach(System.out::println);
}

2.6. 通用Service

说明:

  • 通用 Service CRUD 封装 IService 接口,进一步封装 CRUD 采用 get 查询单行 remove 删除 list 查询集合 page 分页 前缀命名方式区分 Mapper 层避免混淆,
  • 泛型 T 为任意实体对象
  • 建议如果存在自定义通用 Service 方法的可能,请创建自己的 IBaseService 继承 Mybatis-Plus 提供的基类
  1. IService
    MyBatis-Plus中有一个接口 IService和其实现类 ServiceImpl,封装了常见的业务层逻辑,详情查看源码IService和ServiceImpl
  2. 创建Service接口和实现类
/**
* UserService继承IService模板提供的基础功能
*/
public interface UserService extends IService<User> {
}
/**
* ServiceImpl实现了IService,提供了IService中基础功能的实现
* 若ServiceImpl无法满足业务需求,则可以使用自定的UserService定义方法,并在实现类中实现
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
  1. 测试查询记录数
@Autowired
private UserService userService;
@Test
public void testGetCount(){
	long count = userService.count();
	System.out.println("总记录数:" + count);
}
  1. 测试批量插入
@Test
public void testSaveBatch(){
	// SQL长度有限制,海量数据插入单条SQL无法实行,
	// 因此MP将批量插入放在了通用Service中实现,而不是通用Mapper
	ArrayList<User> users = new ArrayList<>();
	for (int i = 0; i < 5; i++) {
		User user = new User();
		user.setName("ybc" + i);
		user.setAge(20 + i);
		users.add(user);
	}
	//SQL:INSERT INTO t_user ( username, age ) VALUES ( ?, ? )
	userService.saveBatch(users);
}

3. 常用注解

3.1. @TableName

MyBatis-Plus在确定操作的表时,由BaseMapper的泛型决定,即实体类型决定,且默认操作的表名和实体类型的类名一致

  1. 问题:若实体类类型的类名和要操作的表的表名不一致,会出现什么问题?
    程序抛出异常,Table ‘mybatis_plus.user’ doesn’t exist,因为现在的表名为t_user,而默认操作的表名和实体类型的类名一致,即user表
  2. 解决:
    1. 使用@TableName:在实体类类型上添加@TableName(“t_user”),标识实体类对应的表,即可成功执行SQL语句
    2. 通过全局配置解决问题:使用MyBatis-Plus提供的全局配置,为实体类所对应的表名设置默认的前缀
mybatis-plus:
	configuration:
		# 配置MyBatis日志
		log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
	global-config:
		db-config:
			# 配置MyBatis-Plus操作表的默认前缀
			table-prefix: t_

3.2. @TableId

MyBatis-Plus在实现CRUD时,会默认将id作为主键列,并在插入数据时,默认基于雪花算法的策略生成id

  1. 问题:若实体类和表中表示主键的不是id,而是其他字段,例如uid,MyBatis-Plus会自动识别uid为主
    键列吗?
    程序抛出异常,Field ‘uid’ doesn’t have a default value,说明MyBatis-Plus没有将uid作为主键
    赋值
  2. 解决
    1. @TableId:在实体类中uid属性上通过@TableId将其标识为主键,即可成功执行SQL语句
    2. @TableId的value属性:@TableId(“uid”)或@TableId(value=“uid”)
    3. @TableId的type属性:type属性用来定义主键策略
      • IdType.ASSIGN_ID(默认):雪花算法的策略生成数据id,与数据库id是否设置自增无关
      • IdType.AUTO:使用数据库的自增策略,注意,该类型请确保数据库设置了id自增,否则无效
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

3.3. @TableField

  1. 问题:MyBatis-Plus在执行SQL语句时,要保证实体类中的属性名和表中的字段名一致,如果实体类中的属性名和字段名不一致的情况,会出现什么问题呢?
  2. 解决:
    1. 若实体类中的属性使用的是驼峰命名风格,而表中的字段使用的是下划线命名风格,此时MyBatis-Plus会自动将下划线命名风格转化为驼峰命名风格
    2. 若实体类中的属性和表中的字段不满足情况1,此时需要在实体类属性上使用@TableField(“username”)设置属性所对应的字段名

3.4. @TableLogic

  • 物理删除:真实删除,将对应数据从数据库中删除,之后查询不到此条被删除的数据
  • 逻辑删除:假删除,将对应数据中代表是否被删除字段的状态修改为“被删除状态”,之后在数据库中仍旧能看到此条数据记录 => 在实体类中添加@TableLogic属性
  • 使用场景:可以进行数据恢复

4. 条件构造器和常用接口

4.1. wapper介绍

在这里插入图片描述

  • Wrapper : 条件构造抽象类,最顶端父类
    • AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件
      • QueryWrapper : 查询条件封装
      • UpdateWrapper : Update 条件封装
      • AbstractLambdaWrapper : 使用Lambda 语法
        • LambdaQueryWrapper :用于Lambda语法使用的查询Wrapper
        • LambdaUpdateWrapper : Lambda 更新封装Wrapper

4.2. QueryWrapper

  1. 组装查询条件
@Test
public void test01(){
	//查询用户名包含a,年龄在20到30之间,并且邮箱不为null的用户信息
	//SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE
	is_deleted=0 AND (username LIKE ? AND age BETWEEN ? AND ? AND email IS NOT NULL)
	QueryWrapper<User> queryWrapper = new QueryWrapper<>();
	queryWrapper.like("username", "a").between("age", 20, 30).isNotNull("email");
	List<User> list = userMapper.selectList(queryWrapper);
	list.forEach(System.out::println);
}
  1. 组装排序条件
@Test
public void test02(){
	//按年龄降序查询用户,如果年龄相同则按id升序排列
	//SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE
	is_deleted=0 ORDER BY age DESC,id ASC
	QueryWrapper<User> queryWrapper = new QueryWrapper<>();
	queryWrapper.orderByDesc("age").orderByAsc("id");
	List<User> users = userMapper.selectList(queryWrapper);
	users.forEach(System.out::println);
}
  1. 组装删除条件
@Test
public void test03(){
	//删除email为空的用户
	//DELETE FROM t_user WHERE (email IS NULL)
	QueryWrapper<User> queryWrapper = new QueryWrapper<>();
	queryWrapper.isNull("email");
	//条件构造器也可以构建删除语句的条件
	int result = userMapper.delete(queryWrapper);
	System.out.println("受影响的行数:" + result);
}
  1. 条件的优先级
@Test
public void test04() {
	QueryWrapper<User> queryWrapper = new QueryWrapper<>();
	//将(年龄大于20并且用户名中包含有a)或邮箱为null的用户信息修改
	//UPDATE t_user SET age=?, email=? WHERE (username LIKE ? AND age > ? OR
	email IS NULL)
	queryWrapper.like("username", "a").gt("age", 20).or().isNull("email");
	User user = new User();
	user.setAge(18);
	user.setEmail("user@163.com");
	int result = userMapper.update(user, queryWrapper);
	System.out.println("受影响的行数:" + result);
}
@Test
public void test04() {
	QueryWrapper<User> queryWrapper = new QueryWrapper<>();
	//将用户名中包含有a并且(年龄大于20或邮箱为null)的用户信息修改
	//UPDATE t_user SET age=?, email=? WHERE (username LIKE ? AND (age > ? OR email IS NULL))
	//lambda表达式内的逻辑优先运算
	queryWrapper.like("username", "a").and(i -> i.gt("age", 20).or().isNull("email"));
	User user = new User();
	user.setAge(18);
	user.setEmail("user@163.com");
	int result = userMapper.update(user, queryWrapper);
	System.out.println("受影响的行数:" + result);
}
  1. 组装select子句
@Test
public void test05() {
	//查询用户信息的username和age字段
	//SELECT username,age FROM t_user
	QueryWrapper<User> queryWrapper = new QueryWrapper<>();
	queryWrapper.select("username", "age");
	//selectMaps()返回Map集合列表,通常配合select()使用,避免User对象中没有被查询到的列值null
	List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);
	maps.forEach(System.out::println);
}
  1. 实现子查询
@Test
public void test06() {
	//查询id小于等于3的用户信息
	//SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE (id IN
	(select id from t_user where id <= 3))
	QueryWrapper<User> queryWrapper = new QueryWrapper<>();
	queryWrapper.inSql("id", "select id from t_user where id <= 3");
	List<User> list = userMapper.selectList(queryWrapper);
	list.forEach(System.out::println);
}

4.3. UpdateWrapper

@Test
public void test07() {
	//将(年龄大于20或邮箱为null)并且用户名中包含有a的用户信息修改
	//组装set子句以及修改条件
	UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
	//lambda表达式内的逻辑优先运算
	updateWrapper.set("age", 18).set("email", "user@163.com").like("username", "a").and(i -> i.gt("age", 20).or().isNull("email"));
	//这里必须要创建User对象,否则无法应用自动填充。如果没有自动填充,可以设置为null
	//UPDATE t_user SET username=?, age=?,email=? WHERE (username LIKE ? AND (age > ? OR email IS NULL))
	//User user = new User();
	//user.setName("张三");
	//int result = userMapper.update(user, updateWrapper);
	//UPDATE t_user SET age=?,email=? WHERE (username LIKE ? AND (age > ? OR email IS NULL))
	int result = userMapper.update(null, updateWrapper);
	System.out.println(result);
}

4.4. condition

在组装条件时,必须先判断用户是否选择了这些条件,若选择则需要组装该条件,若没有选择则不能组装,以免影响SQL执行的结果

  1. 实现一:
@Test
public void test08() {
	//定义查询条件,有可能为null(用户未输入或未选择)
	String username = null;
	Integer ageBegin = 10;
	Integer ageEnd = 24;
	QueryWrapper<User> queryWrapper = new QueryWrapper<>();
	//StringUtils.isNotBlank()判断某字符串是否不为空且长度不为0且不由空白符(whitespace) 构成
	if(StringUtils.isNotBlank(username)){
		queryWrapper.like("username","a");
	}
	if(ageBegin != null){
		queryWrapper.ge("age", ageBegin);
	}
	if(ageEnd != null){
		queryWrapper.le("age", ageEnd);
	}
	//SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE (age >= ? AND age <= ?)
	List<User> users = userMapper.selectList(queryWrapper);
	users.forEach(System.out::println);
}

缺点:代码复杂,可以使用带condition参数的重载方法构建查询条件,简化代码的编写

  1. 实现二:
@Test
public void test08UseCondition() {
	//定义查询条件,有可能为null(用户未输入或未选择)
	String username = null;
	Integer ageBegin = 10;
	Integer ageEnd = 24;
	QueryWrapper<User> queryWrapper = new QueryWrapper<>();
	//StringUtils.isNotBlank()判断某字符串是否不为空且长度不为0且不由空白符(whitespace) 构成
	queryWrapper.like(StringUtils.isNotBlank(username), "username", "a").ge(ageBegin != null, "age", ageBegin).le(ageEnd != null, "age", ageEnd);
	//SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE (age >= ? AND age <= ?)
	List<User> users = userMapper.selectList(queryWrapper);
	users.forEach(System.out::println);
}

4.5. LambdaQueryWrapper

@Test
public void test09() {
	//定义查询条件,有可能为null(用户未输入)
	String username = "a";
	Integer ageBegin = 10;
	Integer ageEnd = 24;
	LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
	//避免使用字符串表示字段,防止运行时错误
	queryWrapper.like(StringUtils.isNotBlank(username), User::getName, username).ge(ageBegin != null, User::getAge, ageBegin).le(ageEnd != null, User::getAge, ageEnd);
	List<User> users = userMapper.selectList(queryWrapper);
	users.forEach(System.out::println);
}

4.6. LambdaUpdateWrapper

@Test
public void test10() {
	//组装set子句
	LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
	updateWrapper.set(User::getAge, 18).set(User::getEmail, "user@163.com").like(User::getName, "a").and(i -> i.lt(User::getAge, 24).or().isNull(User::getEmail)); //lambda表达式内的逻辑优先运算
	User user = new User();
	int result = userMapper.update(user, updateWrapper);
	System.out.println("受影响的行数:" + result);
}

5. 插件

5.1. 分页插件

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

  1. 添加配置类
@Configuration
@MapperScan("com.wyb.mybatisplus.mapper") //可以将主类中的注解移到此处
public class MybatisPlusConfig {
	@Bean
	public MybatisPlusInterceptor mybatisPlusInterceptor() {
		MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
		interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
		return interceptor;
	}
}
  1. 测试
@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());
}

5.2. xml自定义分页

  1. UserMapper中定义接口方法
/**
* 根据年龄查询用户列表,分页显示
* @param page 分页对象,xml中可以从里面进行取值,传递参数 Page 即自动分页,必须放在第一位
* @param age 年龄
* @return
*/
Page<User> selectPageVo(@Param("page") Page<User> page, @Param("age") Integer age);
  1. UserMapper.xml中编写SQL
<!--SQL片段,记录基础字段-->
<sql id="BaseColumns">id,username,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>
  1. 测试
@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());
}

5.3. 乐观锁与悲观锁

  1. 乐观锁实现流程
    数据库中添加version字段,取出记录时,获取当前version
    更新时,version + 1,如果where语句中的version版本不对,则更新失败
  2. Mybatis-Plus实现乐观锁
  • 修改实体类
package com.wyb.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;
}

6. 通用枚举

6.1. 创建通用枚举类型

package com.wyb.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;
	}
}

6.2. 配置扫描通用枚举

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.wyb.mybatisplus.enums

6.3. 测试

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

7. 代码生成器

7.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>

7.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("wyb") // 设置作者
			//.enableSwagger() // 开启 swagger 模式
			.fileOverride() // 覆盖已生成文件
			.outputDir("D://mybatis_plus"); // 指定输出目录
			})
		.packageConfig(builder -> {
			builder.parent("com.wyb") // 设置父包名
			.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();
	}
}

8. 多数据源

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/918872.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【MongoDB】MongoDB的集群,部署架构,OptLog,集群优化等详解

文章目录 一、引入复制集的原因二、复制集成员&#xff08;一&#xff09;基本成员&#xff08;二&#xff09;主节点&#xff08;Primary&#xff09;细化成员 三、复制集常见部署架构&#xff08;一&#xff09;基础三节点&#xff08;二&#xff09;跨数据中心 四、复制集保…

Golang | Leetcode Golang题解之第564题寻找最近的回文数

题目&#xff1a; 题解&#xff1a; func nearestPalindromic(n string) string {m : len(n)candidates : []int{int(math.Pow10(m-1)) - 1, int(math.Pow10(m)) 1}selfPrefix, _ : strconv.Atoi(n[:(m1)/2])for _, x : range []int{selfPrefix - 1, selfPrefix, selfPrefix …

git根据远程分支创建本地新分支

比如我当前本地仓库有4个 remote 仓库&#xff0c;我希望根据其中的一个 <remote>/<branch> 创建本地分支&#xff1a; 先使用 github fetch <remote> 拉取 <remote> 的分支信息&#xff0c;然后在 git checkout -b 创建新分支时使用 -t <remote>…

r-and-r——提高长文本质量保证任务的准确性重新提示和上下文搜索的新方法可减轻大规模语言模型中的迷失在中间现象

概述 随着大规模语言模型的兴起&#xff0c;自然语言处理领域取得了重大发展。这些创新的模型允许用户通过输入简单的 "提示 "文本来执行各种任务。然而&#xff0c;众所周知&#xff0c;在问题解答&#xff08;QA&#xff09;任务中&#xff0c;用户在处理长文本时…

Redis 概 述 和 安 装

安 装 r e d i s: 1. 下 载 r e dis h t t p s : / / d o w n l o a d . r e d i s . i o / r e l e a s e s / 2. 将 redis 安装包拷贝到 /opt/ 目录 3. 解压 tar -zvxf redis-6.2.1.tar.gz 4. 安装gcc yum install gcc 5. 进入目录 cd redis-6.2.1 6. 编译 make …

Android 项目依赖库无法找到的解决方案

目录 错误信息解析 解决方案 1. 检查依赖版本 2. 检查 Maven 仓库配置 3. 强制刷新 Gradle 缓存 4. 检查网络连接 5. 手动下载依赖 总结 相关推荐 最近&#xff0c;我在编译一个 Android 老项目时遇到了一个问题&#xff0c;错误信息显示无法找到 com.gyf.immersionba…

北航软件算法C4--图部分

C4上级图部分 TOPO!步骤代码段TOPO排序部分 完整代码 简单的图图题目描述输入输出样例步骤代码段开辟vector容器作为dist二维数组初始化调用Floyd算法查询 完整代码 负环题目描述输入输出样例步骤代码段全局变量定义spfa1函数用于判断是否有负环spfa2用于记录每个点到1号点的距…

ks 小程序sig3

前言 搞了app版的快手之后 &#xff08;被风控麻了&#xff09; 于是试下vx小程序版的 抓包调试 小程序抓包问题 网上很多教程&#xff0c; github也有开源的工具代码 自行搜索 因为我们需要调试代码&#xff0c;所以就用了下开源的工具 &#xff08;可以用chrome的F12功能&a…

docker:docker: Get https://registry-1.docker.io/v2/: net/http: request canceled

无数次的拉镜像让人崩溃&#xff1a; rootnode11:~/ragflow/docker# more rag.sh #export HTTP_PROXYhttp://192.168.207.127:7890 #export HTTPS_PROXYhttp://192.168.207.127:7890 #export NO_PROXYlocalhost,127.0.0.1,.aliyun.com docker compose -f docker-compose-gpu-C…

基于java Springboot高校失物招领平台

一、作品包含 源码数据库设计文档万字PPT全套环境和工具资源部署教程 二、项目技术 前端技术&#xff1a;Html、Css、Js、Vue、Element-ui 数据库&#xff1a;MySQL 后端技术&#xff1a;Java、Spring Boot、MyBatis 三、运行环境 开发工具&#xff1a;IDEA/eclipse 数据…

菜鸟驿站二维码/一维码 取件识别功能

特别注意需要引入 库文 ZXing 可跳转&#xff1a; 记录【WinForm】C#学习使用ZXing.Net生成条码过程_c# zxing-CSDN博客 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using static System.Net.…

使用WebRTC实现点对点实时音视频通信的技术详解

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 使用WebRTC实现点对点实时音视频通信的技术详解 使用WebRTC实现点对点实时音视频通信的技术详解 使用WebRTC实现点对点实时音视频…

执行flink sql连接clickhouse库

手把手教学&#xff0c;flink connector打通clickhouse大数据库&#xff0c;通过下发flink sql&#xff0c;来使用ck。 组件版本jdk1.8flink1.17.2clickhouse23.12.2.59 1.背景 flink官方不支持clickhouse连接器&#xff0c;工作中难免会用到。 2.方案 利用GitHub大佬提供…

力扣(leetcode)题目总结——辅助栈篇

leetcode 经典题分类 链表数组字符串哈希表二分法双指针滑动窗口递归/回溯动态规划二叉树辅助栈 本系列专栏&#xff1a;点击进入 leetcode题目分类 关注走一波 前言&#xff1a;本系列文章初衷是为了按类别整理出力扣&#xff08;leetcode&#xff09;最经典题目&#xff0c…

基于Java Springboot宠物猫售卖管理系统

一、作品包含 源码数据库全套环境和工具资源部署教程 二、项目技术 前端技术&#xff1a;Html、Css、Js、Vue、Element-ui 数据库&#xff1a;MySQL 后端技术&#xff1a;Java、Spring Boot、MyBatis 三、运行环境 开发工具&#xff1a;IDEA/eclipse 数据库&#xff1a;…

Windows docker下载minio出现“Using default tag: latestError response from daemon”

Windows docker下载minio出现 Using default tag: latest Error response from daemon: Get "https://registry-1.docker.io/v2/": context deadline exceeded 此类情况&#xff0c;一般为镜像地址问题。 {"registry-mirrors": ["https://docker.re…

数据结构查找-哈希表(开发地址法+线性探测法)+(创建+查找+删除代码)+(C语言代码)

#include<stdlib.h> #include<stdio.h> #include<stdbool.h> #define NULLKEY -1//单元为空 #define DELKEY -2//单元内容被删除 #define M 20 typedef struct {int key;//关键字int count;//统计哈希冲突探测次数 }HashTable; //插入到哈希表 void InsertHT…

视频直播5G CPE解决方案:ZX7981PG/ZX7981PMWIFI6网络覆盖

方案背景 视频直播蓬勃发展的当下&#xff0c;传统直播网络联网方式的局限性越来越明显。目前传统直播的局限性主要集中在以下几个方面&#xff1a; 传统直播间网络架构条件有限&#xff0c;可连接WIFI数量少&#xff0c;多终端同时直播难以维持&#xff1b;目前4G网络带宽有限…

【电子设计】按键LED控制与FreeRTOS

1. 安装Keilv5 打开野火资料,寻找软件包 解压后得到的信息 百度网盘 请输入提取码 提取码:gfpp 安装526或者533版本都可以 下载需要的 F1、F4、F7、H7 名字的 DFP pack 芯片包 安装完 keil 后直接双击安装 注册操作,解压注册文件夹后根据里面的图示步骤操作 打开说明 STM…

vue3【实战】切换白天黑夜(暗黑模式)【组件封装】DarkMode.vue

效果预览 原理解析 切换为暗黑模式时&#xff0c;会在 html 标签上添加样式类 dark导入 ElementPlus 的暗黑模式样式后&#xff0c; ElementPlus 组件会自动响应暗黑模式自定义组件需用 UnoCSS 的 dark: 语法自定义暗黑模式的样式 代码实现 技术方案 vue3 vite ElementPlus …