MyBatisPlus(Spring Boot版)的基本使用

news2024/11/18 7:10:22

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

串口DMA接收不定长数据

STM32F767—&#xff1e;串口通信接收不定长数据的处理方法_stm32串口超时中断-CSDN博客 STM32-HAL库串口DMA空闲中断的正确使用方式解析SBUS信号_stm32 hal usart2 dma-CSDN博客 #define USART1_RxBuffSize 100 extern DMA_HandleTypeDef hdma_usart1_rx; //此处声明的变量在…

【Linux】进程字段、环境变量与进程地址空间

&#x1f308; 个人主页&#xff1a;谁在夜里看海. &#x1f525; 个人专栏&#xff1a;《C系列》《Linux系列》《算法系列》 ⛰️ 丢掉幻想&#xff0c;准备斗争 目录 一、查看进程字段 1.字段说明 2.进程优先级 二、环境变量 1.概念 2.指令与PATH 3.环境变…

无人机场景 - 目标检测数据集 - 车辆检测数据集下载「包含VOC、COCO、YOLO三种格式」

数据集介绍&#xff1a;无人机场景车辆检测数据集&#xff0c;真实场景高质量图片数据&#xff0c;涉及场景丰富&#xff0c;比如无人机场景城市道路行驶车辆图片、无人机场景城市道边停车车辆图片、无人机场景停车场车辆图片、无人机场景小区车辆图片、无人机场景车辆遮挡、车…

【C++】vector 类模拟实现:探索动态数组的奥秘

&#x1f31f; 快来参与讨论&#x1f4ac;&#xff0c;点赞&#x1f44d;、收藏⭐、分享&#x1f4e4;&#xff0c;共创活力社区。&#x1f31f; 如果你对string&#xff0c;vector还存在疑惑&#xff0c;欢迎阅读我之前的作品 &#xff1a; 之前文章&#x1f525;&#x1f52…

小程序-基于java+SpringBoot+Vue的驾校预约平台设计与实现

项目运行 1.运行环境&#xff1a;最好是java jdk 1.8&#xff0c;我们在这个平台上运行的。其他版本理论上也可以。 2.IDE环境&#xff1a;IDEA&#xff0c;Eclipse,Myeclipse都可以。推荐IDEA; 3.tomcat环境&#xff1a;Tomcat 7.x,8.x,9.x版本均可 4.硬件环境&#xff1a…

初识算法 · 模拟(1)

目录 前言&#xff1a; 替换所有的问号 题目解析 算法原理 算法编写 提莫攻击 题目解析 算法原理 算法编写 外观数列 题目解析 算法原理 算法编写 前言&#xff1a; ​本文的主题是模拟&#xff0c;通过三道题目讲解&#xff0c;一道是提莫攻击&#xff0c;一道是…

使用 Vue 和 Create-Vue 构建工程化前端项目

目录 前言1. 工程化的意义与 Vue 的生态支持2. 搭建 Vue 工程化项目2.1 环境准备2.2 使用 create-vue 创建项目2.2.1 初始化项目2.2.2 安装依赖2.2.3 本地运行 3. Vue 项目的目录结构解析4. Vue 开发流程详解4.1 项目入口与根组件4.1.1 main.js 的作用4.1.2 App.vue 的结构 4.2…

Android中的AMS(Activity Manager Service)详解

Android中的AMS&#xff08;Activity Manager Service&#xff09;详解 AMS (Activity Manager Service) 是 Android 系统中非常核心的服务之一&#xff0c;它负责管理应用程序的生命周期、任务栈、进程、广播、服务等功能。AMS 是整个 Android Framework 的调度中心&#xff…

31.3 XOR压缩和相关的prometheus源码解读

本节重点介绍 : xor 压缩value原理xor压缩过程讲解xor压缩prometheus源码解读xor 压缩效果 xor 压缩value原理 原理:时序数据库相邻点变化不大&#xff0c;采用异或压缩float64的前缀和后缀0个数 xor压缩过程讲解 第一个值使用原始点存储计算和前面的值的xor 如果XOR值为0&…

UNIAPP发布小程序调用讯飞在线语音合成+实时播报

语音合成能够将文字转化为自然流畅的人声&#xff0c;提供100发音人供您选择&#xff0c;支持多语种、多方言和中英混合&#xff0c;可灵活配置音频参数。广泛应用于新闻阅读、出行导航、智能硬件和通知播报等场景。 在当下大模型火爆的今日&#xff0c;语音交互页离不开语音合…

【蓝牙协议栈】【BLE】【BAS】精讲蓝牙电池服务

1. 蓝牙电池服务(Bluetooth Battery Service)概念 蓝牙电池服务是蓝牙设备与其他设备通信时用于报告其剩余电池电量的标准服务。它让用户能够随时了解蓝牙设备(如无线耳机、智能手表、蓝牙鼠标/键盘等)的电池状态,从而方便地管理这些设备的续航与电源使用。 BAS通常用于在…

无线迷踪:陈欣的网络之旅

第一章 陈欣是一名资深的网络工程师&#xff0c;工作在一家领先的科技公司。她的生活平静而有序&#xff0c;直到有一天&#xff0c;公司的无线网络突然出现了严重的问题。员工们的设备频繁断开连接&#xff0c;无法正常使用。这个问题不仅影响了工作效率&#xff0c;还引起了…

【redis】—— 环境搭建教程

上一节&#xff0c;我们大致了解了Redis的几个重要版本&#xff0c;在本教程中&#xff0c;我们选择了5.0版本&#xff0c;因为5.0已经具备了大部分的功能特性&#xff0c;并且与7.0版本相比&#xff0c;其安装使用过程更为简便。 Redis的官方并不直接支持微软的Windows操作系统…

如何查看python源代码

众所周知&#xff0c;Python内建了许多函数模块&#xff0c;并且我们可能还会安装许多第三方模块等等。 下面以getpass为例查看其源代码。 1.help(getpass) 输入该命令找到file路径&#xff0c;并且可以查看其其提供的功能。 2.利用getpass.__file__查看位置 最后找到该文件…

java笔试练习题笔记(10)

关于继承和实现说法正确的 是 &#xff1f; ( )A.类可以实现多个接口&#xff0c;接口可以继承&#xff08;或扩展&#xff09;多个接口 B.类可以实现多个接口&#xff0c;接口不能继承&#xff08;或扩展&#xff09;多个接口 C.类和接口都可以实现多个接口 D.类和接口都不…

前端开发之打印功的使用和实例(vue-print-nb)

通过插件来进行实现 前言效果图1、安装插件vue2vue32、 引入Vue项目2、 使用2.1、在项目中创建按钮并且使用v-print绑定绑定打印事件2.2、编写要打印的内容,给内容附加唯一的id2.3、绑定的时间的方法和参数3、整体代码(此代码是通过vue3来进行实现的但是逻辑都是一样的)前言…

NavVis VLX3的精度怎么去进行验证?【上海沪敖3D】

01、精度评价现状 三维捕捉行业还没有建立一个用于估算或验证移动激光扫描系统精度的统一标准。因此&#xff0c;需要高精度交付成果的专业人士很难相信设备所标注的精度规格&#xff0c;也就很难知道基于SLAM的移动激光扫描系统是否适合当前的项目。 NavVis将通过展示一种严格…

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

题目&#xff1a; 题解&#xff1a; class Solution {public String nearestPalindromic(String n) {long selfNumber Long.parseLong(n), ans -1;List<Long> candidates getCandidates(n);for (long candidate : candidates) {if (candidate ! selfNumber) {if (ans…

ES6标准-Promise对象

目录 Promise对象的含义 Promise对象的特点 Promise对象的缺点 Promise对象的基本用法 Promise对象的简单例子 Promise新建后就会立即执行 Promise对象回调函数的参数 Promise参数不会中断运行 Promise对象的then方法 Promise对象的catch()方法 Promise状态为resolv…

如何利用CSS制作导航菜单

1.利用CSS技术&#xff0c;结合链接和列表&#xff0c;设计并实现“山水之间”页面 示例代码 <!DOCTYPE html> <html><head><meta charset"utf-8"><title>山水之间</title><style>.all{width:900px;}.top{width:900px;h…