• MybatisPlus


    MybatisPlus学习笔记

    • 一、MyBatisPlus基础
      • 1.1 MyBatisPlus介绍
      • 1.2 MyBatisPlus入门
      • 2. 继承BaseMapper<对应的想要返回类的类名>
      • 1.3 常用注解
        • 1.3.1 @TableName
        • 1.3.2 @Tableid
        • 1.3.3 @TableField
      • 1.4 常用配置
    • 二、条件构造器
      • 2.2 自定义SQL
      • 2.3 Service接口
      • 2.4 基于Restful风格实现下列小练习
      • 2.5 IService的Lambda查询
      • 2.6 批量删除
    • 三、拓展业务
      • 3.1 静态工具
      • 3.2 逻辑删除
      • 3.3、枚举处理器
      • 3.4 JSON处理器
      • 3.5 分页插件功能
      • 3.6 通用分页实体

    一、MyBatisPlus基础

    1.1 MyBatisPlus介绍

    MyBatisPlus:就是相当于开发mybatis的应用小能手,能够简化我们的开发

    1.2 MyBatisPlus入门

    1. 导入对应的start包,可以在这里找到对应想要的坐标信息
    <!-- https://mvnrepository.com/artifact/com.baomidou/mybatisplus-spring-boot-starter -->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatisplus-spring-boot-starter</artifactId>
        <version>1.0.5</version>
    </dependency> 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2. 继承BaseMapper<对应的想要返回类的类名>

    @Mapper
    public interface BookDao extends BaseMapper<Book> { 
    } 
    
    • 1
    • 2
    • 3

    测试:

    @SpringBootTest
    class MyBatisPlusQuickstartApplicationTests {
    
        @Autowired
        private BookDao bookDao;
        @Test
        void contextLoads() {
            System.out.println(bookDao.selectById(14));
            List<Book> books = bookDao.selectList(null);
            System.out.println(books);
        } 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    1.3 常用注解

    MvBatisPlus通过扫描实体类,并基于反射获取实体类信息作为数据库表信息

    当符合以下规定就不用进行配置,MybaitisPlus会自动识别:

    1. 类名驼峰转下划线作为表名
    2. 名为id的字段作为主键
    3. 变量名驼峰转下划线作为表的字段名

    在这里插入图片描述

    • MybatisPlus中比较常用的几个注解如下
      • @TableName:用来指定表名
      • @Tableid:用来指定表中的主键字段信息
      • @TableField:用来指定表中的普通字段信息

    1.3.1 @TableName

    当表名和类的名字不相符合,那么就要添加这个注解来进行消除错误

    在这里插入图片描述

    1.3.2 @Tableid

    如果主键不存在或者主键的名字不为id那么就要加上这个注解

    • ldType枚举:
      • AUTO: 数据库自增长
      • INPUT : 通过set方法自行输入
      • ASSIGN ID : 分配 ID,接口ldentifierGenerator的方法nextld来生成id默认实现类为DefaultldentifierGenerator雪花算法

    1.3.3 @TableField

    • 常用的场景:
      • 成员变量名与数据库字段名不一致
      • 成员变量名以is开头,且是布尔值
      • 成员变量名与数据库关键字冲突
      • 成员变量不是数据库字段
    @TableName("tbl_book")
    public class Book {
        @TableId(type = IdType.AUTO)
        private Integer id;
        @TableField("`type`")//进行转意
        private String type;
        @TableField(exist = false) //将其表面部位数据库当中的字段
        private String description;
        private String name; 
        public Book() {
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在这里插入图片描述

    1.4 常用配置

    mybatis-plus:
                type-aliases-package: com.itheima.mp.domain.po # 别名扫描包
                mapper-locations: "classpath*:/mapper/**/*.xml" # Mapper.xml文件地址,默认值
                configuration:
                  map-underscore-to-camel-case: true # 是否开启下划线和驼峰的映射
                  cache-enabled: false # 是否开启二级缓存
                global-config:
                  db-config:
                    id-type: assign_id # id为雪花算法生成
                    update-strategy: not_null # 更新笑略:只更新非空字段
                    table-prefix: tbl_ #表面所有表面前面都加一个tbl_
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    这些配置不需要记下来,只需要我们需要配置的时候在官网上面进行查询

    二、条件构造器

    MyBatisPlus支持各种复杂的where条件,可以满足日常开发的所有需求

    在这里插入图片描述

    @Test
        void testQueryWrapper(){
            //首先编写了查询的条件
           QueryWrapper<Book> wrapper = new QueryWrapper<Book>()
                   .select("id","username","info","balance")
                   .like("username","o")
                   .ge("balance","1000");
    
           //进行查询操作
           List<Book> books = bookDao.selectList(wrapper);
           books.forEach(System.out::println);
       }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    利用LambdaQueryWrapper书写:

    @Test
        void testQueryWrapper(){
    
            LambdaQueryWrapper<Book> wrapper = new LambdaQueryWrapper<Book>()
                    .select(User::getId, User::getUsername, User::getInfo, User::getBalance)
                    .like(User::getUsername, "o")
                    .ge(User::getBalance, "1000");
    
            //进行查询操作
            List<Book> books = bookDao.selectList(wrapper);
            books.forEach(System.out::println);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在这里插入图片描述

    @Test
        void testQueryWrapper2(){
           //首先编写条件
            QueryWrapper<Book> wrapper = new QueryWrapper<Book>()
                   .eq("name", "jack");
           //然后创建需要修改的对象属性
           User user = new User;
           user.setBalance(2000);
           bookDao.update(user,wrapper);
       }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述

      @Test
        void testUpdateWrapper(){
            //首先编写条件
            List<Long> ids=List.of(1L,4L,2L);
            UpdateWrapper<Book> wrapper = new UpdateWrapper<Book>()
                    .setSql("balance = balance -200")
                    .in("id", ids);
            //然后创建需要修改的对象属性
            User user = new User;
            user.setBalance(2000);
            bookDao.update(user,wrapper);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    2.2 自定义SQL

    我们可以利用MyBatisPlus的Wrapper来构建复杂的Where条件,然后自己定义SQL语句中剩下的部分

    基本步骤:

    1. 基于Wrapper构建where条件
     @Test
        void testQueryWrappers(){
            List<Long> ids=List.of(1L,4L,2L);
            int mount = 200;
            //编写条件
            QueryWrapper<User> wrapper = new QueryWrapper<User>()
                    .in("id", ids);
    
            bookDao.updateIds(wrapper,mount);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    1. 在mapper方法参数中用Param注解声明wrapper变量名称,必须是ew
    @Update("update tb_user set balance = balance - #{mount} ${ew.customSqlSegment}")
    void updateIds(@Param("ew") QueryWrapper<User> wrapper,@Param("mount") int mount);
    
    • 1
    • 2

    2.3 Service接口

    利用的就是MybatisPlus创建好的IServiceServiceImpl两个父类,service和serviceImpl分别继承上述两个父类,就拥有了一系列MybaitisPlus封装好的方法

    步骤:

    • 创建好BookService和 BookServiceImpl类,然后进行继承
    @Service
    public class BookServiceImpl extends ServiceImpl<BookDao, Book> implements BookService {
    
    }
    
    • 1
    • 2
    • 3
    • 4
    public interface BookService extends IService<Book> {
    } 
    
    • 1
    • 2
    • 测试
     /*
        * 用来测试service
        * */
        @Autowired
        private BookService service;
        @Test
        void testService(){
            Book book = new Book();
            book.setName("大学英语");
            book.setDescription("学习英语成就背时人生!!!!");
            book.setType("四级英语");
            service.save(book);
        }
    
        /*
        * 查询
        * */
    
        @Test
        void testServiceQuery(){
            List<Book> books = service.listByIds(List.of(15L, 16L, 22L));
            books.forEach(System.out::println);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    2.4 基于Restful风格实现下列小练习

    在这里插入图片描述

    1. 新增用户
    @Autowired
        private AccountService accountService;
    
        /*增添用户*/
        @PostMapping
        public void InsertAccount(Account account){
            accountService.save(account);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    1. 删除用户
    @DeleteMapping("/{id}")
        public void DeleteAccount(@PathVariable Long id){
            accountService.removeById(id);
        }
    
    • 1
    • 2
    • 3
    • 4
    1. 根据ID查询用户
    @GetMapping("/{id}")
        public Account QueryAccountById(@PathVariable Long id){
            return accountService.getById(id);
        }
    
    • 1
    • 2
    • 3
    • 4
    1. 根据IDS批量查询
    @GetMapping
        public List<Account> QueryAccountByIds(@RequestParam("ids") List<Long> ids){
            return accountService.listByIds(ids);
        }
    
    • 1
    • 2
    • 3
    • 4
    1. 根据Id扣减余额

    Controller中的代码:

     @PutMapping("/{id}/deduction/{money}")
        public void deductionMoneyById(@PathVariable Long id ,@PathVariable Long money){
            accountService.deductionMoney(id,money);
        }
    
    • 1
    • 2
    • 3
    • 4

    AccountService中的代码:

    public interface AccountService extends IService<Account> {
        void deductionMoney(Long id, Long money);
    }
    
    • 1
    • 2
    • 3

    AccountServiceImpl当中的代码:

     public void deductionMoney(Long id, Long money) {
            //查询用户
            Account account = this.getById(id);
            //验证用户信息
            if (account == null || account.getStatus() == 0){
                //代表的是不能进行减去操作
                throw new RuntimeException("用户不存在或账户冻结");
            }
            //查询是否金额能够减去
            if(account.getMoney() < money ){
                throw new RuntimeException("用户的余额不足");
            }
            //进行金额消减
            baseMapper.deductionMoneyById(id,money);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    AccountDao当中的代码:

    @Mapper
    public interface AccountDao extends BaseMapper<Account> { 
        @Update("update tbl_account set money = money - #{money} where id = #{id};")
        void deductionMoneyById(@Param("id") Long id,@Param("money") Long money); 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2.5 IService的Lambda查询

    在这里插入图片描述

    /*lambda查询用户,Condition是利用一个类来接收传输过来的数据*/
        @GetMapping("/list")
        public List<Account> QueryAccountLambda(Condition condition){
            List<Account> accounts = accountService.lambdaQuerys(
                    condition.getUsername(),
                    condition.getStatus(),
                    condition.getMaxBalance(),
                    condition.getMinBalance()
            );
            return accounts;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    @Override
        public List<Account> lambdaQuerys(
                @Param("username") String username,
                @Param("status") Integer status,
                @Param("maxBalance") Long maxBalance,
                @Param("minBalance") Long minBalance)
        {
            return lambdaQuery()
                    .eq(username != null,Account::getUsername, username)
                    .eq(status != null ,Account::getStatus, status)
                    .le(maxBalance!= null,Account::getMoney, maxBalance)
                    .ge(minBalance!= null,Account::getMoney, minBalance)
                    .list();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    在这里插入图片描述
    在Service当中实现的逻辑处理:

     @Override
        public void deductionMoney(Long id, Long money) {
            //查询用户
            Account account = this.getById(id);
            //验证用户信息
            if (account == null || account.getStatus() == 0){
                //代表的是不能进行减去操作
                throw new RuntimeException("用户不存在或账户冻结");
            }
            //查询是否金额能够减去
            if(account.getMoney() < money ){
                throw new RuntimeException("用户的余额不足");
            }
            //进行金额消减
            long deMoney = account.getMoney() - money;
            System.out.println(deMoney);
            lambdaUpdate()
                    .set(Account::getMoney,deMoney)
                    .set(deMoney == 0,Account::getStatus,0)
                    .eq(Account::getId,id)
                    .eq(Account::getMoney,account.getMoney()) //乐观锁,防止多线程并发问题
                    .update(); //必须要进行更新操作
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    2.6 批量删除

    在这里插入图片描述

     @Autowired
        private AccountService service;
    
        /*首先创建一个自动增加的数据*/
        private Account buildAccount(int i){
            Account account = new Account();
            account.setMoney(2000+i);
            account.setStatus(1);
            account.setUsername("zhangsan" + i);
            return account;
        }
    
    
        @Test
        void InsertIds() {
            //首先创建一个容量为1000的list容器
            List<Account> list = new ArrayList<>(1000);
    
            //记录下当前时间
            long now = System.currentTimeMillis();
    
            for (int i = 0; i < 10000; i++) {
                list.add(buildAccount(i));
                //判断是否存满
                if (i%1000 == 0){
                    //已经存满,加载数据
                    service.saveBatch(list);
                    //将list数据清空
                    list.clear();
                }
            }
            long after = System.currentTimeMillis();
            System.out.println("总共用时:"+(after-now));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    在Datasource的url当中&这个参数:
    在这里插入图片描述

    三、拓展业务

    下载插件IDEA当中的MybatisPlus

    在这里插入图片描述
    下载完后就点击这个进行Datasource的配置
    在这里插入图片描述

    在这里插入图片描述

    点击这个进行配置文件的放置

    在这里插入图片描述
    在这里插入图片描述

    3.1 静态工具

    在这里插入图片描述
    出现静态工具的原因是:
    以为如果使用的是service进行创作,如果两张表相互嵌套相互使用,就会出现循环依赖,为了去除这个循环依赖,所以引入了静态工具

    在这里插入图片描述

     Db.lambdaQuery(Account.class)
                    .eq(Account::getId, id)
                    .list()
    
    • 1
    • 2
    • 3

    3.2 逻辑删除

    • 逻辑删除就是基于代码逻辑模拟删除效果,但并不会真正删除数据。思路如下
      • 在表中添加一个字段标记数据是否被删除
      • 当删除数据时把标记置为1
      • 查询时只查询标记为0的数据

    MybatisPlus提供了逻辑删除功能,无需改变方法调用的方式,而是在底层帮我们自动修改CRUD的语句。我们要做的就是在application.yaml文件中配置逻辑删除的字段名称和值即可:

    mybatis-plus:
      global-config:
        db-config: 
          logic-delete-field: deleted# 全局逻辑删除的实体字名,字段类型可以是boolean、integer
          logic-delete-value: 1 # 逻辑已删除值(默认为 1)
          logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    就是说明我们在进行delete删除数据的时候不再是进行数据的删除,只是把deleted修改为1,在查询的时候加上where条件进行查询是否是1,如果为1,就不会在页面当中显示出来

    逻辑删除的弊端:

    • 逻辑删除本身也有自己的问题,比如:会导致数据库表垃圾数据越来越多,影响查询效率
    • SQL中全都需要对逻辑删除字段做判断,影响查询效率因此,我不太推荐采用逻辑删除功能,
    • 如果数据不能删除,可以采用把数据迁移到其它表的办法

    3.3、枚举处理器

    步骤:

    1. 给枚举中的与数据库对应value值添加@EnumValue注解

    2. 在配置文件中配置统一的枚举处理器,实现类型转换

    
    public enum SexEnum {
     
        MAN(1, "男"),
        WOMAN(2, "女");
     
        @EnumValue
        private Integer key;
     
        @JsonValue
        private String display;
     
        SexEnum(Integer key, String display) {
            this.key = key;
            this.display = display;
        }
     
        public Integer getKey() {
            return key;
        }
     
        public String getDisplay() {
            return display;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    3.4 JSON处理器

    将数据库当中的JSON类型的String进行转换:

    在这里插入图片描述

    • 步骤:
      在这里插入图片描述
      在这里插入图片描述

    3.5 分页插件功能

    1. 首先创建一个配置配去配置拦截器
    @Configuration
    public class MyBatisConfig {
        @Bean
        public MybatisPlusInterceptor mybatisPlusInterceptor(){
            MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
            //1. 创建分页条件
            PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
            //2.将分页的条件放入到拦截器当中
            interceptor.addInnerInterceptor(paginationInnerInterceptor);
            return interceptor;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    1. 调用方法,把条件插入
     /*测试分页工具*/
        @Test
        void testPage(){
            //给与条件
            int pageNum = 1;
            int pageSize = 1;
            Page<Book> page = Page.of(pageNum, pageSize);
    
            //添加排序条件,可以添加多个,如果前一个相同,就按照后面的进行操作
            page.addOrder(new OrderItem("id", true));
    
            Page<Book> p =  service.page(page);  
    		//可以查询表的其他信息 
            
            //获取到查询数据的总条数
            long total = p.getTotal();
            System.out.println("total"+total);
            //获取到当前分页的全部数据
            List<Book> records = p.getRecords();
            records.forEach(System.out::println);
            //获取到页码的数量
            long pages = p.getPages();
            System.out.println("pages:"+pages);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    3.6 通用分页实体

    pojo.Book层:

    @Data
    @TableName("tbl_book")
    public class Book {
        private Integer id;
        private String type;
        private String name;
        private String description;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    PageInfo(就是封装的分页信息类):

    @Data
    public class PageInfo<T> {
        /*查询到的总条数*/
        private Long total;
        /*查询到的总页数*/
        private Long pages;
        /*查询到当前页的所有数据*/
        private List<T> info;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    MybatisConfig(注解封装的拦截器,用来拦截信息添加分页功能):

    @Configuration
    @MapperScan("com.itheima.dao")
    public class MyBatisConfig {
        @Bean
        public MybatisPlusInterceptor mybatisPlusInterceptor(){
            MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
            //1. 创建分页条件
            PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
            //2.将分页的条件放入到拦截器当中
            interceptor.addInnerInterceptor(paginationInnerInterceptor);
            return interceptor;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    BookController:

    @RestController
    @RequestMapping("/books")
    public class BookController {
        @Autowired
        private BookService service;
    
       
        @GetMapping("/pages")
        PageInfo<Book> myQueryPages(BookQuery bookQuery){
            return service.QueryPages(bookQuery);
        } 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    BookDao:

    @Mapper
    public interface BookDao extends BaseMapper<Book>{
        @Select("select * from tbl_book where id > #{id}")
        Page<Book> selectPageVo(@Param("page") Page<User> page, @Param("id") Integer id);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    TemplateQuery(查询条件类的模板《父类》):

    @Data
    public class TemplateQuery {
        //页码
        private int queryPage;
        //size
        private int querySize;
        //排序条件
        private String ordered;
        //升序还是降序(true升序)
        private boolean desc;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    BookQuery(创建放置查询条件的类):

    @Data
    public class BookQuery extends TemplateQuery{
        /*查询的书名字*/
        private String name;
        /*查询的科目*/
        private String type;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    Bookservice:

    public interface BookService extends IService<Book> {
        PageInfo<Book> QueryPages(BookQuery bookQuery);
    } 
    
    • 1
    • 2
    • 3

    BookServiceImpl:

    @Service
    public class BookServiceImpl extends ServiceImpl<BookDao, Book> implements BookService {
        @Override
        public PageInfo<Book> QueryPages(BookQuery bookQuery) {
            //获取到传输过来的数据
            int queryPage = bookQuery.getQueryPage();
            String name = bookQuery.getName();
            int querySize = bookQuery.getQuerySize();
            String ordered = bookQuery.getOrdered();
            String type = bookQuery.getType();
    
            //分页查询条件
            Page<Book> page = Page.of(queryPage, querySize);
    
            //排序条件
            //进行判断
            if(ordered != null){
                page.addOrder(new OrderItem(ordered,bookQuery.isDesc()));
            }else {
                //设置默认值
                page.addOrder(new OrderItem("id",true));
            }
    
    
            //分页条件
            lambdaQuery()
                    .like(name !=null,Book::getName,name)
                    .like(type != null,Book::getType,type)
                    .page(page);
    
            //将数据进行封装
            PageInfo<Book> bookPageInfo = new PageInfo<>();
    
            bookPageInfo.setPages(page.getPages());
            bookPageInfo.setInfo(page.getRecords());
            bookPageInfo.setTotal(page.getTotal());
    
            return bookPageInfo;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
  • 相关阅读:
    论文解读-DeepEdit:使用纳米孔直接RNA测序对A - to - I RNA编辑事件进行单分子检测和阶段划分
    基础化学习题及答案
    【广州华锐互动】智能变电站AR仿真实训系统大大提高培训的效率和质量
    Eureka注册中心
    MySQL Server层的 max_connections 和引擎层的 innodb_thread_concurrency
    elementui el-date-picker禁止选择今年、今天、之前、时间范围限制18个月
    [附源码]Python计算机毕业设计Django疫苗药品批量扫码识别追溯系统
    2000-2023年各省年末常住人口数据(无缺失)
    三甲医院基于超融合建设 IaaS 私有云实践
    处理uniapp打包后有广告的问题
  • 原文地址:https://blog.csdn.net/gua_Piter/article/details/134035678