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
如有侵擾,聯系刪除。 內容僅用于自我記錄學習使用。如有錯誤,歡迎指正

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/38970.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/38970.shtml
英文地址,請注明出處:http://en.pswp.cn/news/38970.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

mysql主從復制搭建(一主一從)

提示&#xff1a;文章寫完后&#xff0c;目錄可以自動生成&#xff0c;如何生成可參考右邊的幫助文檔 文章目錄 前言MySQL復制過程分為三部&#xff1a; 一、準備工作二、配置>主庫Master三、配置>從庫SlaveSlave_IO_Running: YesSlave_SQL_Running: Yes 四、測試至此&am…

8 | 美國航班數據分析

"在現代快節奏的生活中,航空旅行已經成為人們出行的重要方式之一。然而,航班的準時性一直以來都是旅客和航空公司關注的焦點。無論是商務出差還是休閑度假,乘客們都希望能夠在既定的時間內安全、準時地到達目的地。而對于航空公司而言,準點運營不僅關乎乘客體驗,還涉…

TCP的相關性質

文章目錄 流量控制擁塞控制擁塞窗口 延遲應答捎帶應答面向字節流粘包問題TCP的異常 流量控制 由于接收端處理數據的速度是有限的&#xff0c;如果發送端發的太快&#xff0c;那么接收端的緩沖區就可能會滿。此時如果發送端還發數據&#xff0c;就會出現丟包現象&#xff0c;并…

輕量級自動化測試框架WebZ

一、什么是WebZ WebZ是我用Python寫的“關鍵字驅動”的自動化測試框架&#xff0c;基于WebDriver。 設計該框架的初衷是&#xff1a;用自動化測試讓測試人員從一些簡單卻重復的測試中解放出來。之所以用“關鍵字驅動”模式是因為我覺得這樣能讓測試人員&#xff08;測試執行人員…

【Sklearn】基于線性判別法的數據分類預測(Excel可直接替換數據)

【Sklearn】基于線性判別法的數據分類預測(Excel可直接替換數據) 1.模型原理2.模型參數3.文件結構4.Excel數據5.下載地址6.完整代碼7.運行結果1.模型原理 線性判別分析(Linear Discriminant Analysis,簡稱LDA)是一種經典的模式識別和分類方法,它的目標是找到一個投影,將…

Linux系列講解 —— FTP協議的應用

簡單介紹一下FTP文件傳輸協議在linux系統中的應用。 目錄 0. 基本概念1. FTP Server1.1 安裝FTP Server1.2 FTP Server開啟和關閉1.3 查看FTP Server是否開啟1.4 FTP服務器配置 2. FTP Client2.1 lftp2.2 ftp2.3 sftp2.4 文件資源管理器集成的ftp和sftp 3. ftp常用命令 0. 基本…

IDE的下載和使用

IDE 文章目錄 IDEJETBRAIN JETBRAIN 官網下載對應的ide 激活方式 dxm的電腦已經把這個腳本下載下來了&#xff0c;腳本是macjihuo 以后就不用買了

Neo4j之SET基礎

在 Neo4j 中&#xff0c;SET 語句用于更新節點或關系的屬性。它允許你修改節點或關系的屬性值&#xff0c;可以單獨使用&#xff0c;也可以與其他查詢語句&#xff08;如 MATCH、CREATE、MERGE 等&#xff09;一起使用。以下是一些使用 SET 語句的常見例子&#xff0c;以及它們…

深入源碼分析kubernetes informer機制(四)DeltaFIFO

[閱讀指南] 這是該系列第四篇 基于kubernetes 1.27 stage版本 為了方便閱讀&#xff0c;后續所有代碼均省略了錯誤處理及與關注邏輯無關的部分。 文章目錄 client-go中的存儲結構DeltaFIFOdelta索引 keyqueue push操作delta push 去重 queue pop操作 總結 client-go中的存儲結構…

設計模式

本文主要介紹設計模式的主要設計原則和常用設計模式。 一、UML畫圖 1.類圖 2.時序圖 二、設計模式原則 1.單一職責原則 就是一個方法、一個類只做一件事&#xff1b; 2.開閉原則 就是軟件的設計應該對拓展開放&#xff0c;對修改關閉&#xff0c;這在java中體現最明顯的就…

什么是A股交易接口_(股票交易c接口)開發原理

A股交易接口是指用于與國內的證券交易所&#xff08;上海證券交易所和深圳證券交易所&#xff09;進行股票買賣交易的電子接口或軟件系統。A股交易接口是金融機構、券商以及個人投資者的必備掌握操作技能之一&#xff0c;它提供了實時的股票行情、交易下單、撤單、查詢賬戶信息…

基于Hadoop的表級監管

現狀 大數據平臺中,采用hadoop的方式存儲數據,hdfs本質上是文件系統,而文件系統對數據的監管能力有限,但是數據安全領域問題日漸凸顯,現目前,大數據平臺一般以分層結構進行授權,但是對于一線開發人員而言,是能夠接觸到整個大數據平臺中的所有表的,那么如何實現這樣一…

yum install/update排除特定/某些包方式

1 什么是 yum&#xff1f; yum 代表 “Yellowdog Updater, Modified”。Yum 是用于 rpm 系統的自動更新程序和包安裝/卸載器。 它在安裝包時自動解決依賴關系。 2 什么是 rpm&#xff1f; rpm 代表 “Red Hat Package Manager”&#xff0c;它是一款用于 Red Hat 系統的功能…

PB:庫管理函數

庫管理函數 1、LibraryCreate() 功 能:創建一個空的PowerBuilder應用庫,并可根據需要在創建應用庫的同時添加庫注解。 語 法:LibraryCreate ( libraryname{, comments } ) 參 數:libraryname:string類型,指定要創建應用庫的名稱,可以帶上路徑,不帶路徑時在當前目…

Docker本地鏡像發布到阿里云

1. 本地鏡像發布到阿里云 2. 鏡像的生成方法 OPTIONS說明&#xff1a; -a :提交的鏡像作者&#xff1b; -m :提交時的說明文字&#xff1b; 本次案例centosubuntu兩個&#xff0c;當堂講解一個&#xff0c;家庭作業一個&#xff0c;請大家務必動手&#xff0c;親自實操。 docke…

Gradio部署應用到服務器不能正常訪問

用Gradio部署一個基于ChatGLM-6B的應用&#xff0c;發布到團隊的服務器上&#xff08;局域網&#xff0c;公網不能訪問&#xff09;&#xff0c;我將gradio應用發布到服務器的9001端口 import gradio as gr with gr.Blocks() as demo:......demo.queue().launch(server_port90…

ad+硬件每日學習十個知識點(34)23.8.14 (DCDC詳細設計,續流二極管的選擇,COMP引腳的環路設計)

文章目錄 1.二極管的rrm電壓和rms電壓有什么不同2.DCDC續流二極管的選擇3.充電電容4.COMP引腳的環路設計5.DCDC設計總結6.多路并聯7.相位匹配8.工作模式9.低溫輸出偏離10.電源負載與效率11.降壓升壓模塊 1.二極管的rrm電壓和rms電壓有什么不同 答&#xff1a; 二極管的 RRM &a…

redis主從復制、哨兵服務、持久化、數據類型

Top NSD DBA DAY10 案例1&#xff1a;配置主從復制案例2&#xff1a;配置帶驗證的主從復制案例3&#xff1a;哨兵服務案例4&#xff1a;使用RDB文件恢復數據案例5&#xff1a;AOF案例6&#xff1a;字符類型案例7&#xff1a;列表類型案例8&#xff1a;散列類型案例9&#xff…

Linux交叉編譯opencv并移植ARM端

Linux交叉編譯opencv并移植ARM端 - 知乎 一、安裝交叉編譯器 目標平臺為arm7l&#xff0c;此為32位ARM架構&#xff0c;要安裝合適的編譯器 sudo apt install arm-linux-gnueabihf-gcc sudo apt install arm-linux-gnueabihf-g注意&#xff1a;64位ARM架構的編譯器與32位ARM架…

【MyBatis】查詢數據庫

目錄 一、什么是MyBatis 二、MyBatis框架的搭建 1、搭建MyBatis框架 2、設置MyBaits項目的配置 三、使用MyBatis完成數據庫的操作 1、MyBatis程序中sql語句的即時執行和預編譯 1.1、即時執行&#xff08;${}&#xff09; 1.2、預編譯&#xff08;#{}&#xff09; 1.3、即…