官网写的非常详细了
前排提示:注意自己的方法名别和mp提供的方法重名了
new SpringBoot 项目
pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.ssm</groupId>
<artifactId>mybatis_plus</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>mybatis_plus</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--1.数据库驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.17</version>
<scope>runtime</scope>
</dependency>
<!--2.lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--3.mybatis-plus 版本很重要3.0.5-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
<!--4.h2-->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.properties
#数据库连接配置
spring.datasource.username=root
spring.datasource.password=
#mysql5~8 驱动不同driver-class-name 8需要增加时区的配置serverTimezone=UTC
#useSSL=false 安全连接
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
UserMapper
//在对应的接口上面继承一个基本的接口 BaseMapper
public interface UserMapper extends BaseMapper<User> {
//所有CRUD操作都编写完成了
}
User
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
MybatisPlusApplication
@MapperScan("com.ssm.mybatis_plus.mapper")
@SpringBootApplication
public class MybatisPlusApplication {
public static void main(String[] args) {
SpringApplication.run(MybatisPlusApplication.class, args);
}
}
MybatisPlusApplicationTests
@SpringBootTest
class MybatisPlusApplicationTests {
@Autowired
private UserMapper userMapper;
@Test
void contextLoads() {
List<User> users = userMapper.selectList(null);
users.forEach(System.out::println);
}
}
#配置日志 log-impl:日志实现
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
@TableId(type = IdType.AUTO)
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String name;
private Integer age;
private String email;
}
@TableId(type = IdType.INPUT)
@Test//测试插入
public void insertTest(){
User user = new User();
user.setId(1112l);
user.setName("hxl2");
user.setAge(18);
user.setEmail("1600767556@qq.com");
Integer result = userMapper.insert(user); //会帮我们自动生成id
System.out.println(result); //受影响的行数
System.out.println(user); //通过日志发现id会自动回填
}
@Test//测试更新
public void updateTest(){
User user = new User();
user.setId(111L);//怎么改id??
//通过条件自动拼接动态Sql
user.setName("root");
user.setAge(12);
user.setEmail("root@qq.com");
int i = userMapper.updateById(user);//updateById,但是参数是个user
System.out.println(i);
}
private Date createTime;
private Date updateTime;
//字段 字段添加填充内容
@TableField(fill = FieldFill.INSERT)//value = ("create_time"),
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
//字段 字段添加填充内容
@TableField(fill = FieldFill.INSERT)//value = ("create_time"),
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override//插入时的填充策略
public void insertFill(MetaObject metaObject) {
log.info("==start insert ······==");
//setFieldValByName(java.lang.String fieldName, java.lang.Object fieldVal, org.apache.ibatis.reflection.MetaObject metaObject)
this.setFieldValByName("createTIme",new Date(),metaObject);
this.setFieldValByName("updateTime",new Date(),metaObject);
}
@Override//更新时的填充策略
public void updateFill(MetaObject metaObject) {
log.info("==start update ······==");
this.setFieldValByName("updateTime",new Date(),metaObject);
}
}
@Test //测试删除
public void testDelete(){
int result = userMapper.deleteById(111l);
System.out.println("result:"+result);
}
@Test //测试删除
public void testDelete(){
HashMap<String, Object> map = new HashMap<>();
map.put("name","hxl2");
map.put("age",18);
int result = userMapper.deleteByMap(map);
System.out.println("result:"+result);
}
@Test //测试删除
public void testDelete3(){
List<Long> list = Arrays.asList(1l,2l,3l);
int result = userMapper.deleteBatchIds(list);
System.out.println("result:"+result);
}
@Test
public void testSelect(){
User user = userMapper.selectById("4");
System.out.println("user:"+user);
}
@Test
public void testSelect2(){
List<Long> list = Arrays.asList(4l,5l);
List<User> user = userMapper.selectBatchIds(list);
user.forEach(System.out::println);
}
@Test
public void testSelect3(){
HashMap<String, Object> map = new HashMap<>();
map.put("name","Billie");
map.put("age",24);
List<User> result = userMapper.selectByMap(map);
System.out.println("result:"+result);
}
@Test
public void testSelect4(){
// 没有条件,即查询所有
List<User> result = userMapper.selectList(null);
System.out.println("result:"+result);
}
指定mapper位置
mybatis-plus.mapper-locations=/mapper/**
UserMapper.xml
<mapper namespace="com.ssm.mybatis_plus.mapper.UserMapper">
<select id="selectMapById" resultType="map">
select id,name,age,email from user where id = #{id}
</select>
</mapper>
UserMapper
public interface UserMapper extends BaseMapper<User> {
Map<String,Object> selectMapById(Long id);
}
@Test
public void testSelect5(){
// 自定义
Map<String,Object> map = userMapper.selectMapById(4L);
System.out.println("map:"+map);
}
@Test
public void testGetCount(){
long count = userService.count(null);
System.out.println("总记录数"+count);
}
@Test
public void testInsertMore(){
ArrayList<User> list = new ArrayList<>();
for (int i = 0; i <=10 ; i++) {
User user = new User();
user.setAge(20+i);
user.setName("ssm"+i);
list.add(user);
}
boolean b = userService.saveBatch(list);
System.out.println(b);
}
3.5.1版本只需要加
@TableLogic
注解即可
is_deleted
,默认值为0@TableLogic
private Integer isDeleted;
mybatis-plus:
# 设置mybatis-plus全局配置
global-config:
db-config:
logic-delete-field: flag # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
//逻辑删除组件
@Bean
public ISqlInjector sqlInjector(){
return new LogicSqlInjector();
}
@TableName("t_user")
public class User {
}
# 设置mybatis-plus全局配置 db t_开头
global-config:
db-config:
# 设置主键生成策略
id-type: auto
# 设置mybatis-plus全局配置 db t_开头
global-config:
db-config:
# 设置数据库表名
table-prefix: t_
@Test
public void test01(){
// 查询用户名包含a,年龄20-30之间,邮箱信息不为空
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.like("name","a").between("age",10,20).isNotNull("email");
List list = userMapper.selectList(queryWrapper);
list.forEach(System.out::println);
}
@Test
public void test02() {
//按年龄降序查询用户,如果年龄相同则按id升序排列
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper
.orderByDesc("age")
.orderByAsc("id");
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
@Test
public void test03(){
//删除email为空的用户
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.isNull("email");
int result = userMapper.delete(queryWrapper);
System.out.println("受影响的行数:" + result);
}
@Test
public void test04() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper
.like("name", "a")
.gt("age", 20)
.or()
.isNull("email");
User user = new User();
user.setAge(18);
user.setEmail("user@ssm.com");
int result = userMapper.update(user, queryWrapper);
System.out.println("受影响的行数:" + result);
}
@Test
public void test05() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//lambda表达式内的逻辑优先运算
queryWrapper
.like("name", "a")
.and(i -> i.gt("age", 20).or().isNull("email"));
User user = new User();
user.setAge(18);
user.setEmail("user@atguigu.com");
int result = userMapper.update(user, queryWrapper);
System.out.println("受影响的行数:" + result);
}
@Test
public void test06() {
//查询用户信息的username和age字段
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.select("name", "age");
//selectMaps()返回Map集合列表,通常配合select()使用,避免User对象中没有被查询到的列值为null
List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);
maps.forEach(System.out::println);
}
@Test
public void test06() {
//查询id小于等于3的用户信息
(select id from t_user where id <= 3))
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.inSql("id", "select id from user where id <= 3");
List<User> list = userMapper.selectList(queryWrapper);
list.forEach(System.out::println);
}
在真正开发的过程中,组装条件是常见的功能,而这些条件数据来源于用户输入,是可选的,因 此我们在组装这些条件时,必须先判断用户是否选择了这些条件,若选择则需要组装该条件,若 没有选择则一定不能组装,以免影响SQL执行的结果
@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), "name", "a")
.ge(ageBegin != null, "age", ageBegin)
.le(ageEnd != null, "age", ageEnd);
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
@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);
}
@Test
public void test10() {
//组装set子句
LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper
.set(User::getAge, 18)
.set(User::getEmail, "user@ssm.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);
}
添加配置类
@Configuration
@MapperScan("com.ssm.mybatis_plus.mapper")
public class MybatisPlusConfig {
/**
* 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2));
return interceptor;
}
@Bean
public ConfigurationCustomizer configurationCustomizer() {
return configuration -> configuration.setUseDeprecatedExecutor(false);
}
}
@Test
public void testPage() {
/*
SELECT uid AS id,user_name AS name,age,email,is_deleted FROM t_user
WHERE is_deleted=0 LIMIT ?
*/
Page<User> page = new Page<>(1, 3);
userMapper.selectPage(page, null);
System.out.println( page.getRecords());
System.out.println("总页数:" + page.getPages());
System.out.println("总记录数" + page.getTotal());
System.out.println("是否有上一页" + page.hasNext());
System.out.println("是否有下一页:" + page.hasPrevious());
}
UserMapper中定义接口方法
/**
* 根据年龄查询用户列表,分页显示
* @param page 分页对象,xml中可以从里面进行取值,传递参数 Page 即自动分页,必须放在第一位 * @param age 年龄
* @return
*/
Page<User> selectPageVo(@Param("page") Page<User> page, @Param("age") Integer age);
UserMapper.xml中编写SQL
<!--Page<User> selectPageVo(@Param("page") Page<User> page, @Param("age") Integer age);-->
<select id="selectPageVo" resultType="User">
select uid as `id`,user_name as `name`,age,email from t_user where age > #{age}
</select>
测试
@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());
}
场景
一件商品,成本价是80元,售价是100元。老板先是通知小李,说你去把商品价格增加50元。小 李正在玩游戏,耽搁了一个小时。正好一个小时后,老板觉得商品价格增加到150元,价格太 高,可能会影响销量。又通知小王,你把商品价格降低30元。 此时,小李和小王同时操作商品后台系统。小李操作的时候,系统先取出商品价格100元;小王 也在操作,取出的商品价格也是100元。小李将价格加了50元,并将100+50=150元存入了数据 库;小王将商品减了30元,并将100-30=70元存入了数据库。是的,如果没有锁,小李的操作就 完全被小王的覆盖了。 现在商品价格是70元,比成本价低10元。几分钟后,这个商品很快出售了1千多件商品,老板亏1 万多。
数据库中增加product表
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);
添加实体类
@Data
public class Product {
private Long id;
private String name;
private Integer price;
private Integer version;
}
添加mapper
@Repository
public interface ProductMapper extends BaseMapper<Product> {
}
测试
@Test
public void testProduct01() {
Product productLi = productMapper.selectById(1);
System.out.println("小李查询商品价格" + productLi.getPrice());
Product productWang = productMapper.selectById(1);
System.out.println("小王查询商品价格" + productWang.getPrice());
// 小李将商品价格提高50
productLi.setPrice(productLi.getPrice() + 50);
productMapper.updateById(productLi);
// 小王将商品价格降低30
productWang.setPrice(productWang.getPrice() - 30);
productMapper.updateById(productWang);
// 5.老板查询商品价格
Product productBoss = productMapper.selectById(1);
System.out.println("老板查询的商品价格:" + productBoss.getPrice());
}
乐观锁和悲观锁
上面的故事,如果是乐观锁,小王保存价格前,会检查下价格是否被人修改过了。如果被修改过了,则重新取出的被修改后的价格,150元,这样他会将120元存入数据库。 如果是悲观锁,小李取出数据后,小王只能等小李操作完之后,才能对价格进行操作,也会保证 最终的价格是120元。
乐观锁实现流程
数据库中添加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
修改实体类
@Data
public class Product {
private Long id;
private String name;
private Integer price;
@Version
private Integer version;
}
添加乐观锁插件配置
@Configuration
@MapperScan("com.ssm.mybatis_plus.mapper")
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
// 添加分页插件
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
// 添加乐观锁插件
mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return mybatisPlusInterceptor;
}
}
优化流程
@Test
public void testProduct01() {
Product productLi = productMapper.selectById(1);
System.out.println("小李查询商品价格" + productLi.getPrice());
Product productWang = productMapper.selectById(1);
System.out.println("小王查询商品价格" + productWang.getPrice());
// 小李将商品价格提高50
productLi.setPrice(productLi.getPrice() + 50);
productMapper.updateById(productLi);
// 小王将商品价格降低30
productWang.setPrice(productWang.getPrice() - 30);
int result = productMapper.updateById(productWang);
if (result == 0){
Product productNew = productMapper.selectById(1);
productNew.setPrice(productNew.getPrice()-30);
productMapper.updateById(productNew);
}
// 5.老板查询商品价格
Product productBoss = productMapper.selectById(1);
System.out.println("老板查询的商品价格:" + productBoss.getPrice());
}
数据库user表添加字段sex
创建通用枚举类型
@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;
}
}
user实体类添加属性 private SexEnum sex;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
@TableId(type = IdType.INPUT)
private Long id;
private String name;
private Integer age;
private String email;
private SexEnum sex;
//字段 字段添加填充内容
@TableField(fill = FieldFill.INSERT)
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
@TableLogic
private Integer isDeleted;
}
配置扫描通用枚举
mybatis-plus:
mapper-locations: /mapper/**
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 设置mybatis-plus全局配置
global-config:
db-config:
# 设置数据库表名
# table-prefix: t_
# 设置主键生成策略
id-type: auto
# 配置类型别名所对应的包
type-aliases-package: com.ssm.mybatis_plus.pojo
# 扫描枚举的包
type-enums-package: com.atguigu.mybatisplus.enums
测试
@Test
public void test() {
User user = new User();
user.setName("admin");
user.setAge(33);
user.setSex(SexEnum.MALE);
int result = userMapper.insert(user);
System.out.println("result = " + result);
}
添加依赖
<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>
生成类
public class FastAutoGeneratorTest {
public static void main(String[] args) {
// 设置我们需要创建在哪的路径
String path = "E:\\ideaProjectAll\\mybatis_plus";
// 这里我是mysql8 5版本可以换成 jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&useSSL=false
FastAutoGenerator.create("jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true", "root", "数据库密码")
.globalConfig(builder -> {
builder.author("ssm") // 设置作者
// .enableSwagger() // 开启 swagger 模式
.fileOverride() // 覆盖已生成文件
.outputDir(path); // 指定输出目录
})
.packageConfig(builder -> {
builder.parent("com.ssm") // 设置父包名
.moduleName("mybatis_plus") // 设置父包模块名
.pathInfo(Collections.singletonMap(OutputFile.mapperXml, path)); // 设置mapperXml生成路径
})
.strategyConfig(builder -> {
builder.addInclude("user");// 设置需要生成的表名
// .addTablePrefix("t_", "c_"); // 设置过滤表前缀
}).templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker 引擎模板,默认的是Velocity引擎模板
.execute();
}
}
适用于多种场景:纯粹多库、 读写分离、 一主多从、 混合模式等 目前我们就来模拟一个纯粹多库的一个场景,其他场景类似 场景说明: 我们创建两个库,分别为:
mybatis_plus
(以前的库不动)与mybatis_plus_1
(新建),将mybatis_plus
库的product
表移动到mybatis_plus_1
库,这样每个库一张表,通过一个测试用例 分别获取用户数据与商品数据,如果获取到说明多库模拟成功
ProductServiceImpl
@Service
@DS("slave_1") // 要操作的数据源
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements ProductService {
}
ProductService
public interface ProductService extends IService<Product> {
}
UserServiceImpl
@Service
@DS("master") // 指定所操作的数据源
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
UserService
public interface UserService extends IService<User> {
}
多数据源配置
spring:
datasource:
# 配置数据源信息 datasource:
dynamic:
# 设置默认的数据源或者数据源组,默认值即为master
primary: master
# 严格匹配数据源,默认false.true未匹配到指定数据源时抛异常,false使用默认数据源
strict: false
datasource:
master:
url: jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: '数据库密码'
slave_1:
url: jdbc:mysql://localhost:3306/mybatis_plus_1?useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: '数据库密码'
测试
@Test
public void test() {
// 测试
System.out.println(userService.getById(5L));
System.out.println(productService.getById(1L));
}
结果: 1、都能顺利获取对象,则测试成功 2、如果我们实现读写分离,将写操作方法加上主库数据源,读操作方法加上从库数据源,自动切换,是不是就能实现读写分离?