当前位置: 首页 > news >正文

Mybatis 源码 ∞ :杂七杂八

文章目录

  • 一、前言
  • 二、TypeHandler
  • 三、KeyGenerator
  • 四、Plugin
    • 1 Interceptor
    • 2 org.apache.ibatis.plugin.Plugin
    • 3. 调用场景
  • 五、Mybatis 嵌套映射 BUG
    • 1. 示例
    • 2. 原因
    • 3. 解决方案
  • 六、discriminator 标签
  • 七、其他
    • 1. RowBounds
    • 2. ResultHandler
    • 3. @MapKey

一、前言

Mybatis 官网 以及 本系列文章地址:

  1. Mybatis 源码 ① :开篇
  2. Mybatis 源码 ② :流程分析
  3. Mybatis 源码 ③ :SqlSession
  4. Mybatis 源码 ④ :TypeHandler
  5. Mybatis 源码 ∞ :杂七杂八

主要是 Mybatis 的一些杂七杂八的内容,用于自己可以快速定位一些问题,所以部分内容写比较随性

二、TypeHandler

关于 TypeHandler 的使用,各处都是文章,这里就不再贴出完整的项目,仅对关键内容进行说明。


  1. 注册或声明 TypeHandler :
    • 通过 mybatis.type-handlers-package 直接指定包路径 :该路径下的 TypeHandler 实现类都会被自动注册,并且只要是符合转换类型无论是入参还是出参都会经过转换

      mybatis.type-handlers-package=com.kingfish.config.handler
      
    • Xml 中 通过如下标签注册,可以指定注册哪些 TypeHandler,并且只要是符合转换类型无论是入参还是出参都会经过转换。

      <configuration><typeHandlers><typeHandler handler="com.kingfish.config.handler.PwdTypeHandler"/></typeHandlers>
      </configuration>
      
    • <result> 标签 通过 typeHandler 属性指定,指定某个属性使用 TypeHandler 查询,需要注意的是,仅仅是返回类型是当前 ResultMap 时才会进行类型转换:

      <resultMap id="BaseResultMap" type="com.kingfish.entity.SysUser"><result property="password" column="password" jdbcType="VARCHAR" typeHandler="com.kingfish.config.handler.PwdTypeHandler"/>
      </resultMap>
      

  1. 定义密码加解密类型转换器 : PwdTypeHandler。密码不能明文存储在库中,所以当我们需要对DB 中的密码进行加密处理。这里便可以通过 TypeHandler 来实现(在新增、更新、删除时自动加密,在查询时自动解密)

    public class PwdTypeHandler extends BaseTypeHandler<String> {private static final SymmetricCrypto AES = new SymmetricCrypto(SymmetricAlgorithm.AES, "1234567890123456".getBytes());@Overridepublic void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {ps.setString(i, AES.encryptBase64(parameter));}@Overridepublic String getNullableResult(ResultSet rs, String columnName) throws SQLException {return AES.decryptStr(rs.getString(columnName));}@Overridepublic String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {return AES.decryptStr(rs.getString(columnIndex));}@Overridepublic String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {return AES.decryptStr(cs.getString(columnIndex));}
    }
    

需要注意的是

  1. 如果以注册的方式(mybatis.type-handlers-package 或者 <typeHandlers> 标签)注册该 TypeHandler。只要是符合其类型转换的情况都会使用该处理器转化,如上面 PwdTypeHandler 转换类型是 String,即只要字段类型是 String,都会被该处理器处理,比如 user_name 也是 String 类型,入库后也会被加密。这种情况并非我们想要的。所以我们可以通过自定义复杂类型的方式来避免将其他类型转换,或者通过下面 标签属性 的方式来转换。

  2. 如果是通过 标签的 typeHandler 属性指定,则只会在查询返回结果时对指定结果集中的指定字段进行处理。

    	<!-- 返回转换(忽略了其他字段) --><resultMap id="BaseResultMap" type="com.kingfish.entity.SysUser"><result property="password" column="password" jdbcType="VARCHAR" typeHandler="com.kingfish.config.handler.PwdTypeHandler"/></resultMap><!-- 插入转换 --> <insert id="insert" keyProperty="id" useGeneratedKeys="true" >insert into sys_user(create_time, modify_time, user_name, password, status, is_delete, nick_name, phone, extend)values (#{createTime}, #{modifyTime}, #{userName}, #{password, typeHandler=com.kingfish.config.handler.PwdTypeHandler}, #{status}, #{isDelete}, #{nickName}, #{phone}, #{extend})</insert><!-- 更新转换(忽略了其他字段)--><update id="update">update sys_user<set><if test="password != null and password != ''">password = #{password, typeHandler=com.kingfish.config.handler.PwdTypeHandler}</if></set>where id = #{id}</update>
    


三、KeyGenerator

在Mybatis中,执行insert操作时,如果我们希望返回数据库生成的自增主键值,那么就需要使用到KeyGenerator对象。

关于 KeyGenerator 的内容,这里直接摘取 Mybatis之KeyGenerator 的部分内容,详细部分请阅读原文


KeyGenerator 定义如下:

public interface KeyGenerator {// BaseStatementHandler 构造函数中调用,在sql 执行前调用void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter);// StatementHandler#update 中会调用,在sql 执行后调用void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter);}

存在如下三个实现类:

  • Jdbc3KeyGenerator:用于处理数据库支持自增主键的情况,如MySQL的auto_increment。
  • NoKeyGenerator:空实现,不需要处理主键。
  • SelectKeyGenerator:用于处理数据库不支持自增主键的情况,比如Oracle的sequence序列。

下面以 Jdbc3KeyGenerator 为例简单看下

@Overridepublic void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {processBatch(ms, stmt, parameter);}public void processBatch(MappedStatement ms, Statement stmt, Object parameter) {// 获取key属性名,一般来说即 id,说明 key 就是属性名为 id 的字段	final String[] keyProperties = ms.getKeyProperties();if (keyProperties == null || keyProperties.length == 0) {return;}try (ResultSet rs = stmt.getGeneratedKeys()) {final ResultSetMetaData rsmd = rs.getMetaData();final Configuration configuration = ms.getConfiguration();// 如果列的长度小于 key的长度则不处理if (rsmd.getColumnCount() < keyProperties.length) {// Error?} else {// 赋值keyassignKeys(configuration, rs, rsmd, keyProperties, parameter);}} catch (Exception e) {throw new ExecutorException("Error getting generated key or setting result to parameter object. Cause: " + e, e);}}private void assignKeys(Configuration configuration, ResultSet rs, ResultSetMetaData rsmd, String[] keyProperties,Object parameter) throws SQLException {if (parameter instanceof ParamMap || parameter instanceof StrictMap) {// Multi-param or single param with @Param// 多个参数或单一参数 使用 @Param 场景assignKeysToParamMap(configuration, rs, rsmd, keyProperties, (Map<String, ?>) parameter);} else if (parameter instanceof ArrayList && !((ArrayList<?>) parameter).isEmpty()&& ((ArrayList<?>) parameter).get(0) instanceof ParamMap) {// Multi-param or single param with @Param in batch operation// 多个参数或单一参数 使用 @Param 批量操作的场景assignKeysToParamMapList(configuration, rs, rsmd, keyProperties, (ArrayList<ParamMap<?>>) parameter);} else {// Single param without @Param// 单个参数未使用 @Param 的场景assignKeysToParam(configuration, rs, rsmd, keyProperties, parameter);}}

下面以单个参数未使用 @Param 场景为例

  private void assignKeysToParam(Configuration configuration, ResultSet rs, ResultSetMetaData rsmd,String[] keyProperties, Object parameter) throws SQLException {// 将对象转换为 集合,就是简单封装Collection<?> params = collectionize(parameter);if (params.isEmpty()) {return;}List<KeyAssigner> assignerList = new ArrayList<>();for (int i = 0; i < keyProperties.length; i++) {assignerList.add(new KeyAssigner(configuration, rsmd, i + 1, null, keyProperties[i]));}Iterator<?> iterator = params.iterator();// 遍历参数while (rs.next()) {if (!iterator.hasNext()) {throw new ExecutorException(String.format(MSG_TOO_MANY_KEYS, params.size()));}// 获取参数Object param = iterator.next();// 反射将Key 值映射到 参数对应的属性上 (即将id的值映射到 param 的id 属性上)assignerList.forEach(x -> x.assign(rs, param));}}

四、Plugin

Mybatis支持我们通过插件的方式扩展具体的过程,我们可以通过如下方式:

// 声明当前类是个拦截器,拦截的类型是 StatementHandler,方法名是 prepare,该方法的入参 Connection 和 Integer 类型。
// 当 StatementHandler 的 prepare  方法执行时会被该拦截器拦截
@Component
@Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class, Integer.class}) })
public class DemoPlugins implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {System.out.println("invocation = " + invocation);return null;}
}

下面我们来看看代码的具体实现

在上面我们提到负责执行Sql的 Executor 被 Interceptor 包装了,实际上并非仅仅只有 执行器会被拦截器拦截,因此我们这里来看看 Mybatis 拦截器的具体实现。

如下是 InterceptorChain#pluginAll 的实现,当创建 Executor、ParameterHandler、ResultSetHandler、StatementHandler 时都会调用该方法:

  public Object pluginAll(Object target) {for (Interceptor interceptor : interceptors) {target = interceptor.plugin(target);}return target;}

可以看到,该方法会通过 Interceptor#plugin 方法对 target 进行包装,具体如下:

1 Interceptor

org.apache.ibatis.plugin.Interceptor 定义如下:

public interface Interceptor {Object intercept(Invocation invocation) throws Throwable;default Object plugin(Object target) {// 使用当前对象包装 targetreturn Plugin.wrap(target, this);}// XML 解析 interceptor 时会调用该方法进行属性赋值,具体看实现default void setProperties(Properties properties) {// NOP}}

这里可以看到,Mybatis 通过 Plugin#wrap 方法代理并返回了一个新的对象。下面我们来看下 org.apache.ibatis.plugin.Plugin 的具体实现。

2 org.apache.ibatis.plugin.Plugin

org.apache.ibatis.plugin.Plugin#wrap 实现如下:

  public static Object wrap(Object target, Interceptor interceptor) {// 1. 获取方法签名Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);Class<?> type = target.getClass();// 获取 type 的所有实现接口Class<?>[] interfaces = getAllInterfaces(type, signatureMap);if (interfaces.length > 0) {// 创建新的代理对象,这里看到,处理器实际上是Pluginreturn Proxy.newProxyInstance(type.getClassLoader(),interfaces,new Plugin(target, interceptor, signatureMap));}return target;}// 解析Intercepts注解并获取方法签名private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {// 获取 @Intercepts 注解信息Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);// issue #251if (interceptsAnnotation == null) {throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());}// 获取 @Intercepts  注解的 @Signature 签名信息Signature[] sigs = interceptsAnnotation.value();Map<Class<?>, Set<Method>> signatureMap = new HashMap<>();for (Signature sig : sigs) {// 创建 代理方法集合,被代理的方法会保存到该 Set 中Set<Method> methods = MapUtil.computeIfAbsent(signatureMap, sig.type(), k -> new HashSet<>());try {// 获取 @Signature.type 指定的类,方法名为 sig.method(),参数为 sig.args() 的方法Method method = sig.type().getMethod(sig.method(), sig.args());methods.add(method);} catch (NoSuchMethodException e) {throw new PluginException("Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + e, e);}}// 返回代理方法签名return signatureMap;}

可以看到,这里会为 target 创建一个代理对象,代理处理器由 Plugin 来担任,Plugin#invoke 方法如下:

  @Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {// 从代理方法签名中获取当前类的代理方法,如果当前方法需要代理则进行代理,否则执行调用Set<Method> methods = signatureMap.get(method.getDeclaringClass());if (methods != null && methods.contains(method)) {// 执行代理拦截器,这里 interceptor 实际上是 Interceptor 的实现类,也就是 Mybatis 的插件类return interceptor.intercept(new Invocation(target, method, args));}return method.invoke(target, args);} catch (Exception e) {throw ExceptionUtil.unwrapThrowable(e);}}

3. 调用场景

在 Mybatis 中,插件的包装调用都在 Configuration 中,如下

 public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);return parameterHandler;}public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,ResultHandler resultHandler, BoundSql boundSql) {ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);return resultSetHandler;}public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);return statementHandler;}public Executor newExecutor(Transaction transaction, ExecutorType executorType) {executorType = executorType == null ? defaultExecutorType : executorType;executorType = executorType == null ? ExecutorType.SIMPLE : executorType;Executor executor;if (ExecutorType.BATCH == executorType) {executor = new BatchExecutor(this, transaction);} else if (ExecutorType.REUSE == executorType) {executor = new ReuseExecutor(this, transaction);} else {executor = new SimpleExecutor(this, transaction);}if (cacheEnabled) {executor = new CachingExecutor(executor);}executor = (Executor) interceptorChain.pluginAll(executor);return executor;}

这里可以看到Mybatis Plugin 的实现还是比较简单的,通过注解解析,来创建对应类的对应方法的拦截器,(如 PageHelper 的实现核心就是通过 com.github.pagehelper.PageInterceptor 来完成的。)

五、Mybatis 嵌套映射 BUG

1. 示例

Mybatis 嵌套映射在行数据完全相同时 (这里的行数据完全相同指的是sql 查询出来的数据万完全相同,而非 Mybatis 的ResultMap 映射的字段的值完全相同)会丢失的缺陷,以下面为例子 :

  1. sys_user 表数据如下
    在这里插入图片描述

  2. sys_role 数据如下
    在这里插入图片描述

  3. 执行如下SQL, 该 Sql 目的是为了查询有几个用户具有admin 权限,这里可以看到使用了Left join 所以会返回两条完全相同的数据:

            SELECTsr.*,su.user_name user_user_name,su.PASSWORD user_passwordFROMsys_role srLEFT JOIN sys_user su ON sr.id = su.role_idwhere sr.id = 1
    

    执行结果如下:在这里插入图片描述

  4. 但实际上如果通过Mybatis 执行上述逻辑则会出现错误结果如下:

    SysRoleDto 如下,这里不再贴出SysUser:

    public class SysRoleDto {/*** 自增主键ID*/private Long id;/*** 用户名*/private String roleName;/*** 状态*/private String status;/*** 用户*/private List<SysUser> sysUsers;
    }
    

    Mapper 如下:

    <mapper namespace="com.kingfish.dao.SysRoleDao"><resultMap id="BaseResultMap" type="com.kingfish.entity.SysRole"><result property="id" column="id" jdbcType="INTEGER"/><result property="roleName" column="role_name" jdbcType="VARCHAR"/><result property="status" column="status" jdbcType="VARCHAR" /><!-- 忽略余下属性 --></resultMap><!-- 内部嵌套映射 --><resultMap id="InnerNestMap" type="com.kingfish.entity.dto.SysRoleDto" extends="BaseResultMap"><!-- 指定 sysUsers 属性都是前缀为 user_ 的属性 --><collection property="sysUsers" columnPrefix="user_"resultMap="com.kingfish.dao.SysUserDao.BaseResultMap"></collection></resultMap><!-- 通过联表查询出来多个属性,如果属性名跟 sysUsers 对应的com.kingfish.dao.SysUserDao.BaseResultMap配置的属性名一致则会映射上去 (属性名映射规则受到columnPrefix影响) --><select id="selectRoleUser" resultMap="InnerNestMap">SELECTsr.*,su.user_name user_user_name,su.PASSWORD user_passwordFROMsys_role srLEFT JOIN sys_user su ON sr.id = su.role_idwhere sr.id = 1</select>
    </mapper>
    
  5. 执行结果如下,可以发现 sysUsers 属性少了一条记录,因为这里两条查询的记录相同 在nestedResultObjects 中被判断已经存在。
    在这里插入图片描述

  6. 如果我们把其中一个【张三】改成【李四】,其余全都不动,那么sysUsers两条记录数据就不相同,则不会出现这种问题,如下:
    在这里插入图片描述
    执行结果如下:
    在这里插入图片描述


2. 原因

该缺陷的原因在于在 Mybatis 中会缓存嵌套对象到 DefaultResultSetHandler#nestedResultObjects 中,而缓存的key 的生成策略可以简单理解为 resultMapid + 属性名 + 属性值。而上面的例子中 Sql正常执行是如下数据,可以看到查出来的两行数据完全相同:
在这里插入图片描述
当处理第一条数据时一切正常,而因为是嵌套映射则会将当前行数据缓存到 DefaultResultSetHandler#nestedResultObjects 中。当处理到第二条数据时,
在 DefaultResultSetHandler#applyNestedResultMappings 方法中从 nestedResultObjects 获取到了缓存,从而不会将该行数据保存, 如下图:

在这里插入图片描述


3. 解决方案

解决方案就是保证两行数据不完全相同,比如这里可以通过增加 sys_user 的id 查询保证数据的唯一性, 如下:

  SELECTsr.*,su.id user_id,su.user_name user_user_name,su.PASSWORD user_passwordFROMsys_role srLEFT JOIN sys_user su ON sr.id = su.role_idwhere sr.id = 1

在这里插入图片描述

六、discriminator 标签

我们以下面的情况为例:

    <resultMap id="CollectionBaseResultMap" type="com.kingfish.entity.dto.SysUserDto" extends="BaseResultMap"><discriminator javaType="java.lang.Integer" column="id"><!-- value = '1' 的情况下是 resultType, Mybatis会为resultType自动生成一个 ResultMap, discriminatedMapId  是 com.kingfish.dao.SysUserDao.mapper_resultMap[CollectionBaseResultMap]_discriminator_case[1]  --><case value="1" resultType="com.kingfish.entity.dto.SysUserDto"><result column="user_name" property="extend1"/></case><!-- value = '1' 的情况下是 resultMap, discriminatedMapId 即为 CollectionBaseResultMap 的id : com.kingfish.dao.SysUserDao.CollectionBaseResultMap--><case value="2" resultMap="CollectionBaseResultMap"><result column="nick_name" property="extend1"/></case></discriminator></resultMap>

这里需要注意 :

  1. discriminator 标签中 case 中使用 resultType 和 resultMap 的 discriminatedMapId 并不相同, 返回类型是 resultType 时 则会自动生成一个 ResultMap,
  2. resultType情况下需要自己重新对名字进行转换,因为没有 ResultMap 的转换,变量名无法对应。resultMap情况下会忽略 case 条件下的Result ,因为直接从缓存中获取之前加载好的 CollectionBaseResultMap结构了。

七、其他

1. RowBounds

Mybatis可以通过传参中的 RowBounds 可以完成逻辑分页,但不推荐,因为所有的数据都是查询到内存中再筛选。如下:

	// 逻辑分页查询 :入参中有 RowBounds 参数List<SysMenuDto> selectByParam(RowBounds rowBounds);

2. ResultHandler

Mybatis可以通过传参中的 ResultHandler 可以结果集处理,而不再通过 Mapper Method 方法再返回结果,如果不指定,则默认是通过 DefaultResultHandler 来处理。如下:

	// 无返回值 && 入参中有 ResultHandler 实例void selectByParam(ResultHandler resultHandler);

官方对 ResultHandler 的说明【ResultHandler 参数允许自定义每行结果的处理过程。你可以将它添加到 List 中、创建 Map 和 Set,甚至丢弃每个返回值,只保留计算后的统计结果。你可以使用 ResultHandler 做很多事,这其实就是 MyBatis 构建 结果列表的内部实现办法。】

需要注意的是

  • ResultHandler 要求方法必须无返回值,在 MapperMethod#execute 中会判断进行该判断:
    在这里插入图片描述

  • DefaultResultSetHandler#handleResultSet 中判断了如果指定了 ResultHandler 则使用指定的,否则使用 DefaultResultHandler:

    在这里插入图片描述

3. @MapKey

官方描述 :供返回值为 Map 的方法使用的注解。它使用对象的某个属性作为 key,将对象 List 转化为 Map。属性:value,指定作为 Map 的 key 值的对象属性名。

即: 当一个查询方法想要返回 Map 时,可以通过 @MapKey 来指定用来聚合的key 是什么字段,如下:

        <select id="selectRoleForMap" resultMap="BaseResultMap">select *from sys_role</select
    @MapKey("id")Map<Long, SysRoleDto> selectRoleForMap();

查询结果会把 id 当做 Map 的key 字段来聚合,返回如下:
在这里插入图片描述


源码处理逻辑在 :org.apache.ibatis.binding.MapperMethod#executeForMap 中,调用 DefaultSqlSession#selectMap 方法来处理,这里会交由 DefaultMapResultHandler 来处理结果, 将结果封装成对应的 Map。

在这里插入图片描述


以上:内容部分参考
https://www.jianshu.com/p/05f643f27246
https://juejin.cn/post/6844904127818891278
如有侵扰,联系删除。 内容仅用于自我记录学习使用。如有错误,欢迎指正

相关文章:

Mybatis 源码 ∞ :杂七杂八

文章目录 一、前言二、TypeHandler三、KeyGenerator四、Plugin1 Interceptor2 org.apache.ibatis.plugin.Plugin3. 调用场景 五、Mybatis 嵌套映射 BUG1. 示例2. 原因3. 解决方案 六、discriminator 标签七、其他1. RowBounds2. ResultHandler3. MapKey 一、前言 Mybatis 官网…...

堆的实现以及应用

&#x1f493;博主个人主页:不是笨小孩&#x1f440; ⏩专栏分类:数据结构与算法&#x1f440; 刷题专栏&#x1f440; C语言&#x1f440; &#x1f69a;代码仓库:笨小孩的代码库&#x1f440; ⏩社区&#xff1a;不是笨小孩&#x1f440; &#x1f339;欢迎大家三连关注&…...

MySql011——检索数据:过滤数据(使用正则表达式)

前提&#xff1a;使用《MySql006——检索数据&#xff1a;基础select语句》中创建的products表 一、正则表达式介绍 关于正则表达式的介绍大家可以看我的这一篇博客《Java038——正则表达式》&#xff0c;这里就不再累赘。 二、使用MySQL正则表达式 2.1、基本字符匹配 检索…...

数据结构与算法-栈(LIFO)(经典面试题)

一&#xff1a;面试经典 1. 如何设计一个括号匹配的功能&#xff1f;比如给你一串括号让你判断是否符合我们的括号原则&#xff0c; 栈 力扣 2. 如何设计一个浏览器的前进和后退功能&#xff1f; 思想&#xff1a;两个栈&#xff0c;一个栈存放前进栈&…...

NSI45030AT1G LED驱动器方案为汽车外部及内部照明恒流稳流器(CCR)方案

关于线性恒流调节器&#xff08;CCR&#xff09;&#xff1a;是一种用于控制电流的稳定输出。它通常由一个功率晶体管和一个参考电流源组成。CCR的工作原理是通过不断调节功率晶体管的导通时间来维持输出电流的恒定。当输出电流超过设定值时&#xff0c;CCR会减少功率晶体管的导…...

uni-app中使用pinia

目录 Pinia 是什么&#xff1f; uni-app 使用Pinia main.js 中引用pinia 创建和注册模块 定义pinia方式 选项options方式 定义pinia 页面中使用 pinia选项options方式 函数方式 定义pinia 页面中使用 函数方式 定义的pinia Pinia 是什么&#xff1f; Pinia&#xff0…...

Spring之事务管理

文章目录 前言一、事务及其参数含义1.事务的四个特性2.事务的传播行为&#xff08;propagation&#xff09;3.事务隔离性4.事务的隔离级别&#xff08;ioslation&#xff09;5.timeout&#xff08;超时&#xff09;6.readOnly&#xff08;是否只读&#xff09;7.rollbackFor&am…...

linux常见的mysql问题

当涉及到MySQL在Linux系统上的常见问题时&#xff0c;以下是10个经常遇到的问题及其解答&#xff1a; 无法连接到MySQL服务器。 确保MySQL服务器正在运行&#xff1a;可以使用systemctl status mysql或service mysql status命令检查MySQL服务状态。确保MySQL服务器网络设置正确…...

常见分辨率时序信息

分辨率列表 分辨率一:640x480(逐行) 分辨率二:800x600(逐行) 分辨率三:1024x768(逐行) 分辨率四:大名鼎鼎720P(逐行) 注:选择720P@30帧的,需拉长HOR TOTAL TIME 分辨率五:1280x800(逐行) 分辨率六:1280x960(逐行...

机器人CPP编程基础-05完结The End

非常不可思议……之前四篇博文竟然有超过100的阅读量…… 此文此部分终结&#xff0c;没有继续写下去的必要了。 插入一个分享&#xff1a; 编程基础不重要了&#xff0c;只要明确需求&#xff0c;借助AI工具就能完成一个项目。 当然也不是一次成功&#xff0c;工具使用也需要…...

数据库应用系统DBAS功能设计与实施(三级数据库)

目录 一、了解软件体系结构及设计过程 1、软件体系结构与设计过程 2、软件设计过程 二、了解DBAS总体设计 1、DBAS体系结构设计 2、软件体系结构设计 3、软硬件选型与配置设计 4、业务规则初步设计 三、了解DBAS功能概要设计 1、表示层概要设计 2、业务逻辑层概要设计…...

快速幂典型

题目描述 求 a 乘 b 对 p 取模的值&#xff0c;其中 1≤a,b,p≤1018。 输入描述: 第一行a&#xff0c;第二行b&#xff0c;第三行p。 输出描述: 一个整数&#xff0c;表示abmodp的值。 示例1 输入 2 3 9 输出 6 #include<bits/stdc.h> using namespace std; t…...

计算机竞赛 python+opencv+机器学习车牌识别

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于机器学习的车牌识别系统 &#x1f947;学长这里给一个题目综合评分(每项满分5分) 难度系数&#xff1a;4分工作量&#xff1a;4分创新点&#xff1a;3分 该项目较为新颖&#xff0c;适…...

解决电脑声音正常但就是某些游戏没声音问题

电脑声音正常&#xff0c;玩普遍游戏也正常&#xff0c;就有游戏不出声音 详细介绍经过&#xff0c;不喜欢的请直接跳 第三部分。 一、先说下起因现象。 1 大富翁11 没声音。 前段时间无聊怀旧就买了个大富翁11玩玩&#xff0c;近二十年前的老台式机正常无问题。后来想在性能…...

【UniApp开发小程序】小程序首页(展示商品、商品搜索、商品分类搜索)【后端基于若依管理系统开发】

文章目录 界面效果界面实现工具js页面首页让文字只显示两行路由跳转传递对象将商品分为两列显示使用中划线划掉原价 后端商品controllerservicemappersql 界面效果 【说明】 界面中商品的图片来源于闲鱼&#xff0c;若侵权请联系删除关于商品分类页面的实现&#xff0c;请在我…...

Redis 持久化及集群架构

Redis 持久化及集群架构 本篇技术博文将深入探讨 Redis 持久化机制的原理、配置和使用方式。我们将介绍两种常用的持久化方式&#xff1a;RDB 持久化和 AOF 持久化。您将了解到它们的工作原理、优缺点以及如何根据需求选择合适的持久化方式。 通过深入学习 Redis 持久化及集群…...

FPGA + WS2812采灯控制

文章目录 一、WS2812C-2020-V11、产品概述2、引出端排列及功能3、数据传输时间4、数据传输方法 二、使用WS2812C显示图片1、静态显示2、动态显示 一、WS2812C-2020-V1 1、产品概述 WS2812C-2020-V1是一个集控制电路与发光电路于一体的智能外控LED光源&#xff1b;其外型采用最…...

【视频】使用OBS将MP4推流至腾讯云直播

1、下载OBS OBS官网:https://obsproject.com/ OBS支持Win、Mac、Linux,如果下载速度很慢,建议使用迅雷下载 2、OBS推流设置 2.1 添加场景 默认会有一个“场景”,如果想继续添加可以点击“+”按钮 2.2 添加媒体源 1)点击“来源”窗口中“+”按钮 2)支持的媒体源如…...

Vue基本知识

一、vue入门 Vue为前端的框架&#xff0c;免除了原生js的DOM操作。简化书写。 基于MVVM的思想&#xff0c;实现数据的双向绑定&#xff0c;使编程的重点放在数据上。 1、引入vue.js文件 2、定义vue核心对象&#xff0c;定义数据模型 3、编写视图 //1、引入vue.js <scr…...

item_get_sales-获取商品销量详情

一、接口参数说明&#xff1a; item_get_sales-获取商品销量详情&#xff0c;点击更多API调试&#xff0c;请移步注册API账号点击获取测试key和secret 公共参数 请求地址: https://api-gw.onebound.cn/taobao/item_get_sales 名称类型必须描述keyString是调用key&#xff08…...

RestClient

什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端&#xff0c;它允许HTTP与Elasticsearch 集群通信&#xff0c;而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级&#xff…...

TDengine 快速体验(Docker 镜像方式)

简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能&#xff0c;本节首先介绍如何通过 Docker 快速体验 TDengine&#xff0c;然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker&#xff0c;请使用 安装包的方式快…...

使用分级同态加密防御梯度泄漏

抽象 联邦学习 &#xff08;FL&#xff09; 支持跨分布式客户端进行协作模型训练&#xff0c;而无需共享原始数据&#xff0c;这使其成为在互联和自动驾驶汽车 &#xff08;CAV&#xff09; 等领域保护隐私的机器学习的一种很有前途的方法。然而&#xff0c;最近的研究表明&…...

解锁数据库简洁之道:FastAPI与SQLModel实战指南

在构建现代Web应用程序时&#xff0c;与数据库的交互无疑是核心环节。虽然传统的数据库操作方式&#xff08;如直接编写SQL语句与psycopg2交互&#xff09;赋予了我们精细的控制权&#xff0c;但在面对日益复杂的业务逻辑和快速迭代的需求时&#xff0c;这种方式的开发效率和可…...

css的定位(position)详解:相对定位 绝对定位 固定定位

在 CSS 中&#xff0c;元素的定位通过 position 属性控制&#xff0c;共有 5 种定位模式&#xff1a;static&#xff08;静态定位&#xff09;、relative&#xff08;相对定位&#xff09;、absolute&#xff08;绝对定位&#xff09;、fixed&#xff08;固定定位&#xff09;和…...

鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/

使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题&#xff1a;docker pull 失败 网络不同&#xff0c;需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...

Unit 1 深度强化学习简介

Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库&#xff0c;例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体&#xff0c;比如 SnowballFight、Huggy the Do…...

Redis数据倾斜问题解决

Redis 数据倾斜问题解析与解决方案 什么是 Redis 数据倾斜 Redis 数据倾斜指的是在 Redis 集群中&#xff0c;部分节点存储的数据量或访问量远高于其他节点&#xff0c;导致这些节点负载过高&#xff0c;影响整体性能。 数据倾斜的主要表现 部分节点内存使用率远高于其他节…...

Linux 内存管理实战精讲:核心原理与面试常考点全解析

Linux 内存管理实战精讲&#xff1a;核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用&#xff0c;还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...

【笔记】WSL 中 Rust 安装与测试完整记录

#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统&#xff1a;Ubuntu 24.04 LTS (WSL2)架构&#xff1a;x86_64 (GNU/Linux)Rust 版本&#xff1a;rustc 1.87.0 (2025-05-09)Cargo 版本&#xff1a;cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...