MybatisPlus拓展篇 ╰+哭是因爲堅強的太久メ 2024-03-22 22:28 26阅读 0赞 #### 文章目录 #### * 逻辑删除 * 通用枚举 * 字段类型处理器 * 自动填充功能 * 防全表更新与删除插件 * MybatisX快速开发插件 * * 插件安装 * 逆向工程 * 常见需求代码生成 * 乐观锁 * * 问题引入 * 乐观锁的使用 * 效果测试 * 代码生成器 * 执行SQL分析打印 * 多数据源 ## 逻辑删除 ## * 逻辑删除的操作就是增加一个字段表示这个数据的状态,如果一条数据需要删除,我们通过改变这条数据的状态来实现,这样既可以表示这条数据是删除的状态,又保留了数据以便以后统计。 1. 在表中增加一列字段,表示是否删除的状态,这里使用的字段类型为int类型,通过1表示该条数据可用,0表示该条数据不可用。 ![在这里插入图片描述][4f5e53e755434795aa52bd3f9680cf71.png] 2. 实体类添加一个字段为Integer,用于对应表中的字段 @Data @AllArgsConstructor @NoArgsConstructor public class User extends Model<User> { private Long id; private String name; private Integer age; private String email; @TableLogic(value = "1",delval = "0") private Integer status; } 3. 测试逻辑删除效果 @Test void logicDelete(){ userMapper.deleteById(7L); } ![在这里插入图片描述][52752a4a26b8474ba4382041aa1bc2e1.png] ![在这里插入图片描述][5af5082866324492951db779f42317b9.png] * 还可以通过全局配置来实现逻辑删除的效果 ![在这里插入图片描述][7d5886a2b1f44112a9317a85c21a9c8b.png] ## 通用枚举 ## * 当想要表示一组信息,这组信息只能从一些固定的值中进行选择,不能随意写,在这种场景下,枚举就非常的合适。 1. 在表中添加一个字段,表示性别,使用int来描述,因为int类型可以通过0和1这两个值来表示两个不同的性别 ![在这里插入图片描述][05b8899a13194d3093df35d117014acf.png] 2. 编写枚举类 public enum GenderEnum { MAN(0,"男"), WOMAN(1,"女"); @EnumValue private Integer gender; private String genderName; GenderEnum(Integer gender, String genderName) { this.gender = gender; this.genderName = genderName; } } 1. 实体类添加相关字段 @Data @AllArgsConstructor @NoArgsConstructor public class User extends Model<User> { private Long id; private String name; private Integer age; private String email; private GenderEnum gender; private Integer status; } 1. 添加数据 @Test void enumTest(){ User user = new User(); user.setName("liu"); user.setAge(29); user.setEmail("liu@powernode.com"); user.setGenderEnum(GenderEnum.MAN); user.setStatus(1); userMapper.insert(user); } ![在这里插入图片描述][dd00bccb0bd3427d945f8c56aded6f3e.png] ## 字段类型处理器 ## * 在某些场景下,在实体类中是使用Map集合作为属性接收前端传递过来的数据的,但是这些数据存储在数据库时,使用的是json格式的数据进行存储,json本质是一个字符串,就是varchar类型。那怎么做到实体类的Map类型和数据库的varchar类型的互相转换,这里就需要使用到字段类型处理器来完成。 1. 实体类 @Data @AllArgsConstructor @NoArgsConstructor public class User extends Model<User> { private Long id; private String name; private Integer age; private String email; private GenderEnum gender; private Integer status; private Map<String,String> contact;//联系方式 } 2. 在数据库中添加一个字段,为varchar类型 ![在这里插入图片描述][146a25bf9a8c4815bf6e6cc839397d7e.png] 3. 为实体类添加上对应的注解,实现使用字段类型处理器进行不同类型数据转换 @Data @AllArgsConstructor @NoArgsConstructor @TableName(autoResultMap = true)//查询时将json字符串封装为Map集合 public class User extends Model<User> { private Long id; private String name; private Integer age; private String email; private GenderEnum gender; private Integer status; @TableField(typeHandler = FastjsonTypeHandler.class)//指定字段类型处理器 private Map<String,String> contact;//联系方式 } 4. 引入字段类型处理器依赖Fastjson <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.76</version> </dependency> ## 自动填充功能 ## * 在项目中有一些属性,如果我们不希望每次都填充的话,我们可以设置为自动填充,比如常见的时间,创建时间和更新时间可以设置为自动填充。 1. 在实体类中,添加对应字段,并为需要自动填充的属性指定填充时机 @Data @NoArgsConstructor @AllArgsConstructor @TableName(autoResultMap = true) public class User extends Model<User> { @TableId private Long id; private String name; private Integer age; private String email; private Integer status; private GenderEnum gender; @TableField(typeHandler = FastjsonTypeHandler.class) private Map<String,String> contact; @TableField(fill = FieldFill.INSERT) private Date createTime; @TableField(fill = FieldFill.INSERT_UPDATE) private Date updateTime; } 1. 编写自动填充处理器,指定填充策略 @Component public class MyMetaHandler implements MetaObjectHandler { @Override public void insertFill(MetaObject metaObject) { setFieldValByName("createTime",new Date(),metaObject); setFieldValByName("updateTime",new Date(),metaObject); } @Override public void updateFill(MetaObject metaObject) { setFieldValByName("updateTime",new Date(),metaObject); } } 2. 设置一下mysql时区,更新yml连接配置 set GLOBAL time_zone='+8:00' select NOW(); ![在这里插入图片描述][aa17934956fe429eb10f99f11c8fdbcc.png] ## 防全表更新与删除插件 ## * 在实际开发中,全表更新和删除是非常危险的操作,在MybatisPlus中,提供了插件和防止这种危险操作的发生。 * 实现步骤: 1. 注入MybatisPlusInterceptor类,并配置BlockAttackInnerInterceptor拦截器 @Configuration public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor()); return interceptor; } } 1. 测试全表更新,会出现抛出异常,防止了全表更新 @SpringBootTest public class QueryTest { @Autowired private UserService userService; @Test void allUpdate(){ User user = new User(); user.setId(999L); user.setName("wang"); user.setEmail("wang@powernode.com"); userService.saveOrUpdate(user,null); } } ![在这里插入图片描述][09a373b1c6fe419dbfe63bd9d05f0b66.png] ## MybatisX快速开发插件 ## ### 插件安装 ### * MybatisX是一款IDEA提供的插件,目的是为了我们简化Mybatis以及MybatisPlus框架而生。 * 在IDEA中安装插件 1. 首先选择`File -> Settings->Plugins` ![在这里插入图片描述][27d6a1eb325d438f884c54ea7ae05d48.png] 2. 搜索MybatisX,点击安装 ![在这里插入图片描述][14b2e001dd604cd59df436e2a2169491.png] 3. 重启IDEA,让该插件生效,至此MybatisX插件就安装完毕 -------------------- * 插件安装好以后,我们来看一下插件的功能 1. Mapper接口和映射文件的跳转功能 ![在这里插入图片描述][b2cb015a3f7c4fa9bd4c783783980ab0.png] ![在这里插入图片描述][94ebaa8e51bb4d94b13aed477e8a66cc.png] ### 逆向工程 ### * 逆向工程就是通过数据库表结构,逆向产生Java工程的结构,包括以下几点: 1. 实体类 2. Mapper接口 3. Mapper映射文件 4. Service接口 5. Service实现类 * 实现步骤: 1. 首先使用IDEA连接mysql,填写连接信息,测试连接通过 ![在这里插入图片描述][f5321e012e66436596ce12a3c7592872.png] ![在这里插入图片描述][4b478ff9f85345478c06e9a65f4ffc70.png] 2. 找到表右键,选择插件的逆向工程选项 ![在这里插入图片描述][c1553f76d5f8461e8c2f71e366af1d70.png] 3. 编写逆向工程配置信息 ![在这里插入图片描述][e55b014727794d12821d422bfb688d1b.png] 4. 编写生成信息 ![在这里插入图片描述][d7173e0c97b242cea37716d01e8cc03d.png] ### 常见需求代码生成 ### * 虽然Mapper接口中提供了一些常见方法,我们可以直接使用这些常见的方法来完成sql操作,但是对于实际场景中各种复杂的操作需求来说,依然是不够用的,所以MybatisX提供了更多的方法,以及可以根据这些方法直接生成对应的sql语句,这样使得开发变得更加的简单。 * 可以根据名称联想常见的操作 @Mapper public interface UserMapper extends BaseMapper<User> { //添加操作 int insertSelective(User user); //删除操作 int deleteByNameAndAge(@Param("name") String name, @Param("age") Integer age); //修改操作 int updateNameByAge(@Param("name") String name, @Param("age") Integer age); //查询操作 List<User> selectAllByAgeBetween(@Param("beginAge") Integer beginAge, @Param("endAge") Integer endAge); } * 在映射配置文件中,会生成对应的sql,并不需要我们编写 <insert id="insertSelective"> insert into powershop_user <trim prefix="(" suffix=")" suffixOverrides=","> <if test="id != null">id,</if> <if test="name != null">name,</if> <if test="age != null">age,</if> <if test="email != null">email,</if> </trim> values <trim prefix="(" suffix=")" suffixOverrides=","> <if test="id != null">#{id,jdbcType=BIGINT},</if> <if test="name != null">#{name,jdbcType=VARCHAR},</if> <if test="age != null">#{age,jdbcType=INTEGER},</if> <if test="email != null">#{email,jdbcType=VARCHAR},</if> </trim> </insert> <delete id="deleteByNameAndAge"> delete from powershop_user where name = #{name,jdbcType=VARCHAR} AND age = #{age,jdbcType=NUMERIC} </delete> <update id="updateNameByAge"> update powershop_user set name = #{name,jdbcType=VARCHAR} where age = #{age,jdbcType=NUMERIC} </update> <select id="selectAllByAgeBetween" resultMap="BaseResultMap"> select <include refid="Base_Column_List"/> from powershop_user where age between #{beginAge,jdbcType=INTEGER} and #{endAge,jdbcType=INTEGER} </select> ## 乐观锁 ## ### 问题引入 ### * 并发请求就是在同一时刻有多个请求同时请求服务器资源,如果是获取信息,没什么问题,但是如果是对于信息做修改操作呢,那就会出现问题。 * 比如目前商品的库存只剩余1件了,这个时候有多个用户都想要购买这件商品,都发起了购买商品的请求,那么能让这多个用户都购买到么,肯定是不行的,因为多个用户都买到了这件商品,那么就会出现超卖问题,库存不够是没法发货的。所以在开发中就要解决这种超卖的问题。 ![在这里插入图片描述][534c137c45f547318a4b5616ecad4b99.png] -------------------- 核心问题:一个请求在执行的过程中,其他请求不能改变数据,如果是一次完整的请求,在该请求的过程中其他请求没有对于这个数据产生修改操作,那么这个请求是能够正常修改数据的。如果该请求在改变数据的过程中,已经有其他请求改变了数据,那该请求就不去改变这条数据 ![在这里插入图片描述][c97e2ecfd7a04e2bb14310302f3c7fed.png] * 想要解决这类问题,最常见的就是加锁的思想,锁可以用验证在请求的执行过程中,是否有数据发生改变。 * 常见的数据库锁类型有两种,悲观锁和乐观锁。一次完成的修改操作是,先查询数据,然后修改数据。 * 悲观锁:悲观锁是在查询的时候就锁定数据,在这次请求未完成之前,不会释放锁。等到这次请求完毕以后,再释放锁,释放了锁以后,其他请求才可以对于这条数据完成读写。 * 悲观锁的优缺点:能够保证读取到的信息就是当前的信息,保证了信息的正确性,但是并发效率很低。 * **在实际开发中使用悲观锁的场景很少,因为在并发时我们是要保证效率的。** * 乐观锁:乐观锁是通过表字段完成设计的,核心思想是,在读取的时候不加锁,其他请求依然可以读取到这个数据,在修改的时候判断一个数据是否有被修改过,如果有被修改过,那本次请求的修改操作失效。 * 具体的通过sql是实现 Update 表 set 字段 = 新值,version = version + 1 where version = 1 * 这样做的操作是不会对于数据读取产生影响,并发的效率较高。但是可能目前看到的数据并不是真实信息数据,是被修改之前的,但是在很多场景下是可以容忍的,并不是产生很大影响。 ### 乐观锁的使用 ### 1. 在数据库表中添加一个字段version,表示版本,默认值是1 ![在这里插入图片描述][cdb00f9dd7fa4fbda4d17104bb72b5bb.png] 2. 找到实体类,添加对应的属性,并使用@Version标注为这是一个乐观锁字段信息 @Data @NoArgsConstructor @AllArgsConstructor public class User { private Long id; private String name; private Integer age; private String email; @Version private Integer version; } 1. 通过拦截器的配置,让每条修改的sql语句在执行的时候,都加上版本控制的功能 @Configuration public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor(){ MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); return interceptor; } } ### 效果测试 ### * 接下来模拟一下,当出现多个修改请求的时候,是否能够做到乐观锁的效果。 * 乐观锁的效果是,一个请求在修改的过程中,是允许另一个请求查询的,但是修改时会通过版本号是否改变来决定是否修改,如果版本号变了,证明已经有请求修改过数据了,那这次修改不生效,如果版本号没有发生变化,那就完成修改。 -------------------- @Test void updateTest2(){ //模拟操作1的查询操作 User user1 = userMapper.selectById(6L); //模拟操作2的查询操作 User user2 = userMapper.selectById(6L); //模拟操作2的修改操作 user2.setName("lisi"); userMapper.updateById(user2); //模拟操作1的修改操作 user1.setName("zhangsan"); userMapper.updateById(user1); } * 代码的执行过程 1. 操作1的查询:此时版本为2 ![在这里插入图片描述][6f8f046d1bcc4bc7bd3ea8d55c38c876.png] 2. 操作2的查询:此时版本为2 ![在这里插入图片描述][4c470aaf56e24706b182598f6d4823f0.png] 3. 操作2的修改:此时检查版本,版本没有变化,所以完成修改,并将版本改为3 ![在这里插入图片描述][5fc83f40ea5e49e1817efe11c80df4fc.png] 4. 操作1的修改:此时检查版本,版本已经有最初获取的版本信息发生了变化,所以杜绝修改 ![在这里插入图片描述][b70b756ca9b642d2baf4f70c21b9755b.png] ## 代码生成器 ## * 代码生成器和逆向工程的区别在于,代码生成器可以生成更多的结构,更多的内容,允许我们能够配置生成的选项更多。 1. 引入依赖 <!--代码生成器依赖--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.5.3</version> </dependency> <!--freemarker模板依赖--> <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.31</version> </dependency> 2. 编写代码生成器代码 * 示例一: public class CodeGenerator { /* SELECT table_name FROM information_schema.tables WHERE table_schema = 'xxx' ORDER BY table_name DESC; */ public static String scanner(String tip) { Scanner scanner = new Scanner(System.in); StringBuilder help = new StringBuilder(); help.append("请输入" + tip + ":"); System.out.println(help.toString()); if (scanner.hasNext()) { String ipt = scanner.next(); if (!ipt.equals("")) { return ipt; } } throw new MybatisPlusException("请输入正确的" + tip + "!"); } public static void main(String[] args) { // 代码生成器 AutoGenerator mpg = new AutoGenerator(); // 全局配置 GlobalConfig gc = new GlobalConfig(); String projectPath = System.getProperty("user.dir"); gc.setOutputDir(projectPath + "/src/main/java");//设置代码生成路径 gc.setFileOverride(true);//是否覆盖以前文件 gc.setOpen(false);//是否打开生成目录 gc.setAuthor("xxx");//设置项目作者名称 gc.setIdType(IdType.AUTO);//设置主键策略 gc.setBaseResultMap(true);//生成基本ResultMap gc.setBaseColumnList(true);//生成基本ColumnList gc.setServiceName("%sService");//去掉服务默认前缀 gc.setDateType(DateType.ONLY_DATE);//设置时间类型 mpg.setGlobalConfig(gc); // 数据源配置 DataSourceConfig dsc = new DataSourceConfig(); dsc.setUrl("jdbc:mysql://localhost:3306/care_home?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8"); dsc.setDriverName("com.mysql.cj.jdbc.Driver"); dsc.setUsername("root"); dsc.setPassword("xxx"); mpg.setDataSource(dsc); // 包配置 PackageConfig pc = new PackageConfig(); pc.setParent("com.test"); pc.setMapper("com/test/mapper"); pc.setXml("mapper.xml"); pc.setEntity("com/test/pojo"); pc.setService("com/test/service"); pc.setServiceImpl("service.impl"); pc.setController("com/test/controller"); mpg.setPackageInfo(pc); // 策略配置 StrategyConfig sc = new StrategyConfig(); sc.setNaming(NamingStrategy.underline_to_camel); sc.setColumnNaming(NamingStrategy.underline_to_camel); sc.setEntityLombokModel(true); //自动lombok sc.setRestControllerStyle(true); sc.setControllerMappingHyphenStyle(true); sc.setLogicDeleteFieldName("deleted");//设置逻辑删除 //设置自动填充配置 TableFill gmt_create = new TableFill("create_time", FieldFill.INSERT); TableFill gmt_modified = new TableFill("update_time", FieldFill.INSERT_UPDATE); ArrayList<TableFill> tableFills=new ArrayList<>(); tableFills.add(gmt_create); tableFills.add(gmt_modified); sc.setTableFillList(tableFills); //乐观锁 sc.setVersionFieldName("version"); sc.setRestControllerStyle(true);//驼峰命名 // sc.setTablePrefix("tbl_"); 设置表名前缀 sc.setInclude(scanner("表名,多个英文逗号分割").split(",")); mpg.setStrategy(sc); // 生成代码 mpg.execute(); } } * 示例二: /** * @author 缘友一世 * date 2023/7/19-16:09 */ public class CodeGenerator { public static void main(String[] args) { String projectPath = System.getProperty("user.dir"); // 获取当前项目的绝对路径 String MainPath=projectPath+"/src/main/java"; String url="jdbc:mysql://localhost:3306/db2?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useUnicode=true&useSSL=false"; String username="root"; String password="xxx"; String author="yang"; String moduleName="system"; String mapperLocation=projectPath+"/src/main/resources/mapper/"+moduleName; String parentPackageName="com.yang"; /* CREATE TABLE x_user ( id int(11) NOT NULL AUTO_INCREMENT, username varchar(50) NOT NULL , password varchar(100) DEFAULT NULL, email varchar(50) DEFAULT NULL, phone varchar(20) DEFAULT NULL, status int(1) DEFAULT NULL, avatar varchar(200) DEFAULT NULL, deleted INT(1) DEFAULT 0, PRIMARY KEY (id) )ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; DELETE FROM x_user WHERE id > 20; */ /* * SELECT table_name FROM information_schema.tables WHERE table_schema = 'xxx' ORDER BY table_name DESC; */ String tables="x_user"; FastAutoGenerator.create(url, username, password) .globalConfig(builder -> { builder.author(author) // 设置作者 //.enableSwagger() // 开启 swagger 模式 //.fileOverride() // 覆盖已生成文件 .outputDir(MainPath); // 指定输出目录 }) .dataSourceConfig(builder -> builder.typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> { int typeCode = metaInfo.getJdbcType().TYPE_CODE; if (typeCode == Types.SMALLINT) { // 自定义类型转换 return DbColumnType.INTEGER; } return typeRegistry.getColumnType(metaInfo); })) .packageConfig(builder -> { builder.parent(parentPackageName) // 设置父包名 .moduleName(moduleName) // 设置父包模块名 .pathInfo(Collections.singletonMap(OutputFile.xml, mapperLocation)); // 设置mapperXml生成路径 }) .strategyConfig(builder -> { builder.addInclude(tables) // 设置需要生成的表名 .addTablePrefix("x_"); // 设置过滤表前缀 }) .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板 .execute(); } } ## 执行SQL分析打印 ## * 可以使用MybatisPlus提供的SQL分析打印的功能,来获取SQL语句执行的时间。 1. 由于该功能依赖于p6spy组件,所以需要在pom.xml中先引入该组件 <dependency> <groupId>p6spy</groupId> <artifactId>p6spy</artifactId> <version>3.9.1</version> </dependency> 2. 在application.yml中进行配置 spring: datasource: driver-class-name: com.p6spy.engine.spy.P6SpyDriver url: jdbc:p6spy:mysql 1. 在resources下,创建 spy.properties配置文件 #3.2.1以上使用modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory # 自定义日志打印 logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger #日志输出到控制台 appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger # 使用日志系统记录 sql #appender=com.p6spy.engine.spy.appender.Slf4JLogger # 设置 p6spy driver 代理 deregisterdrivers=true # 取消JDBC URL前缀 useprefix=true # 配置记录 Log 例外,可去掉的结果集error,info,batch,debug,statement,commit,rollback,result,resultset. excludecategories=info,debug,result,commit,resultset # 日期格式 dateformat=yyyy-MM-dd HH:mm:ss # 实际驱动可多个 #driverlist=org.h2.Driver # 是否开启慢SQL记录 outagedetection=true # 慢SQL记录标准 2 秒 outagedetectioninterval=2 1. 测试:执行查询所有的操作,可以看到sql语句的执行时间 ![在这里插入图片描述][9d050310dd2e46f1a8138162ce394037.png] ## 多数据源 ## * 分库分表:当一个项目的数据库的数据十分庞大时,在完成SQL操作的时候,需要检索的数据就会更多,我们会遇到性能问题,会出现SQL执行效率低的问题。 * 针对这个问题,我们的解决方案是,将一个数据库中的数据,拆分到多个数据库中,从而减少单个数据库的数据量,从分摊访问请求的压力和减少单个数据库数据量这两个方面,都提升了效率。 -------------------- * 在MybatisPlus中,如何演示数据源切换的效果 1. 先创建一个新的模块,将之前模块中的内容复制过来 ![在这里插入图片描述][a2bd44746cf44ea992ae365a6f3b4041.png] 2. 引入依赖 <dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <version>3.1.0</version> </dependency> 1. 创建新的数据库,提供多数据源环境 ![在这里插入图片描述][063bc48e521149b0a5e52a542af6fe6c.png] ![在这里插入图片描述][c53f23bf38ce4da2a2da7bc32c0e2543.png] 2. 编写配置文件,指定多数据源信息 spring: datasource: dynamic: primary: master strict: false datasource: master: username: root password: xxx url: jdbc:mysql://localhost:3306/mybatisplus?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=false driver-class-name: com.mysql.cj.jdbc.Driver slave_1: username: root password: xxx url: jdbc:mysql://localhost:3306/mybatisplus2?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=false driver-class-name: com.mysql.cj.jdbc.Driver 1. 创建多个Service,分别使用@DS注解描述不同的数据源信息 @Service @DS("master") public class UserServiceImpl extends ServiceImpl<UserMapper,User> implements UserService { } @Service @DS("slave_1") public class UserServiceImpl2 extends ServiceImpl<UserMapper, User> implements UserService{ } 1. 测试service多数据源环境执行结果 @SpringBootTest class Mp03ApplicationTests { @Autowired private UserServiceImpl userServiceImpl; @Autowired private UserServiceImpl2 userServiceImpl2; @Test public void select(){ User user = userServiceImpl.getById(1L); System.out.println(user); } @Test public void select2(){ User user = userServiceImpl2.getById(1L); System.out.println(user); } } 1. 观察测试结果,发现结果可以从两个数据源中获取 ![在这里插入图片描述][d4fa8a35a15746dc8487fe2163e2b585.png] ![在这里插入图片描述][1088360ca0414ca6bbd45295b518d5cc.png] [4f5e53e755434795aa52bd3f9680cf71.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/14/334941d5151b4aaa92ca1625a8dd2e6a.png [52752a4a26b8474ba4382041aa1bc2e1.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/14/b1656e379a014eee90dae55ea082ab02.png [5af5082866324492951db779f42317b9.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/14/9ee1f0ccabe04b6f91b7d58dff709838.png [7d5886a2b1f44112a9317a85c21a9c8b.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/14/b7da7b6b2f1944bc93b9f82623658716.png [05b8899a13194d3093df35d117014acf.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/14/36ac6e86bd2a47eebaef377db63d7f2c.png [dd00bccb0bd3427d945f8c56aded6f3e.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/14/a9c9e6cc6b334f7ca16379d34d1e889f.png [146a25bf9a8c4815bf6e6cc839397d7e.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/14/e2ed7738a49c445ab2b8fc415c06a33e.png [aa17934956fe429eb10f99f11c8fdbcc.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/14/a7fce8926d654d7faff0b3180c315969.png [09a373b1c6fe419dbfe63bd9d05f0b66.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/14/7d4d4978f76d485d934109e93d6b45cf.png [27d6a1eb325d438f884c54ea7ae05d48.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/14/8d58e7ce6efc435a84f88d035c3f74af.png [14b2e001dd604cd59df436e2a2169491.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/14/c7e17dd78cb240efb7d99e3abd1ad776.png [b2cb015a3f7c4fa9bd4c783783980ab0.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/14/e2c4ce96bd12441b95fad4ede4db3693.png [94ebaa8e51bb4d94b13aed477e8a66cc.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/14/25e17c16d28d4a12b25700efe3a8b42e.png [f5321e012e66436596ce12a3c7592872.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/14/e48698392da14c96b0fdd735237f2cae.png [4b478ff9f85345478c06e9a65f4ffc70.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/14/34428560a63344a2939a2fbc14419752.png [c1553f76d5f8461e8c2f71e366af1d70.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/14/f8e31e810bfa42fb8200299f02cff871.png [e55b014727794d12821d422bfb688d1b.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/14/64bc2f0edf0b41968e46feeeb7950344.png [d7173e0c97b242cea37716d01e8cc03d.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/14/7e0a4340d9744e159d0ce204005490a8.png [534c137c45f547318a4b5616ecad4b99.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/14/be2d755bbca64eb5aa642fb61721d250.png [c97e2ecfd7a04e2bb14310302f3c7fed.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/14/6ccfcbb09d0a4b9bad239cd8e500715d.png [cdb00f9dd7fa4fbda4d17104bb72b5bb.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/14/8a68fc319f3f4fb78fe40ccc5a57bf9c.png [6f8f046d1bcc4bc7bd3ea8d55c38c876.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/14/5b086c5e00f34424b297287cced6775d.png [4c470aaf56e24706b182598f6d4823f0.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/14/efe9399e2af44880b06842edd6371164.png [5fc83f40ea5e49e1817efe11c80df4fc.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/14/f83feff3341e4ac8bbbde480b0c87459.png [b70b756ca9b642d2baf4f70c21b9755b.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/14/c5c8f579efb2466ba06661f580d03aaf.png [9d050310dd2e46f1a8138162ce394037.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/14/31b101cf6b6d45e48bd1a3c9d503278c.png [a2bd44746cf44ea992ae365a6f3b4041.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/14/94a4818bcabe4b92b4339b57a3787c6c.png [063bc48e521149b0a5e52a542af6fe6c.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/14/fa702029034048e1aa7fb84e55cf3888.png [c53f23bf38ce4da2a2da7bc32c0e2543.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/14/720ee07008584ae6a5a277ff5b4072d2.png [d4fa8a35a15746dc8487fe2163e2b585.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/14/2ce1b4a8ba8f45c38b88d841b8949162.png [1088360ca0414ca6bbd45295b518d5cc.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/14/34ff558ad434420ebef9f1f9b2a10abc.png
还没有评论,来说两句吧...