Mini MyBatis-Plus(下)

作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO

联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬
 

最核心的内容前两篇已经讲完了,这一篇只有代码:

先看demo目录下的三个文件:

DemoApplication.java

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}

User.java

package com.example.demo;

import com.example.demo.mybatisplus.annotations.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

/**
 * @author mx
 */
@Data
@TableName("t_user")
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Long id;
    private String name;
    private Integer age;
    private Date birthday;
}

UserMapper.java

package com.example.demo;

import com.example.demo.mybatisplus.AbstractBaseMapper;

/**
 * @author mx
 */
public class UserMapper extends AbstractBaseMapper<User> {
}

mybatisplus下AbstractBaseMapper.java

package com.example.demo.mybatisplus;

import com.example.demo.mybatisplus.annotations.TableName;
import com.example.demo.mybatisplus.core.JdbcTemplate;
import com.example.demo.mybatisplus.query.QueryWrapper;
import com.example.demo.mybatisplus.query.SqlParam;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * Mapper基类
 *
 * @author mx
 */
public abstract class AbstractBaseMapper<T> {

    private static Logger logger = LoggerFactory.getLogger(AbstractBaseMapper.class);

    private JdbcTemplate<T> jdbcTemplate = new JdbcTemplate<T>();

    private Class<T> beanClass;

    private final String TABLE_NAME;

    private static final String DEFAULT_LOGICAL_TYPE = " and ";

    public AbstractBaseMapper() {
        // DO对象的Class
        beanClass = (Class<T>) ((ParameterizedType) this.getClass()
                .getGenericSuperclass())
                .getActualTypeArguments()[0];
        // DO对应的表名 TODO 非空判断及默认处理
        TABLE_NAME = beanClass.getAnnotation(TableName.class).value();
    }

    public T select(QueryWrapper<T> queryWrapper) {
        List<T> list = this.list(queryWrapper);
        if (!list.isEmpty()) {
            return list.get(0);
        }

        return null;
    }

    public List<T> list(QueryWrapper<T> queryWrapper) {
        StringBuilder sqlBuilder = new StringBuilder("SELECT * FROM ").append(TABLE_NAME).append(" WHERE ");

        List<Object> paramList = new ArrayList<>();
        Map<String, SqlParam> conditionMap = queryWrapper.build();
        conditionMap.forEach((operator, param) -> {
            sqlBuilder.append(param.getColumnName()).append(operator).append("?").append(DEFAULT_LOGICAL_TYPE);
            paramList.add(param.getValue());
        });

        // 删除最后一个 and
        String sql = sqlBuilder.replace(sqlBuilder.length() - DEFAULT_LOGICAL_TYPE.length(), sqlBuilder.length(), ";").toString();

        try {
            logger.info("sql: {}", sql);
            logger.info("params: {}", paramList);
            return jdbcTemplate.queryForList(sql, paramList, beanClass);
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("query failed", e);
        }

        return Collections.emptyList();
    }

    public int insert(T bean) {
        // 得到DO对象的所有字段
        Field[] declaredFields = beanClass.getDeclaredFields();

        // 拼接sql语句,表名来自DO的TableName注解value
        StringBuilder sqlBuilder = new StringBuilder()
                .append("INSERT INTO ")
                .append(TABLE_NAME)
                .append(" VALUES(");
        for (int i = 0; i < declaredFields.length; i++) {
            sqlBuilder.append("?");
            if (i < declaredFields.length - 1) {
                sqlBuilder.append(",");
            }
        }
        sqlBuilder.append(")");

        // 收集sql参数
        ArrayList<Object> paramList = new ArrayList<>();
        try {
            for (Field declaredField : declaredFields) {
                declaredField.setAccessible(true);
                Object o = declaredField.get(bean);
                paramList.add(o);
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        int affectedRows = 0;
        try {
            logger.info("sql: {}", sqlBuilder.toString());
            logger.info("params: {}", paramList);
            affectedRows = jdbcTemplate.update(sqlBuilder.toString(), paramList);
            logger.info("insert success, affectedRows: {}", affectedRows);
            return affectedRows;
        } catch (SQLException e) {
            e.printStackTrace();
            logger.error("insert failed", e);
        }

        return 0;
    }

    public int updateSelective(T bean, QueryWrapper<T> queryWrapper) {
        // 得到DO对象的所有字段
        Field[] declaredFields = beanClass.getDeclaredFields();

        // 拼接sql语句,表名来自DO的TableName注解value
        StringBuilder sqlSetBuilder = new StringBuilder()
                .append("UPDATE ")
                .append(TABLE_NAME)
                .append(" SET ");

        List<Object> paramList = new ArrayList<>();

        // 先拼接要SET的字段占位符 SET name=?, age=?
        try {
            for (int i = 0; i < declaredFields.length; i++) {
                Field declaredField = declaredFields[i];
                declaredField.setAccessible(true);
                Object fieldValue = declaredField.get(bean);
                if (fieldValue != null) {
                    sqlSetBuilder.append(declaredField.getName()).append(" = ").append("?").append(", ");
                    paramList.add(fieldValue);
                }
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        // 删除最后一个 ,
        sqlSetBuilder = sqlSetBuilder.delete(sqlSetBuilder.length() - 2, sqlSetBuilder.length());

        // 再拼接WHERE条件占位符
        StringBuilder sqlWhereBuilder = new StringBuilder(" WHERE ");
        Map<String, SqlParam> conditionMap = queryWrapper.build();
        for (Map.Entry<String, SqlParam> stringSqlParamEntry : conditionMap.entrySet()) {
            String operator = stringSqlParamEntry.getKey();
            SqlParam param = stringSqlParamEntry.getValue();
            sqlWhereBuilder.append(param.getColumnName()).append(operator).append("?").append(DEFAULT_LOGICAL_TYPE);
            paramList.add(param.getValue());
        }
        // 删除最后一个 and
        sqlWhereBuilder = sqlWhereBuilder.replace(sqlWhereBuilder.length() - DEFAULT_LOGICAL_TYPE.length(), sqlWhereBuilder.length(), ";");

        String sql = sqlSetBuilder.append(sqlWhereBuilder).toString();

        int affectedRows = 0;
        try {
            logger.info("sql: {}", sqlSetBuilder.toString());
            logger.info("params: {}", paramList);
            affectedRows = jdbcTemplate.update(sql, paramList);
            logger.info("update success, affectedRows: {}", affectedRows);
            return affectedRows;
        } catch (SQLException e) {
            e.printStackTrace();
            logger.error("update failed", e);
        }

        return 0;
    }

}

annotations下的TableName.java

package com.example.demo.mybatisplus.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author mx
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface TableName {

    String value();

}

core下的

JdbcTemplate.java

package com.example.demo.mybatisplus.core;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

/**
 * JdbcTemplate,简化jdbc操作
 *
 * @author mx
 */
public class JdbcTemplate<T> {

    public List<T> queryForList(String sql, List<Object> params, RowMapper<T> rowMapper) throws SQLException {
        return query(sql, params, rowMapper);
    }

    public T queryForObject(String sql, List<Object> params, RowMapper<T> rowMapper) throws SQLException {
        List<T> result = query(sql, params, rowMapper);
        return result.isEmpty() ? null : result.get(0);
    }

    public List<T> queryForList(String sql, List<Object> params, Class<T> clazz) throws Exception {
        return query(sql, params, clazz);
    }

    public T queryForObject(String sql, List<Object> params, Class<T> clazz) throws Exception {
        List<T> result = query(sql, params, clazz);
        return result.isEmpty() ? null : result.get(0);
    }

    public int update(String sql, List<Object> params) throws SQLException {
        // 1.获取Connection
        Connection conn = getConnection();

        // 2.传入sql模板、sql参数,得到PreparedStatement
        PreparedStatement ps = getPreparedStatement(sql, params, conn);

        // 3.执行更新(增删改)
        int affectedRows = ps.executeUpdate();

        // 4.释放资源
        closeConnection(conn, ps, null);

        return affectedRows;
    }

    // ************************* private methods **************************

    private List<T> query(String sql, List<Object> params, RowMapper<T> rowMapper) throws SQLException {
        // 外部传入rowMapper(手写规则)
        return baseQuery(sql, params, rowMapper);
    }

    private List<T> query(String sql, List<Object> params, Class<T> clazz) throws Exception {
        // 自己创建rowMapper(反射)后传入
        BeanHandler<T> beanHandler = new BeanHandler<>(clazz);
        return baseQuery(sql, params, beanHandler);
    }

    /**
     * 基础查询方法,必须传入Bean的映射规则
     *
     * @param sql
     * @param params
     * @param rowMapper
     * @return
     * @throws SQLException
     */
    private List<T> baseQuery(String sql, List<Object> params, RowMapper<T> rowMapper) throws SQLException {
        // TODO 参数非空校验

        // 1.获取Connection
        Connection conn = getConnection();

        // 2.传入sql模板、sql参数,得到PreparedStatement
        PreparedStatement ps = getPreparedStatement(sql, params, conn);

        // 3.执行查询
        ResultSet rs = ps.executeQuery();

        // 4.处理结果
        List<T> result = new ArrayList<>();
        while (rs.next()) {
            T obj = rowMapper.mapRow(rs);
            result.add(obj);
        }

        // 5.释放资源
        closeConnection(conn, ps, rs);
        return result;
    }

    /**
     * 内部类,实现了RowMapper接口,底层使用反射
     *
     * @param <R>
     */
    private static class BeanHandler<R> implements RowMapper<R> {
        // clazz表示最终封装的bean类型
        private Class<R> clazz;

        public BeanHandler(Class<R> clazz) {
            this.clazz = clazz;
        }

        @Override
        public R mapRow(ResultSet rs) {
            try {
                if (rs.next()) {
                    // 1.获取表数据
                    ResultSetMetaData metaData = rs.getMetaData();

                    // 2.反射创建bean
                    R bean = clazz.newInstance();

                    // 3.利用反射,把表数据设置到bean中
                    for (int i = 0; i < metaData.getColumnCount(); i++) {
                        String name = metaData.getColumnName(i + 1);
                        Object value = rs.getObject(name);
                        Field field = clazz.getDeclaredField(name);
                        field.setAccessible(true);
                        field.set(bean, value);
                    }

                    // 4.返回bean
                    return bean;
                } else {
                    return null;
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    private PreparedStatement getPreparedStatement(String sql, List<Object> params, Connection conn) throws SQLException {
        // 1.传入sql模板,得到PreparedStatement
        PreparedStatement ps = conn.prepareStatement(sql);

        // 2.为sql模板设置参数
        for (int i = 0; i < params.size(); i++) {
            ps.setObject(i + 1, params.get(i));
        }

        return ps;
    }

    private Connection getConnection() throws SQLException {
        // TODO 可以抽取配置到properties文件
        String url = "jdbc:mysql://localhost:3306/demo";
        String user = "root";
        String password = "123456";
        return DriverManager.getConnection(url, user, password);
    }

    private void closeConnection(Connection conn, PreparedStatement preparedStatement, ResultSet rs) throws SQLException {
        if (rs != null) {
            rs.close();
        }

        if (preparedStatement != null) {
            preparedStatement.close();
        }

        if (conn != null) {
            conn.close();
        }
    }

}

 RowMapper.java

package com.example.demo.mybatisplus.core;

import java.sql.ResultSet;

/**
 * 结果集映射器
 *
 * @author mx
 */
@FunctionalInterface
public interface RowMapper<T> {
    /**
     * 将结果集转为指定的Bean
     *
     * @param resultSet
     * @return
     */
    T mapRow(ResultSet resultSet);
}

query下的

QueryWrapper.java

package com.example.demo.mybatisplus.query;

import com.example.demo.mybatisplus.utils.ConditionFunction;
import com.example.demo.mybatisplus.utils.Reflections;

import java.util.HashMap;
import java.util.Map;

/**
 * 模拟MyBatis-Plus的LambdaQueryWrapper(思路完全不同,仅仅是形似)
 *
 * @author mx
 */
public class QueryWrapper<T> {
    // conditionMap,收集查询条件
    // {
    //    " LIKE ": {
    //        "name": "bravo1988"
    //    },
    //    " = ": {
    //        "age": 18
    //    }
    // }
    private final Map<String, SqlParam> conditionMap = new HashMap<>();

    // 操作符类型,比如 name like 'bravo' 中的 LIKE
    private static final String OPERATOR_EQ = " = ";
    private static final String OPERATOR_GT = " > ";
    private static final String OPERATOR_LT = " < ";
    private static final String OPERATOR_LIKE = " LIKE ";

    public QueryWrapper<T> eq(ConditionFunction<T, ?> fn, Object value) {
        String columnName = Reflections.fnToColumnName(fn);
        conditionMap.put(OPERATOR_EQ, new SqlParam(columnName, value));
        return this;
    }

    public QueryWrapper<T> gt(ConditionFunction<T, ?> fn, Object value) {
        String columnName = Reflections.fnToColumnName(fn);
        conditionMap.put(OPERATOR_GT, new SqlParam(columnName, value));
        return this;
    }

    public QueryWrapper<T> lt(ConditionFunction<T, ?> fn, Object value) {
        String columnName = Reflections.fnToColumnName(fn);
        conditionMap.put(OPERATOR_LT, new SqlParam(columnName, value));
        return this;
    }

    public QueryWrapper<T> like(ConditionFunction<T, ?> fn, Object value) {
        String columnName = Reflections.fnToColumnName(fn);
        conditionMap.put(OPERATOR_LIKE, new SqlParam(columnName, "%" + value + "%"));
        return this;
    }

    public Map<String, SqlParam> build() {
        return conditionMap;
    }
}

 SqlParam.java

package com.example.demo.mybatisplus.query;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author mx
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SqlParam {
    private String columnName;
    private Object value;
}

utils下的

 ConditionFunction.java

package com.example.demo.mybatisplus.utils;

import java.io.Serializable;
import java.util.function.Function;

/**
 * 扩展java.util.function包下的Function接口:支持Serializable
 * 搭配Reflections工具类一起使用,用于获取Lambda表达式的方法名
 *
 * @author mx
 */
@FunctionalInterface
public interface ConditionFunction<T, R> extends Function<T, R>, Serializable {
}

Reflections.java

package com.example.demo.mybatisplus.utils;

import java.beans.Introspector;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Method;
import java.util.regex.Pattern;

/**
 * 获取Lambda入参的方法名
 *
 * @author mx
 */
public class Reflections {
    private static final Pattern GET_PATTERN = Pattern.compile("^get[A-Z].*");
    private static final Pattern IS_PATTERN = Pattern.compile("^is[A-Z].*");

    /**
     * 注意: 非标准变量(非小驼峰)调用这个方法可能会有问题
     *
     * @param fn
     * @param <T>
     * @return
     */
    public static <T> String fnToColumnName(ConditionFunction<T, ?> fn) {
        try {
            Method method = fn.getClass().getDeclaredMethod("writeReplace");
            method.setAccessible(Boolean.TRUE);
            SerializedLambda serializedLambda = (SerializedLambda) method.invoke(fn);
            String getter = serializedLambda.getImplMethodName();
            // 对于非标准变量生成的Get方法这里可以直接抛出异常,或者打印异常日志
            if (GET_PATTERN.matcher(getter).matches()) {
                getter = getter.substring(3);
            } else if (IS_PATTERN.matcher(getter).matches()) {
                getter = getter.substring(2);
            }
            return Introspector.decapitalize(getter);
        } catch (ReflectiveOperationException e) {
            throw new RuntimeException(e);
        }
    }
}

其实第一篇的内容是最难的,不只是从0到1,而是从0到90,后面两篇其实只是90到100,在这基础稍微扩展了一下而已。

AbstractBaseMapper代码还有冗余,有兴趣的同学可以自行完善。但还是那句话,如果你的目的是为了锻炼封装能力,可以精益求精,但我们的AbstractBaseMapper注定不能用于生产,即使要优化,点到为止即可。

学习必须往深处挖,挖的越深,基础越扎实!

阶段1、深入多线程

阶段2、深入多线程设计模式

阶段3、深入juc源码解析

阶段4、深入jdk其余源码解析

阶段5、深入jvm源码解析

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

docker 安装可视化工具 Protainer 以及 汉化

一、创建保存数据的卷 安装网址&#xff1a;Install Portainer BE with Docker on Linux - Portainer Documentation docker pull portainer/portainer二、根据portainer镜像创建容器 docker run -d -p 8000:8000 -p 9000:9000\ --name portainer --restartalways \ -v /var/r…

【Python_PySide2学习笔记(二十)】消息对话框QMessageBox类的基本用法

消息对话框QMessageBox类的基本用法 消息对话框QMessageBox类的基本用法前言正文1、警告对话框 QMessageBox.warning()2、消息对话框 QMessageBox.information()3、提问对话框 QMessageBox.question()4、错误对话框 QMessageBox.critical()5、关于对话框 QMessageBox.about()6、…

小白进公司不会用Jenkins,一文带你流利使用

起因 需要将应用从阿里云迁移到微软云&#xff0c;重新部署应用。 一. 前置知识学习 CI/CD 是一组经常在现代软件开发中提到的实践&#xff0c;代表持续集成&#xff08;Continuous Integration&#xff09;和持续交付/部署&#xff08;Continuous Delivery/Deployment&…

(JAVA)-(多线程)-线程池

线程池&#xff0c;顾名思义就是存放线程的池子&#xff0c;当有任务时能够随时取用线程&#xff0c;任务结束后能够放回线程池中。如果把线程比成碗&#xff0c;线程池就像一个碗柜一样。 使用线程池的好处&#xff1a; 1.当有大量线程对象时&#xff0c;减少了线程创建销毁…

ssm基于vue的大学生社团管理系统的设计与实现+vue论文

基于vue的大学生社团管理系统的设计与实现 计算机科学与技术 2022届 姓名 学号 摘要 当下&#xff0c;正处于信息化的时代&#xff0c;许多行业顺应时代的变化&#xff0c;结合使用计算机技术向数字化、信息化建设迈进。传统的大学生社团信息管理模式&#xff0c;采用人工登…

【Vue2+3入门到实战】(13)插槽<slot>详细示例及自定义组件的创建与使用代码示例 详解

目录 一、学习目标1.插槽2.综合案例&#xff1a;商品列表 一、插槽-默认插槽1.作用2.需求3.问题4.插槽的基本语法5.代码示例6.总结 二、插槽-后备内容&#xff08;默认值&#xff09;1.问题2.插槽的后备内容3.语法4.效果5.代码示例 三、插槽-具名插槽1.需求2.具名插槽语法3.v-s…

Apache Flink连载(二十):Flink On Yarn运行 - Yarn Per-Job模式(弃用)

🏡 个人主页:IT贫道_大数据OLAP体系技术栈,Apache Doris,Clickhouse 技术-CSDN博客 🚩 私聊博主:加入大数据技术讨论群聊,获取更多大数据资料。 🔔 博主个人B栈地址:豹哥教你大数据的个人空间-豹哥教你大数据个人主页-哔哩哔哩视频 目录 1. 任务提交命令 2. 任务…

使用electron属性实现保存图片并获取图片的磁盘路径

在普通的网页开发中&#xff0c;JavaScript由于安全性的考虑&#xff0c;通常是无法直接获取到客户端的磁盘路径的。浏览器出于隐私和安全原因对此类信息进行了限制。 在浏览器环境下&#xff0c;JavaScript主要通过Web APIs来与浏览器进行交互&#xff0c;而这些API通常受到浏…

lottie 动画在 vue 中的使用

前言 最近我所负责的项目中采用了动画效果&#xff0c;最早使用 gif 来实现。然而&#xff0c;在实践过程中&#xff0c;我发现 gif 格式的动画在 git 中出现了明显的锯齿感&#xff0c;这让我非常困扰。为了追求更完美的表现效果&#xff0c;我最终选择了 lottie 来实现我的动…

C++核心编程四(继承、多态、virtual关键字、文件操作)

文章目录 继承继承方式继承中的对象模型继承中构造和析构顺序继承同名成员处理方式继承同名<静态>成员处理方式多继承语法菱形继承多态多态案例1、计算器类 纯虚函数和抽象类多态案例2、制作饮品 虚析构和纯虚析构多态案例3、电脑组装 文件写操作读文件二进制写文件二进制…

ClickHouse基础知识(四):ClickHouse 引擎详解

1. 表引擎的使用 表引擎是 ClickHouse 的一大特色。可以说&#xff0c; 表引擎决定了如何存储表的数据。包括&#xff1a; ➢ 数据的存储方式和位置&#xff0c;写到哪里以及从哪里读取数据。 默认存放在/var/lib/clickhouse/data ➢ 支持哪些查询以及如何支持。 ➢ 并发数…

CUDA驱动深度学习发展 - 技术全解与实战

全面介绍CUDA与pytorch cuda实战 关注TechLead&#xff0c;分享AI全维度知识。作者拥有10年互联网服务架构、AI产品研发经验、团队管理经验&#xff0c;同济本复旦硕&#xff0c;复旦机器人智能实验室成员&#xff0c;阿里云认证的资深架构师&#xff0c;项目管理专业人士&…

【SD】保持图片大小 精细化处理 高清放大

首先开启 ADetailer可以修复手部&#xff0c;脸部&#xff0c;全身。 生成一张图片。 best quality,masterpiece,simple_white_background,golden and white theme,Sense of coordination,sense of order,mathematics beauty,(((cover design))),(((((cover art))))),((trim)),…

喜讯!云起无垠获评ISC 2023数字安全创新能力百强双料大奖

近日&#xff0c;第四届数字安全“奥斯卡”——ISC 2023数字安全创新能力百强评选活动在北京圆满闭幕。本次活动旨在挖掘和孵化数字安全领域的“专精特新”力量&#xff0c;共同推进数字中国的安全建设。 在本次评选中&#xff0c;云起无垠凭借其在软件供应链安全领域的创新实…

SpringBoot 3.2.0 结合Redisson接入Redis

依赖版本 JDK 17 Spring Boot 3.2.0 Redisson 3.25.0 工程源码&#xff1a;Gitee 集成Redis步骤 导入依赖 <properties><redisson.version>3.25.0</redisson.version> </properties> <dependencies><dependency><groupId>org.pr…

Vue3-30-路由-嵌套路由的基本使用

什么是嵌套路由 嵌套路由 &#xff1a;就是一个组件内部还希望展示其他的组件&#xff0c;使用嵌套的方式实现页面组件的渲染。 就像 根组件 通过路由渲染 普通组件一样&#xff0c;嵌套路由也是一样的道理。 嵌套路由的相关关键配置 1、<router-view> 标签 声明 被嵌套组…

Echarts中饼图-实现放大显示数据

示例 代码演示 option {tooltip: {trigger: item},legend: {top: 5%,left: center},series: [{name: Access From,type: pie,radius: [40%, 70%],avoidLabelOverlap: false,label: {show: false,position: center},emphasis: {scale: true,//是否开启高亮后扇区的放大效果。s…

python查找mongo中符合条件的json记录

一、需求&#xff1a; 之前有次需要临时查找mongo中存储的json串&#xff0c;符合特定条件的记录&#xff1b; 举个例子&#xff0c;mongo中记录如下图&#xff1a; 其中每条存储的数据大概为&#xff1a; [{"createUser": "Zxtech","paramName&qu…

LVM逻辑卷与扩容

目录 一.LVM&#xff1a; 1.什么是LVM&#xff1a; 2.LVM的基本核心组件&#xff1a; 3.LVM的基本命令&#xff1a; 二.逻辑卷的创建&#xff1a; 第一步&#xff0c;我们先要为虚拟机添加硬盘 然后我们要添加依赖包 然后我们要进行磁盘分区 再添加好分区后&#xff0…

回顾2023,展望2024

时光飞逝&#xff0c;光阴似箭&#xff0c;转眼间又到了一年的年末&#xff0c;现在是2023年12月29日&#xff0c;再过两天就要元旦了&#xff0c;我们也要跨入2024年了。 记录自己的总结&#xff0c;一直想写&#xff0c;不知从何写起&#xff0c;在这一年中&#xff0c;有深夜…