zoukankan      html  css  js  c++  java
  • MybatisPlus

    一、简介

    MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

    1)特性

     无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
    
    ● 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
    
    ● 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
    
    ● 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
    
    ● 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
    
    ● 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
    
    ● 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
    
    ● 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
    
    ● 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
    
    ● 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
    
    ● 内置性能分析插件:可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
    
    ● 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作

    二、快速入门

    使用第三方组件:

    1、导入对应的依赖

    2、研究依赖如何配置

    3、代码如何编写

    4、提高扩展技术能力!

    1)步骤

    1、创建数据库 mybatis_plus

    2、创建user表

    # 创建表
    DROP TABLE IF EXISTS user;
    
    CREATE TABLE user
    (
        id BIGINT(20) NOT NULL COMMENT '主键ID',
        name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
        age INT(11) NULL DEFAULT NULL COMMENT '年龄',
        email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
        PRIMARY KEY (id)
    );
    
    # 伪造数据
    DELETE FROM user;
    
    INSERT INTO user (id, name, age, email) VALUES
    (1, 'Jone', 18, 'test1@baomidou.com'),
    (2, 'Jack', 20, 'test2@baomidou.com'),
    (3, 'Tom', 28, 'test3@baomidou.com'),
    (4, 'Sandy', 21, 'test4@baomidou.com'),
    (5, 'Billie', 24, 'test5@baomidou.com');
    View Code

    3、编写项目,初始化项目!使用SpringBoot初始化!

    4、导入依赖

    <!-- Mysql数据库驱动-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    <!-- lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <!-- mybatis-plus 是开源的自己开发的,非官方!-->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.0.5</version>
    </dependency>

    说明:使用 mybatis-plus 可以节省我们大量的代码,但是尽量不要同时导入 mybatis 和 mybatis-plus !版本的差异可能会导致出错

    5、连接数据库!这一步和 mybatis 相同

    # 数据库驱动
    spring.datasource.username=root
    spring.datasource.password=123456
    spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?useUnicode=true&characterEncoding=UTF-8&useSSL=true&serverTimezone=GMT%2B8
    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

    6、传统方式 pojo-dao(连接mybatis,配置mapper.xml文件),-service-controller

    6、现在使用 mybatis-plus 之后

    ● pojo

    package com.cyan.pojo;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class User {
    
        private Long id;
        private String name;
        private Integer age;
        private String email;
    
    }

    ● mapper

    package com.cyan.mapper;
    
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.cyan.pojo.User;
    import org.springframework.stereotype.Repository;
    
    // 在对应的 Mapper 上面继承基本的类 baseMapper
    @Repository // 代表持久层
    public interface UserMapper extends BaseMapper<User> {
    
        // 所有的CURD操作都已经在父类编写完成,不需要像以前一样配置一大堆文件
    
    }

    ● 注意:需要在主启动类上去扫描mapper包下的所有接口 @MapperScan(“com.cyan.mapper”)

    ● 测试类中测试

    package com.cyan;
    
    import com.cyan.mapper.UserMapper;
    import com.cyan.pojo.User;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    
    import java.util.List;
    
    @SpringBootTest
    class MybatisPlusApplicationTests {
    
        // 继承BaseMapper,所有方法都来自父类;也可以扩展自己的方法
        @Autowired
        private UserMapper userMapper;
    
        @Test
        void contextLoads() {
            // selectList查询全部;参数是一个wapper,条件构造器,这里先不使用。
            List<User> list = userMapper.selectList(null);
            list.forEach(System.out::println);
        }
    }

    ● 结果

    总结:从测试中可以看出来,SQL 和 mapper 方法 Mybatis-Plus 都已经帮我们写好了!

    三、配置日志

    mybatis-plus默认在执行中所有SQL是不可见的,想要知道它怎么执行的,就必须配置日志输出

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

    配置完日志之后,就可以查看自动生成的SQL语句了。

    四、CURD扩展

    1)插入操作

    Insert插入

    // 测试插入
    @Test
    void insert(){
    
        User user = new User();
        user.setName("TestInsert");
        user.setAge(3);
        user.setEmail("TestInsert@qq.com");
    
        int insert = userMapper.insert(user); // 自动生成ID
    
        System.out.println(insert); // 受影响的行数
        System.out.println(user);   // id会自增回填
    }

    数据库插入的ID的默认的值为:全局唯一ID(前提-Long类型的主键)

    2)主键生成策略

    默认 ID_WORKER 全局唯一id

    分布式系统唯一id生成策略:https://www.cnblogs.com/haoxinyue/p/5208136.html

    ① 雪花算法

      snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit数据中心,5个bit是机器ID),12bit作为毫秒内的流水号(意味着每个节点每毫秒可以生成4096个ID),最后还有一个符号为,永远是0;

    ② 主键自增:需要配置主键自增

    1、实体类主键ID字段上添加 @TableId(type = IdType.AUTO)

    2、数据库字段一定要自增

    3、再次插入即可

    ③ 其他主键生成策略

    public enum IdType {
    
        AUTO(0),  // 数据库ID自增
        NONE(1),  // 该类型为未设置主键类型
        INPUT(2), // 用户输入ID,该类型可以通过自己注册自动填充插件进行填充
    
        /* 以下3种类型、只有当插入对象ID 为空,才自动填充。 */
        ID_WORKER(3),   // 全局唯一ID (idWorker)
        UUID(4),      // 全局唯一ID (UUID)
        ID_WORKER_STR(5); // 字符串全局唯一ID (idWorker 的字符串表示)
    }

    ④ 生成策略使用:有两种方式

    1.实体类字段上添加注解

    @TableId(type = IdType.策略类型)
    private Long id;

    2.在application配置文件中添加全局配置

    mybatis-plus.global-config.db-config.id-type=id_worker

    3)更新操作

    // 测试更新
    @Test
    void update() {
        // 修改刚才插入的user,
        User user = new User();
        user.setId(1257554457532624898L);
        user.setName("newTestInsert");
        user.setAge(24);
        user.setEmail("newTestInsert@qq.com");
        int insert = userMapper.updateById(user); //ID必须存在,字段根据是否为null自动拼接,实现动态SQL
        System.out.println(insert); // 受影响的行数
        System.out.println(user);   // id会自增回填
    }

    4)自动填充

    表字段 创建时间、修改时间!这些操作一般都是自动化完成,不需要手动更新。

    阿里巴巴开发手册:所有的数据表:gmt_create、gmt_modified几乎所有的表都要配置上!而且需要自动化!

    1、表中新增字段 create_time、update_time

    2、实体类字段增加属性注解 @TableField

    @TableField(fill = FieldFill.INSERT) // 插入填充字段
    private Date createTime;
    
    @TableField(fill = FieldFill.INSERT_UPDATE) // 插入和更新填充字段
    private Date updateTime;

    3、编写处理器处理上面的注解

    package com.cyan.handler;
    
    import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.ibatis.reflection.MetaObject;
    import org.springframework.stereotype.Component;
    
    import java.util.Date;
    
    @Slf4j
    @Component // 将处理器加载到IOC中
    public class MyMetaObjectHandler implements MetaObjectHandler {
    
        // 插入时填充策略
        @Override
        public void insertFill(MetaObject metaObject) {
            log.info("start insert fill......");
            this.setFieldValByName("createTime", new Date(), metaObject);
            this.setFieldValByName("updateTime", new Date(), metaObject);
        }
    
        // 更新时填充策略
        @Override
        public void updateFill(MetaObject metaObject) {
            log.info("start update fill......");
            this.setFieldValByName("updateTime", new Date(), metaObject);
        }
    }

    4、测试插入

    插入成功

    5、测试更新

    修改成功

    5)乐观锁

    乐观锁:顾名思义十分乐观,他总是认为不会出现问题。无论干什么都不上锁!如果出现了问题,再次更新值测试!

    悲观锁:顾名思义十分悲观,他总是认为总会出现问题。无论干什么都会上锁再去操作!

    1、乐观锁实现方式:

    1. 取出记录时,获取当前version
    2. 更新时,带上version
    3. 执行更新时,set version = newVersion where version = oldVersion
    4. 如果version不对,就更新失败
    乐观锁:1、先查询,获得版本号 version = 1
    
    - - - A
    update user set name = "newName1",version = version + 1
    where id = 2 and version = 1
    
    - - - B 线程抢先完成,这个时候,version = 2,会导致 A 修改失败
    update user set name = "newName2",version = version + 1
    where id = 2 and version = 1

    2、测试MybatisPlus乐观锁插件

    ① 给数据库中添加version字段

    ② 同步实体类,实体类添加字段

    @Version // 乐观锁注解
    private Integer version;

    ③ 注册乐观锁插件

    package com.cyan.config;
    
    import com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor;
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.transaction.annotation.EnableTransactionManagement;
    
    @MapperScan("com.cyan.mapper") // 扫描mapper文件夹
    @EnableTransactionManagement // 自动管理事务
    @Configuration
    public class MybatisPlusConfig {
    
        // 注册乐观锁插件
        @Bean
        public OptimisticLockerInterceptor optimisticLockerInterceptor() {
            return new OptimisticLockerInterceptor();
        }
    }

    ④ 测试

    - 乐观锁成功操作:

    // 乐观锁 - 成功测试
    @Test
    void  testOptimisticLocker(){
        // 1、查询用户信息
        User user = userMapper.selectById(1257703058003005442L);
        // 2、修改用户信息
        user.setName("testOptimisticLocker");
        user.setAge(11);
        // 3、执行更新操作
        userMapper.updateById(user);
    }

    - 乐观锁失败操作:

    // 乐观锁 - 失败测试
    @Test
    void testOptimisticLocker2() {
        // 模拟线程1
        User user = userMapper.selectById(1257703058003005442L);
        user.setName("4444");
        user.setAge(22);
    
        // 模拟线程2 - 插队
        User user2 = userMapper.selectById(1257703058003005442L);
        user2.setName("6666");
        user2.setAge(33);
        userMapper.updateById(user2);
    
        // 一般使用自旋来多次尝试提交
        userMapper.updateById(user); // 如果没有乐观锁就会覆盖插队线程的值
    }

    6)询操作

    @Test //根据ID查询
        void testSelectById() { 
        User user = userMapper.selectById(1L);
        System.out.println(user);
    }
    
    @Test //根据ID批量查询
    void testSelectBatchIds() {
        List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3, 4, 5));
        users.forEach(System.out::println);
    }
    
    @Test // 条件查询 map
    void testSelectByMap() {
        HashMap<String, Object> map = new HashMap<>(); 
        map.put("age", 24);
        map.put("version", 1);
        List<User> users = userMapper.selectByMap(map);
        users.forEach(System.out::println);
    }

    7)分页查询

    (1)分页方式有三种

    1. 原始的limit进行分页
    2. pageHelper第三方插件
    3. MybatisPlus内置分页插件

    (2)MP内置分页插件使用

    1、Config配置拦截器组件

    @Bean
    public PaginationInterceptor paginationInterceptor() {
        return new PaginationInterceptor();
    }

    2、传递分页对象Page进行分页

    @Test
    void testPage() { // 分页查询
        Page page = new Page(1, 3); //current页码,size页大小
        userMapper.selectPage(page, null);
        List list = page.getRecords();//获取结果
        list.forEach(System.out::println);
    }

    8)基本删除操作

    // 根据ID删除
    void testDelete() {
        userMapper.deleteById(1257703058003005442L);
    }
    
    // 根据ID批量删除
    void deleteBatchIds() {
        userMapper.deleteBatchIds(Arrays.asList(1257703058003005442L, 1257554457532624898L));
    }
    
    // 通过map删除
    void deleteByMap() {
        HashMap<String, Object> map = new HashMap<>();
        map.put("name", "deleteName");
        userMapper.deleteByMap(map);
    }

    9)逻辑删除

    物理删除:从数据库中直接移除

    逻辑删除:在数据库中没有被移除,而是通过变量来让他失效! deleted = 0 ---> deleted = 1

    管理员可以查看被删除的记录!防止数据丢失,类似于回收站!

    ● 测试

    1、在数据库中添加deleted字段

    2、实体类字段上加上@TableLogic注解

    @TableLogic // 逻辑删除
    private Integer deleted;

    3、application加入配置(如果你的默认值和mybatis-plus默认的一样,则不需要该配置,否则肯能会出错,本人踩过坑)

    # 逻辑删除
    mybatis-plus.global-config.db-config.logic-delete-value=1       # 逻辑已删除值(默认为 1)
    mybatis-plus.global-config.db-config.logic-not-delete-value=0   # 逻辑未删除值(默认为 0)

    4、注册 Bean(3.1.1开始不再需要这一步)

    @Bean
    public ISqlInjector sqlInjector() {
        return new LogicSqlInjector();
    }

    5、测试

    10)性能分析插件

    MybatisPlus提供了性能分析插件,如果这个操作超过指定的时间就停止运行。

    作用:性能分析拦截器,用于输出每条SQL语句及其执行时间

    1、导入插件

    /**
     * SQL执行效率插件
     */
    @Bean
    @Profile({"dev", "test"})// 设置 dev test 环境开启
    public PerformanceInterceptor performanceInterceptor() {
        PerformanceInterceptor interceptor = new PerformanceInterceptor();
        interceptor.setMaxTime(1000); // SQL执行最大时长,超过自动停止运行,有助于发现问题。
        interceptor.setFormat(true);  // SQL是否格式化,默认false。
        return interceptor;
    }

    该插件只用于开发环境,不建议生产环境使用。

    2、测试使用

    使用性能分析插件,可以提高效率。

    11)条件构造器

    测试一:

    void contextLoads() {
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper
                .isNotNull("name")         // name!=null
                .isNotNull("email")        // emaim!=null
                .ge("age", 20);            // age>=20
        userMapper.selectList(wrapper).forEach(System.out::println);
    }

    测试二:

    @Test
    void contextLoads2() {
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("name", "newTestInsert");              // name=newTestInsert
        List<User> users = userMapper.selectList(wrapper);   // 查询一个数据,出现多个使用 List 或者 Map
        users.forEach(System.out::println);
    }

    测试三:

    @Test
    void contextLoads3() { // 查询年龄在20~28岁之间的用户
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.between("age", 20, 28);  // 区间 age BETWEEN 20 AND 28
        Integer count = userMapper.selectCount(wrapper);      // 查询结果数
        System.out.println("count = " + count);
    }

    测试四:

    @Test
    void contextLoads4() { // 模糊查询
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper
                .notLike("name", "o")     //不包含o
                .likeRight("email", "t"); // 以t开头:likeRight t%
        List<Map<String, Object>> maps = userMapper.selectMaps(wrapper);   // 查询一个数据,出现多个使用 List 或者 Map
        maps.forEach(System.out::println);
    }

    测试五:

    @Test
    void contextLoads5() { // 联表查询
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        // Id在子查询中查询出来
        wrapper.inSql("id", "SELECT id FROM user WHERE id >= 18");
        List<Object> list = userMapper.selectObjs(wrapper); // selectObjs查询对象
        list.forEach(System.out::println);
    }

    测试六:

    @Test
    void contextLoads6() { // 排序
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        // 通过age进行排序
        wrapper.orderByDesc("age");
        List<User> list = userMapper.selectList(wrapper);
        list.forEach(System.out::println);
    }

    12)代码生成器

    AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码,极大的提升了开发效率。

    1、添加依赖:MyBatis-Plus 从 3.0.3 之后移除了代码生成器与模板引擎的默认依赖,需要手动添加相关依赖:

    添加 代码生成器 依赖

    <!-- mybatis-plus 是开源的自己开发的,非官方!-->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.0.5</version>
    </dependency>

    添加 模板引擎 依赖:MyBatis-Plus 支持 Velocity(默认)、Freemarker、Beetl,用户可以选择自己熟悉的模板引擎,如果都不满足您的要求,可以采用自定义模板引擎。

    <!-- velocity(默认) -->
    <dependency>
        <groupId>org.apache.velocity</groupId>
        <artifactId>velocity-engine-core</artifactId>
        <version>2.2</version>
    </dependency>
    <!-- freemarker--> <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.30</version> </dependency>
    <!-- beetl --> <dependency> <groupId>com.ibeetl</groupId> <artifactId>beetl</artifactId> <version>3.1.5.RELEASE</version> </dependency>

    注意!如果您选择了非默认引擎,需要在 AutoGenerator 中 设置模板引擎。

    AutoGenerator generator = new AutoGenerator();
    
    // set freemarker engine
    generator.setTemplateEngine(new FreemarkerTemplateEngine());
    // set beetl engine
    generator.setTemplateEngine(new BeetlTemplateEngine());
    // set custom engine (reference class is your custom engine class)
    generator.setTemplateEngine(new CustomTemplateEngine());

    2、编写生成器类

    import com.baomidou.mybatisplus.annotation.DbType;
    import com.baomidou.mybatisplus.annotation.IdType;
    import com.baomidou.mybatisplus.generator.AutoGenerator;
    import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
    import com.baomidou.mybatisplus.generator.config.GlobalConfig;
    import com.baomidou.mybatisplus.generator.config.PackageConfig;
    import com.baomidou.mybatisplus.generator.config.StrategyConfig;
    import com.baomidou.mybatisplus.generator.config.rules.DateType;
    import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
    import org.junit.jupiter.api.Test;
    import org.springframework.boot.test.context.SpringBootTest;
    
    // 代码自动生成器
    @SpringBootTest
    public class CodeGenerator {
    
        @Test
        void codeGenerator() {
            // 代码生成器
            AutoGenerator mpg = new AutoGenerator();
    
            // 设置全局配置
            GlobalConfig gc = new GlobalConfig();
            gc.setOutputDir(System.getProperty("user.dir") + "/src/main/java");     //输出目录
            gc.setAuthor("Cyan");       // 作者
            gc.setOpen(false);          // 是否打开文件资源管理器(文件夹)
            gc.setFileOverride(false);  // 是否覆盖
            gc.setServiceName("%sService");     //去除 Service 的 I 前缀
            gc.setIdType(IdType.ID_WORKER);     //主键生成策略
            gc.setDateType(DateType.ONLY_DATE); //日期类型 - 只使用 java.util.date 代替
            // gc.setSwagger2(true); // 是否配置 Swagger2 文档
            mpg.setGlobalConfig(gc);
    
            // 设置数据源
            DataSourceConfig dsc = new DataSourceConfig();
            dsc.setUrl("jdbc:mysql://localhost:3306/games_db?useUnicode=true&characterEncoding=UTF-8&useSSL=true&serverTimezone=GMT%2B8");
            dsc.setDriverName("com.mysql.cj.jdbc.Driver");
            dsc.setUsername("root");     // 用户名
            dsc.setPassword("123456");     // 密码
            dsc.setDbType(DbType.MYSQL); // 数据库类型
            mpg.setDataSource(dsc);
    
            // 包的配置
            PackageConfig pc = new PackageConfig();
            pc.setParent("com.cyan");   // 生成父包名
            pc.setEntity("pojo");       // 实体类包名
            mpg.setPackageInfo(pc);
    
            // 数据库表配置
            StrategyConfig strategy = new StrategyConfig();
            strategy.setInclude("goods", "role", "inventory"); // 设置需要生成的表
            strategy.setNaming(NamingStrategy.underline_to_camel);       // 数据库表映射到实体的命名策略 - 下划线转驼峰
            strategy.setColumnNaming(NamingStrategy.underline_to_camel); // 数据库表字段映射到实体的命名策略 - 下划线转驼峰
            strategy.setEntityLombokModel(true); // 自动生成Lombok
    //        strategy.setLogicDeleteFieldName("deleted"); // 设置逻辑删除字段
    //        strategy.setVersionFieldName("version");     // 设置乐观锁字段
    //        TableFill gmtCreate = new TableFill("gmt_create", FieldFill.INSERT);            // 字段填充
    //        TableFill gmtModified = new TableFill("gmt_modified", FieldFill.INSERT_UPDATE); // 字段填充
    //        strategy.setTableFillList(Arrays.asList(gmtCreate, gmtModified)); // 表填充字段
            strategy.setRestControllerStyle(true); // 开启result驼峰命名
            strategy.setControllerMappingHyphenStyle(true); //驼峰转连字符 ---> localhost:8080/hello_id_2
            mpg.setStrategy(strategy);
    
            // 执行代码生成器
            mpg.execute();
        }
    }
  • 相关阅读:
    单例模式
    Curator Zookeeper分布式锁
    LruCache算法原理及实现
    lombok 简化java代码注解
    Oracle客户端工具出现“Cannot access NLS data files or invalid environment specified”错误的解决办法
    解决mysql Table ‘xxx’ is marked as crashed and should be repaired的问题。
    Redis 3.0 Cluster集群配置
    分布式锁的三种实现方式
    maven发布项目到私服-snapshot快照库和release发布库的区别和作用及maven常用命令
    How to Use Convolutional Neural Networks for Time Series Classification
  • 原文地址:https://www.cnblogs.com/Dm920/p/12828572.html
Copyright © 2011-2022 走看看