手敲Mybatis(八)-参数处理器

  1. 前言

在之前的章节里边,类PreparedStatementHandler我们还没有处理在执行Sql时的参数,目前是硬编码写死存储的,如:ps.setLong(),这里就只能处理long类型的数据因为写死了,我们需要处理下让它支持设置不同的数据类型。

根据要求我们知道本次最重要的就是参数处理器,考虑下需要怎么解析XML中的参数并放到对应容器里,使用时调用参数处理器进行设置对应参数呢?这就是今天的主题。

2. UML类图

本章涉及的内容并不多,新加了7个类,下面图片标灰色以及蓝色和橘黄色都是新增的类,其余的都是根据需求串流程的修改。

2.1 解析生成类型处理器分析

上节我们把XML解析进行解构处理,那么真正解析处理Sql语句以及参数的处理则在SqlSourceBuilder来处理,我们这节以这个为入口在参数处理时考虑加个参数的类型处理器,用来执行SQL语句时最终设置参数处理,这里我们类型处理器只实现了两个类,LongTypeHandler和StringTypeHanler,实际上还会有很多的TypeHandler,本节实现这两个先了解下。

ParameterMappingTokenHandler:SqlSourceBuilder的内部类,主要处理参数映射的,我们在生成ParameterMapping对象时进行修改从类型注册器里找出是否有当前参数类型的值,有则从类型注册器中取,无则通过反射工具类将对象参数解析进行参数类型获取处理。

ParameterMapping:全局变量里添加一个私有的类型处理器的映射,在调用build的方法时进行绑定赋值。

TypeHandler:类型处理器接口,本节添加的类,由于存在于设置参数时使用,只定义了设置参数的方法。

LongTypeHandler:类型处理器实现类,Long类型处理器,只处理Long类型参数值的设置。

StringTypeHandler:类型处理器实现类,String类型处理器,只处理String类型参数值的设置。

TypeHandlerRegistry:类型处理器注册器,定义了启动时直接注册两种(Long,String)类型处理器以及从注册器获取类型处理器方法,判断是否存在此类型处理器方法等。

2.2 使用参数处理器和类型处理器

我们想象一下在哪里需要使用我们处理好的参数映射呢,是不是在Sql执行时,我们把解析好的Sql语句和参数拿过来然后调用JDBC将Sql语句和参数传过去,那么就需要在执行器中的语句处理器下手修改

因为参数执行处理在Mybatis里需要进入预处理语句处理器,所以从这里开始

PreparedStatementHandler:预处理语句处理器实现在parameterize方法中通过参数处理器进行参数设置,将参数设置到jdbc的PreparedStatement中。

BaseStatementHandler:基础语句处理器,全局变量定义参数处理器,并在构造方法中创建并赋值参数处理器,供PreparedStatementHandler使用。

Configuration:Configuration中修改下添加创建参数处理器的方法供BaseSattementHandler调用

LanguageDriver:语言驱动器,添加了定义了创建参数处理器方法,供Configuration调用

XMLLanguageDriver:语言驱动器实现类,实例化参数处理器DefaultParameterHandler并返回。

ParameterHandler:参数处理器接口,定义了获取参数以及设置参数方法。

DefaultParameterHandler:参数处理器接口实现类,获取参数映射根据参数类型取出对应的类型处理器,并调用对应类型的参数设置,设置完直接执行Sql语句就结束。

本节比较好理解,也比较简单,大家好好学习,好好理解

  1. 代码

// doto MapperMethod的参数处理

3.1 类型处理器(TypeHandler)

在Mybatis源码里有type的包,这个包下都是关于类型的处理

package df.middleware.mybatis.type

我们创建类型处理器接口,定义设置参数方法,需要用到的参数有预处理语句(preparedment)以及第一个参数i,参数值paramter,参数类型jdbcType即可。

/**
 * @Author df
 * @Date 2023/3/13 12:36
 * @Version 1.0
 * 类型处理器接口
 */
public interface TypeHandler<T> {
    /**
     * 设置参数
     */
    void setParameter(PreparedStatement ps,int i,T paramter,JdbcType jdbcType) throws SQLException;
}

3.1.1 抽象类处理器(BaseTypeHandler)

此类使用了模板设计模式,实现了setParameter方法,定义了设置不为空参数setNonNullParameter的抽象方法。通过抽象出基类便于共有化处理,在此类中也可以判断和处理,目前我们暂未有那么多流程,但写出来分析Mybatis源码时也好知道有这样的结构,如果要扩展也非常方便

/**
 * @Author df
 * @Date 2023/3/13 12:39
 * @Version 1.0
 * 类型处理器基类,模板模式
 */
public abstract class BaseTypeHandler<T> implements TypeHandler<T> {

    @Override
    public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
        setNonNullParameter(ps, i, parameter, jdbcType);
    }

    protected abstract void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
}

3.1.2 LONG类型处理器(LongTypeHandler)

此类继承抽象类处理器,实现setNonNullParameter方法,见名知意,处理Long类型结构的参数,所以使用ps.setLong(),使用过jdbc框架的都理解这里,就不扩展了

/**
 * @Author df
 * @Date 2023/3/13 12:44
 * @Version 1.0
 * Long类型处理器
 */
public class LongTypeHandler extends BaseTypeHandler<Long> {
    @Override
    protected void setNonNullParameter(PreparedStatement ps, int i, Long parameter, JdbcType jdbcType) throws SQLException {
        ps.setLong(i, parameter);
    }
}

3.1.3 STRING类型处理器(StringTypeHandler)

此类继承抽象类处理器,实现setNonNullParameter方法,见名知意,处理String类型结构的参数,所以使用ps.setString(i,parameter),

/**
 * @Author df
 * @Date 2023/3/13 12:48
 * @Version 1.0
 */
public class StringTypeHandler extends BaseTypeHandler<String> {
    @Override
    protected void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
        ps.setString(i, parameter);
    }
}

3.1.4 类型处理器注册器(TypeHandlerRegistry)

类型处理注册器主要是提供了注册类型以及根据类型获取对应的类型处理器,在构造方法中将用到的类型注册全部注册进来,在使用时将参数类型传入进来拿到对应的类型处理器即可。同时可以注意到,无论是对象类型,还是基本类型,都是一个类型处理器。只不过在注册的时候多注册了一个。这种操作方式和我们平常的业务开发中,也是一样的。一种是多注册,另外一种是判断处理

/**
 * @Author df
 * @Date 2023/3/13 12:50
 * @Version 1.0
 * 类型处理器注册机
 */
public final class TypeHandlerRegistry {
    private final Map<JdbcType, TypeHandler<?>> JDBC_TYPE_HANDLER_MAP = new EnumMap<JdbcType, TypeHandler<?>>(JdbcType.class);
    private final Map<Type, Map<JdbcType, TypeHandler<?>>> TYPE_HANDLER_MAP = new HashMap();
    private final Map<Class<?>, TypeHandler<?>> ALL_TYPE_HANDLER_MAP = new HashMap<>();

    public TypeHandlerRegistry() {
        // 注册类型注册机
        register(Long.class, new LongTypeHandler());
        register(long.class, new LongTypeHandler());

        register(String.class, new StringTypeHandler());
        register(String.class, JdbcType.CHAR, new StringTypeHandler());
        register(String.class, JdbcType.VARCHAR, new StringTypeHandler());
    }

    private <T> void register(Type javaType, TypeHandler<? extends T> typeHandler) {
        register(javaType, null, typeHandler);
    }

    private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
        if (null != javaType) {
            Map<JdbcType, TypeHandler<?>> map = TYPE_HANDLER_MAP.computeIfAbsent(javaType, k -> new HashMap<>());
            map.put(jdbcType, handler);
        }
        ALL_TYPE_HANDLER_MAP.put(handler.getClass(), handler);
    }

    public <T> TypeHandler<T> getTypeHandler(Class<T> type, JdbcType jdbcType) {
        return getTypeHandler((Type) type, jdbcType);
    }
    // 获取当前类的类型处理器
    private <T> TypeHandler<T> getTypeHandler(Type type, JdbcType jdbcType) {
        Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = TYPE_HANDLER_MAP.get(type);
        TypeHandler<?> handler = null;
        if (jdbcHandlerMap != null) {
            handler = jdbcHandlerMap.get(jdbcType);

        }
        return (TypeHandler<T>) handler;
    }
    // 是否有当前类的类型处理器
    public boolean hasTypeHandler(Class<?> javaType) {
        return hasTypeHandler(javaType, null);
    }

    public boolean hasTypeHandler(Class<?> javaType, JdbcType jdbcType) {
        return javaType != null && getTypeHandler((Type) javaType, jdbcType) != null;
    }
}

3.2 SQL源构造器(SqlSource)

SqlSource本身代码没有改变,它的内部类ParameterMappingTokenHandler在参数映射的时候进行了改变,在得到的参数时进行了判断,判断当前参数类是否有对应的类型处理器,如果没有则认同感反射工具类解析对象类型,最后剑门关属性类型,属性名称传递给ParameterMapping进行构建

public ParameterMapping buildParameterMapping(String content) {
            // 解析参数映射
            Map<String, String> parameterMap = new ParameterExpression(content);
            String property = parameterMap.get("property");
            Class<?> propertyType;
            // 判断是否当前参数类型在类型注册器中存在
            if (typeHandlerRegistry.hasTypeHandler(parameterType)) {
                // 可直接将参数类型赋值给propertyType,传给ParameterMapping使用
                propertyType = parameterType;
            } else if (property != null) {
                // 如果不是基本类型,如对象需要通过反射类解析属性处理。如:xx.xxxx.User
                MetaClass metaClass = MetaClass.forClass(parameterType);
                // 解析完毕获取对应的参数类型赋值propertyType
                if (metaClass.hasGetter(property)) {
                    propertyType = metaClass.getGetterType(property);
                } else {
                    propertyType = Object.class;
                }
            } else {
                propertyType = Object.class;
            }
            System.out.println("构建参数映射 property:" + property + "propertyType:" + propertyType);
            // 构建ParameterMapping
            ParameterMapping.Builder builder = new ParameterMapping.Builder(configuration, property, propertyType);
            return builder.build();
        }

3.2.1 参数映射器(ParameterMapping)

参数映射器改动不多,全局变量里加上了类型处理器typeHandler,并且在内部处理器构建里修改了内容,通过当前的参数映射的java类型获取类型注册器里的类型处理器,并赋值给全局变量typeHandler

 private TypeHandler typeHandler;

public static class Builder {
    public ParameterMapping build() {
            if (parameterMapping.typeHandler == null && parameterMapping.javaType != null) {
                Configuration configuration = parameterMapping.configuration;
                // 得到类型注册器
                TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
                // 将得到的类型处理器赋值给全局变量
                parameterMapping.typeHandler = typeHandlerRegistry.getTypeHandler(parameterMapping.javaType, parameterMapping.jdbcType);
            }
            return parameterMapping;
    }
}

4. 使用参数类型处理器

4.1 参数处理器(ParameterHandler)

package df.middleware.mybatis.executor.parameter

参数处理器接口,定义了设置参数和获取参数对象方法

/**
 * @Author df
 * @Date 2023/3/14 12:43
 * @Version 1.0
 */
public interface ParameterHandler {
    /**
     * 获取参数
     */
    Object getParameterObject();

    void setParameters(PreparedStatement ps) throws SQLException;
}

4.2参数处理器实现类(DefaultParameterHandler)

package df.middleware.mybatis.scripting.defaults;

实现参数类型处理器接口,设置参数时循环处理参数通过ParameterMapping获取的类型处理器可直接调用不同的类型处理器设置参数,完成动态的调用不同的参数类型

/**
 * @Author df
 * @Date 2023/3/14 12:45
 * @Version 1.0
 * 默认参数处理器
 */
public class DefaultParameterHandler implements ParameterHandler {
    private final TypeHandlerRegistry typeHandlerRegistry;
    private final MappedStatement mappedStatement;
    private final Object parameterObject;
    private BoundSql boundSql;
    private Configuration configuration;

    public DefaultParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
        this.typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();
        this.mappedStatement = mappedStatement;
        this.parameterObject = parameterObject;
        this.boundSql = boundSql;
        this.configuration = mappedStatement.getConfiguration();
    }

    @Override
    public Object getParameterObject() {
        return parameterObject;
    }

    @Override
    public void setParameters(PreparedStatement ps) throws SQLException {
        // 得到参数映射的结果
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        if (null != parameterMappings) {
            for (int i = 0; i < parameterMappings.size(); i++) {
                // 循环得到ParameterMapping
                ParameterMapping parameterMapping = parameterMappings.get(i); 
                //得到属性名称
                String propertyName = parameterMapping.getProperty();
                Object value;
                // 判断在哪里获取值
                if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                    value = parameterObject;
                } else {
                    MetaObject metaObject = configuration.newMetaObject(parameterObject);
                    value = metaObject.getValue(propertyName);
                }
                JdbcType jdbcType = parameterMapping.getJdbcType();
                // 设置参数
                System.out.println("根据每个ParameterMapping中的TypeHandler设置对应的参数信息 value:" + value);
                // 从parameterMapping获取类型处理器
                TypeHandler typeHandler = parameterMapping.getTypeHandler();
                // 可策略的调用不用的类型处理器
                typeHandler.setParameter(ps, i + 1, value, jdbcType);
            }
        }
    }
}

4.3 基础语句处理器(BaseStatementHandler)

执行器里的BaseStatementHandler的一些小修改,添加了全局变量参数处理器ParameterHandler,

在构造方法中通过configuration实例化ParameterHandler,并将得到的ParameterHandler赋值给全局变量的一个操作

 protected final ParameterHandler parameterHandler;
 public BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, ResultHandler resultHandler, BoundSql boundSql) {
        // 省略其他章节有的,添加处理parameterHandler
        this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
 }

4.4 Configuration

configuration中里又依赖了语句驱动器创建参数处理器statement.getLang()就是获取LanguageDriver,职责分明,语句驱动器定义和实现创建参数处理器

  public ParameterHandler newParameterHandler(MappedStatement statement, Object paramObject, BoundSql boundSql) {
        // 创建参数处理器
        ParameterHandler parameterHandler = statement.getLang().createParameterHandler(statement, paramObject, boundSql);
        // 插件的一些参数,也是在这里处理,暂时不添加这部分内容 interceptorChain.pluginAll(parameterHandler);
        return parameterHandler;
    }

4.5 语言驱动器(LanguageDriver)

添加了createParameterHandler()方法的处理,都蛮简单的

/**
 * @Author df
 * @Date 2022/12/5 14:16
 * @Version 1.0
 * 脚本语言驱动
 */
public interface LanguageDriver {
    /**
     * 创建参数处理器
     */
    ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql);
}

4.6 XMLLanguageDriver

xml语言驱动器,实例化DefaultParameterHandler类并返回,最终流转回BaseStatementHandler里供执行语句和参数使用

/**
 * @Author df
 * @Date 2022/12/5 14:28
 * @Version 1.0
 * XML语言驱动器
 */
public class XMLLanguageDriver implements LanguageDriver {
    @Override
    public ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
        return new DefaultParameterHandler(mappedStatement, parameterObject, boundSql);
    }
}

4.7 预处理语句处理器(PreparedStatementHandler)

在PreparedStatementHandler中可直接调用自己父类的参数处理器即可设置参数

// 参数设置
    @Override
    public void parameterize(Statement statement) throws SQLException {
      parameterHandler.setParameters((PreparedStatement) statement);
    }

4.8 MapperMethod

再MapperMethod里添加了内部类方法签名类,此类处理封装参数数据,以前是直接将args参数直接传给selectOne方法,调整为转换后的对象传给selectOne方法。

public static class MethodSignature {
        private final SortedMap<Integer, String> params;

        public MethodSignature(Configuration configuration, Method method) {
            this.params = Collections.unmodifiableSortedMap(getParams(method));
        }

        public Object convertArgsToSqlCommandParam(Object[] args) {
            final int paramCount = params.size();
            if (args == null || paramCount == 0) {
                // 如果没有参数
                return null;
            } else if (paramCount == 1) {
                // 获得参数
                return args[params.keySet().iterator().next().intValue()];
            } else {
                // 否则,返回一个ParamMap,修改参数名,参数名就是其位置
                final Map<String, Object> param = new ParamMap<>();
                int i = 0;
                for (Map.Entry<Integer, String> entry : params.entrySet()) {
                    // 1.先加一个#{0},#{1},#{2}...参数
                    param.put(entry.getValue(), args[entry.getKey().intValue()]);
                    final String genericParamName = "param" + (i + 1);
                    if (!param.containsKey(genericParamName)) {
                        /*
                         * 2.再加一个#{param1},#{param2}...参数
                         * 你可以传递多个参数给一个映射器方法。如果你这样做了,
                         * 默认情况下它们将会以它们在参数列表中的位置来命名,比如:#{param1},#{param2}等。
                         * 如果你想改变参数的名称(只在多参数情况下) ,那么你可以在参数上使用@Param(“paramName”)注解。
                         */
                        param.put(genericParamName, args[entry.getKey()]);
                    }
                    i++;
                }
                return param;
            }

        }
        // 获取参数处理成下标方便查找
        private SortedMap<Integer, String> getParams(Method method) {
            // 用一个TreeMap,这样就保证还是按参数的先后顺序
            final SortedMap<Integer, String> params = new TreeMap<>();
            final Class<?>[] argsType = method.getParameterTypes();
            for (int i = 0; i < argsType.length; i++) {
                String paramName = String.valueOf(params.size());
                // 不做 Param 的实现,这部分不处理。如果扩展学习,需要添加 Param 注解并做扩展实现。
                params.put(i, paramName);
            }
            return params;
        }
    }

MapperMethod中的修改,全局变量里添加了MethodSignature属性,在构造函数里赋值,并在执行方法execute中进行参数转换处理就ok了

 private final MethodSignature method;

    public MapperMethod(Class<?> mapperInterface, Method method, Configuration configuration) {
        command = new SqlCommand(configuration, mapperInterface, method);
        this.method=new MethodSignature(configuration,method);
    }

    public Object execute(SqlSession sqlSession, Object[] args) {
        Object result = null;
        switch (command.getType()) {
            case INSERT:
                break;
            case DELETE:
                break;
            case UPDATE:
                break;
            case SELECT:
                Object param=method.convertArgsToSqlCommandParam(args);
                result = sqlSession.selectOne(command.getName(), param);
                break;
            default:
                throw new RuntimeException("Unknown execution method for: " + command.getName());
        }
        return result;
    }

3. 测试

user_Mapper.xml里添加一个queryUserInfo的查询Sql。参数类型是一个对象

<select id="queryUserInfo" parameterType="df.middleware.mybatis.po.User" resultType="df.middleware.mybatis.po.User">
        SELECT id, userId, userName, userHead
        FROM user
        where id = #{id} and userId = #{userId}
</select>

单元测试,执行test_queryUserInfo方法

/**
 * @Author df
 * @Date 2022/12/15 9:00
 * @Version 1.0
 */
public class TestApi {
    private SqlSession sqlSession;

    @Before
    public void init() throws IOException {
        // 1. 从SqlSessionFactory中获取SqlSession
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader("mybatis-config-datasource.xml"));
        sqlSession = sqlSessionFactory.openSession();
    }


    @org.junit.Test
    public void test_queryUserInfoById() {
        // 1. 获取映射器对象
        IUserDao userDao = sqlSession.getMapper(IUserDao.class);

        // 2. 测试验证:基本参数
        User user = userDao.queryUserInfoById(1L);
        System.out.println("测试结果:" + user.getUserName());
    }

    @org.junit.Test
    public void test_queryUserInfo() {
        // 1. 获取映射器对象
        IUserDao userDao = sqlSession.getMapper(IUserDao.class);

        // 2. 测试验证:对象参数
        User user = userDao.queryUserInfo(new User(1L, "10001"));
        System.out.println(user);
        System.out.println(user.getUserName());
    }

}

执行结果,从打印来看我们能够构建long类型和String类型的处理器,最终根据类型处理器设置对应的参数类型的值,并最终执行成功了Sql语句

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

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

相关文章

【Linux 网络编程4】网络层--UDP/TCP协议,3次握手4次挥手、粘包问题等

netstat命令-n.拒绝显示别名&#xff0c;能显示数字的全部转化成数字(IPPORT)-l 仅列出有在 Listen (监听) 的服务的状态-p 显示建立相关链接的程序名&#xff08;pid&#xff09;-t 仅显示tcp相关选项-u 仅显示udp相关选项 2.UDP协议2.1.全双工和半双工的区别全双工&#xff1…

了解Session、LocatStorage、Cache-Control、ETag区别

一、cookie与session有什么区别&#xff1f; 1. 由于HTTP协议是无状态的协议&#xff0c;所以服务端需要记录用户的状态时&#xff0c;就需要用某种机制来识具体的用户&#xff0c;这个机制就是Session.典型的场景比如购物车&#xff0c;当你点击下单按钮时&#xff0c;由于HT…

SpringBoot学习笔记(4)-分析 SpringBoot 底层机制

文章目录4. 分析 SpringBoot 底层机制4.1 Tomcat启动分析4.2 创建Spring 容器4.3 将Tomcat 和 Spring 容器关联&#xff0c;并启动 Spring 容器4.4 扩展-debug查看 ac.refresh()4. 分析 SpringBoot 底层机制 【Tomcat 启动分析 Spring 容器初始化Tomcat 如何关联 Spring 容器…

微软分享修复WinRE BitLocker绕过漏洞的脚本

微软发布了一个脚本&#xff0c;可以更轻松地修补 Windows 恢复环境 (WinRE) 中的 BitLocker 绕过安全漏洞。 此 PowerShell 脚本 (KB5025175) 简化了保护 WinRE 映像以防止试图利用CVE-2022-41099漏洞的过程&#xff0c;该漏洞使攻击者能够绕过 BitLocker 设备加密功能系统存…

jvm03垃圾回收篇

p134 垃圾回收相关章节的说明 p135 什么是GC 为什么需要GC P136 了解早起垃圾回收行为 p137 java自动内存管理介绍 p138垃圾回收相关算法概述 p139引用计数算法的原理及优缺点 p140 python引用计数实施方案 p141 可达性分析算法与GC ROOTS p142 对象的finalization机制 p143 代…

【MyBatis】字段名和属性名不同时,如何处理

目录 前言 1、返回类型&#xff1a;resultType 2、返回字典映射&#xff1a;resultMap 2.1、字段名和属性名不同怎么处理 解决方案一&#xff1a;使用resultMap 解决方案二&#xff1a;使用as起别名 3、多表查询 总结&#xff1a; 前言 在之前的文章中&#xff0c;我们可…

TXT 和 SEV技术小知识

1.Intel TXT 可信执行技术(Trusted Execute Technology&#xff0c;TXT)是Intel公司的可信计算技术&#xff0c;主要通过改造芯片组和CPU&#xff0c;增加安全特性&#xff0c;通过结合一个基于硬件的安全设备—可信平台模块(Trusted Platform Module&#xff0c;TPM)&#xf…

蓝桥杯C/C++VIP试题每日一练之Sine之舞

💛作者主页:静Yu 🧡简介:CSDN全栈优质创作者、华为云享专家、阿里云社区博客专家,前端知识交流社区创建者 💛社区地址:前端知识交流社区 🧡博主的个人博客:静Yu的个人博客 🧡博主的个人笔记本:前端面试题 个人笔记本只记录前端领域的面试题目,项目总结,面试技…

Python 之列表推导式

文章目录参考描述列表推导式举个栗子基本形式一般式基本形式&#xff08;高阶&#xff09;判断使用逻辑运算符笛卡尔积拆解变量污染列表推导式参考 项目描述流畅的 PythonLuciano Ramalho 著 / 安道 吴珂 译搜索引擎Bing 描述 项目描述Python3.10.6 列表推导式 列表推导式是…

python---函数的进阶

函数的进阶 1.8函数的进阶 1.8.1函数作为参数进行传入 1.简介&#xff1a;函数作为范围进行传递到函数中进行操作 2.函数作为参数传入到函数中 3.函数调用和逻辑传入之间的区别 一个是作为数据进行传入&#xff0c;但是调用的函数时一定的一个作为逻辑进行调用&#xff0c;但是…

PTA——1036 跟奥巴马一起编程、1037 在霍格沃茨找零钱、1038 统计同成绩学生、1039 到底买不买、1040 有几个PAT

1036 跟奥巴马一起编程 解决代码 模拟题目即可。 #include<iostream> #include<vector> #include<algorithm> using namespace std; int main(){int n;char c;cin>>n>>c;double col(double)n/2;int n0n/2;if(col>n00.4) n01; for(int i0;i&…

华为路由器 基本ACL配置

1、什么是ACL&#xff1f; 访问控制列表ACL&#xff08;Access Control List&#xff09;是由一条或多条规则组成的集合。所谓规则&#xff0c;是指描述报文匹配条件的判断语句&#xff0c;这些条件可以是报文的源地址、目的地址、端口号等。 ACL本质上是一种报文过滤器&…

Android 高通Camera2 Camera Device Close

1、很多人看到这个日志第一感觉可能觉得哪里没有合理释放&#xff0c;于是带着这个思路去进行百度探索 2、一开始我去寻找 ImageReader.OnImageAvailableListener 这个问题 var afterBitmap: Bitmap? null/**监听拍照的图片 */private val imageAvailableListener ImageRead…

GPT免费网站分享(持续更新)

大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的见解。曾经辅导过若干个非计算机专业的学生进入到算法…

从二叉查找树到B*树,一文搞懂搜索树的演进!|金三银四系列

对于准备面试这篇再适合不过了&#xff01;详细讲解了从BST、AVL、红黑树、B树、B树最后到B*树的演进过程&#xff0c;以及各种结构的优劣。点击上方“后端开发技术”&#xff0c;选择“设为星标” &#xff0c;优质资源及时送达在面试中&#xff0c;面试官很容易抛出这样的问题…

Java设计模式-2、⼯⼚模式

⼯⼚模式 工厂模式是对简单工厂的一个衍生&#xff0c;解决了许多简单工厂模式的问题。 一、说⼀说简单⼯⼚模式 简单⼯⼚模式指由⼀个⼯⼚对象来创建实例&#xff0c;客户端不需要关注创建逻 辑&#xff0c;只需提供传⼊⼯⼚的参数。 适⽤于⼯⼚类负责创建对象较少的情况&a…

wait讲解

hello啊,今天为大家带来wait的相关介绍 开始正题之前,我们要先进行一点知识点的补充 上一期我们更新了一期关于线程安全的知识,对于volatile在这里在做出一些补充 有些文章上说线程修改一个变量的时候,从主内存读取到工作内存上,在工作内存上修改完以后再返回主内存 由于t1线程…

[数据库原理与应用]educoder-MySQL 单表查询(一)

目录 第1关&#xff1a;用like匹配字符串 第2关&#xff1a;用BETWEEN AND表达查询范围 第3关&#xff1a;空值的判断 第4关&#xff1a;集合运算符IN的应用 第5关&#xff1a;消除重复结果 第6关&#xff1a;聚合函数应用 第7关&#xff1a;分组查询 第8关&#xf…

基于GPT3.5实现本地知识库解决方案-利用向量数据库和GPT向量接口-实现智能回复并限制ChatGPT回答的范围...

标题有点长&#xff0c;但是基本也说明出了这篇文章的主旨&#xff0c;那就是利用GPT AI智能回答自己设置好的问题 既能实现自己的AI知识库机器人&#xff0c;又能节省ChatGPT调用的token成本费用。 代码仓库地址 document.ai: 基于GPT3.5的通用本地知识库解决方案 下面图片是整…

【数据分析实战】基于python对Airbnb房源进行数据分析

文章目录&#x1f4da;引言&#x1f4d6;数据加载以及基本观察&#x1f4c3;缺失值观察及处理&#x1f516;缺失值观察以及可视化&#x1f516;缺失值处理&#x1f4c3;异常值观察及处理&#x1f4d6;数据探索&#x1f4a1;哪个区域的房源最受欢迎&#xff1f;&#x1f4a1;哪种…