Mybatis源碼閱讀(五 ):接口層——SqlSession

*************************************優雅的分割線 **********************************

分享一波:程序員賺外快-必看的巔峰干貨

如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程

請關注微信公眾號:HB荷包
在這里插入圖片描述
一個能讓你學習技術和賺錢方法的公眾號,持續更新
*************************************優雅的分割線 **********************************
SimpleExecutor
SqlSession

SqlSession是Mybatis的核心接口之一,對外提供常用的數據庫操作api。mybatis提供了兩個SqlSession的實現,其中最常用的是DefaultSqlSession。

SqlSession的代碼如下

/**

  • 接口層,也是開發人員使用mybatis去操作sql所使用的主要的接口

  • @author Clinton Begin
    */
    public interface SqlSession extends Closeable {

    /**

    • 查詢sql單條數據
    • @param 返回的數據類型
    • @param statement sql
    • @return Mapped object
      */
      T selectOne(String statement);

    /**

    • 指定sql并傳入實參去查單條數據
    • @param 返回的數據類型
    • @param statement 預編譯的帶有?的sql
    • @param parameter 用戶傳入的實參,與前面sql綁定
    • @return Mapped object
      */
      T selectOne(String statement, Object parameter);

    /**

    • 執行sql查詢多條數據
    • @param 返回的數據類型
    • @param statement sql
    • @return List of mapped object
      */
      List selectList(String statement);

    /**

    • 指定sql并傳入實參去查多條數據
    • @param 返回的數據類型
    • @param statement 預編譯的帶有問號的sql
    • @param parameter 用戶傳入的實參,與前面的sql綁定
    • @return List of mapped object
      */
      List selectList(String statement, Object parameter);

    /**

    • 使用預編譯的sql,指定傳入的實參以及結果集范圍
    • 查詢指定范圍的所有數據
    • @param 返回的數據類型
    • @param statement 預編譯的帶有問號的sql
    • @param parameter 用戶傳入的實參,與前面的sql綁定
    • @param rowBounds 指定查詢范圍
    • @return List of mapped object
      */
      List selectList(String statement, Object parameter, RowBounds rowBounds);

    /**

    • 執行sql,返回map對象
    • @param the returned Map keys type
    • @param the returned Map values type
    • @param statement Unique identifier matching the statement to use.
    • @param mapKey The property to use as key for each value in the list.
    • @return Map containing key pair data.
      */
      <K, V> Map<K, V> selectMap(String statement, String mapKey);

    /**

    • 指定sql和實參,返回map
    • @param the returned Map keys type
    • @param the returned Map values type
    • @param statement Unique identifier matching the statement to use.
    • @param parameter A parameter object to pass to the statement.
    • @param mapKey The property to use as key for each value in the list.
    • @return Map containing key pair data.
      */
      <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey);

    /**

    • 指定sql、實參、范圍,返回map
    • @param the returned Map keys type
    • @param the returned Map values type
    • @param statement Unique identifier matching the statement to use.
    • @param parameter A parameter object to pass to the statement.
    • @param mapKey The property to use as key for each value in the list.
    • @param rowBounds Bounds to limit object retrieval
    • @return Map containing key pair data.
      */
      <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds);

    /**

    • A Cursor offers the same results as a List, except it fetches data lazily using an Iterator.
    • @param the returned cursor element type.
    • @param statement Unique identifier matching the statement to use.
    • @return Cursor of mapped objects
      */
      Cursor selectCursor(String statement);

    /**

    • A Cursor offers the same results as a List, except it fetches data lazily using an Iterator.
    • @param the returned cursor element type.
    • @param statement Unique identifier matching the statement to use.
    • @param parameter A parameter object to pass to the statement.
    • @return Cursor of mapped objects
      */
      Cursor selectCursor(String statement, Object parameter);

    /**

    • A Cursor offers the same results as a List, except it fetches data lazily using an Iterator.
    • @param the returned cursor element type.
    • @param statement Unique identifier matching the statement to use.
    • @param parameter A parameter object to pass to the statement.
    • @param rowBounds Bounds to limit object retrieval
    • @return Cursor of mapped objects
      */
      Cursor selectCursor(String statement, Object parameter, RowBounds rowBounds);

    /**

    • 將查詢結果通過此處的ResultHandler對象封裝成對應的對象
    • @param statement Unique identifier matching the statement to use.
    • @param parameter A parameter object to pass to the statement.
    • @param handler ResultHandler that will handle each retrieved row
      */
      void select(String statement, Object parameter, ResultHandler handler);

    /**

    • Retrieve a single row mapped from the statement
    • using a {@code ResultHandler}.
    • @param statement Unique identifier matching the statement to use.
    • @param handler ResultHandler that will handle each retrieved row
      */
      void select(String statement, ResultHandler handler);

    /**

    • Retrieve a single row mapped from the statement key and parameter
    • using a {@code ResultHandler} and {@code RowBounds}.
    • @param statement Unique identifier matching the statement to use.
    • @param rowBounds RowBound instance to limit the query results
    • @param handler ResultHandler that will handle each retrieved row
      */
      void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler);

    /**

    • 執行insert
    • @param statement Unique identifier matching the statement to execute.
    • @return int The number of rows affected by the insert.
      */
      int insert(String statement);

    /**

    • Execute an insert statement with the given parameter object. Any generated
    • autoincrement values or selectKey entries will modify the given parameter
    • object properties. Only the number of rows affected will be returned.
    • @param statement Unique identifier matching the statement to execute.
    • @param parameter A parameter object to pass to the statement.
    • @return int The number of rows affected by the insert.
      */
      int insert(String statement, Object parameter);

    /**

    • 執行update
    • @param statement Unique identifier matching the statement to execute.
    • @return int The number of rows affected by the update.
      */
      int update(String statement);

    /**

    • Execute an update statement. The number of rows affected will be returned.
    • @param statement Unique identifier matching the statement to execute.
    • @param parameter A parameter object to pass to the statement.
    • @return int The number of rows affected by the update.
      */
      int update(String statement, Object parameter);

    /**

    • 執行delete
    • @param statement Unique identifier matching the statement to execute.
    • @return int The number of rows affected by the delete.
      */
      int delete(String statement);

    /**

    • Execute a delete statement. The number of rows affected will be returned.
    • @param statement Unique identifier matching the statement to execute.
    • @param parameter A parameter object to pass to the statement.
    • @return int The number of rows affected by the delete.
      */
      int delete(String statement, Object parameter);

    /**

    • 提交事務
      */
      void commit();

    /**

    • Flushes batch statements and commits database connection.
    • @param force forces connection commit
      */
      void commit(boolean force);

    /**

    • 回滾事務
      */
      void rollback();

    /**

    • Discards pending batch statements and rolls database connection back.
    • Note that database connection will not be rolled back if no updates/deletes/inserts were called.
    • @param force forces connection rollback
      */
      void rollback(boolean force);

    /**

    • 將請求刷新到數據庫
    • @return BatchResult list of updated records
    • @since 3.0.6
      */
      List flushStatements();

    /**

    • 關閉SqlSession
      */
      @Override
      void close();

    /**

    • 清空 緩存
      */
      void clearCache();

    /**

    • Retrieves current configuration.
    • @return Configuration
      */
      Configuration getConfiguration();

    /**

    • 使用type獲取對應的Mapper
    • @param the mapper type
    • @param type Mapper interface class
    • @return a mapper bound to this SqlSession
      */
      T getMapper(Class type);

    /**

    • 獲取該SqlSession對應的數據庫連接
    • @return Connection
      */
      Connection getConnection();
      }

DefaultSqlSession

在mybatis單獨使用的時候,DefaultSqlSession是最常使用的SqlSession實現。DefaultSqlSession核心字段如下,其中已經過多介紹的類將不再注釋。

private final Configuration configuration;
private final Executor executor;/*** 是否自動提交事務*/
private final boolean autoCommit;/*** 當前緩存是否有臟數據*/
private boolean dirty;

DefaultSqlSession中使用到了策略模式(不知道策略模式的請看我以前的帖子)。DefaultSqlSession扮演了上下文,只是通過executor字段的不同,而選擇不同的Executor去操作數據庫。

DefaultSqlSession為每種SQL操作都提供了大量的重載,對于不同的參數都提供了一個重載方法, 便于開發者去調用。這里只貼出核心的方法,對于重載方法將不進行介紹。

 * 根據sql和實參查詢一條數據* @param statement 預編譯的帶有?的sql* @param parameter 用戶傳入的實參,與前面sql綁定* @param <T>* @return*/
@Override
public <T> T selectOne(String statement, Object parameter) {// 調用selectList查詢多條List<T> list = this.selectList(statement, parameter);// 如果查詢到的數據長度1是或者0就正常,否則拋出異常if (list.size() == 1) {return list.get(0);} else if (list.size() > 1) {// 這里的異常信息是不是很熟悉呢throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());} else {return null;}
}/*** 查詢結果封裝成map返回* 閱讀源碼發現,這里的selectMap貌似并不是將結果集按照屬性映射成map* 而是把map當做list去使用。* 查詢出多條數據,使用不同的key去封裝到map* 這里的V應該是這一條數據映射的對象,或者是Map<String, Object>* @param statement Unique identifier matching the statement to use.* @param parameter A parameter object to pass to the statement.* @param mapKey    The property to use as key for each value in the list.* @param rowBounds Bounds to limit object retrieval* @param <K>* @param <V>* @return*/
@Override
public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) {// 查詢列表final List<? extends V> list = selectList(statement, parameter, rowBounds);// 創建Map返回集處理器final DefaultMapResultHandler<K, V> mapResultHandler = new DefaultMapResultHandler<>(mapKey,configuration.getObjectFactory(), configuration.getObjectWrapperFactory(), configuration.getReflectorFactory());final DefaultResultContext<V> context = new DefaultResultContext<>();for (V o : list) {// 暫存一下當前結果對象context.nextResultObject(o);// 處理上下文中的結果對象mapResultHandler.handleResult(context);}// 將map返回回去return mapResultHandler.getMappedResults();
}/*** 根據傳入的sql、實參、查詢范圍去查詢一個列表* @param statement 預編譯的帶有問號的sql* @param parameter 用戶傳入的實參,與前面的sql綁定* @param rowBounds 指定查詢范圍* @param <E>* @return*/
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {try {MappedStatement ms = configuration.getMappedStatement(statement);return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);} catch (Exception e) {throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);} finally {ErrorContext.instance().reset();}
}/*** 根據sql、實參、范圍查詢* 將查詢結果交給指定的ResultHandler去處理* @param statement Unique identifier matching the statement to use.* @param parameter* @param rowBounds RowBound instance to limit the query results* @param handler   ResultHandler that will handle each retrieved row*/
@Override
public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {try {MappedStatement ms = configuration.getMappedStatement(statement);executor.query(ms, wrapCollection(parameter), rowBounds, handler);} catch (Exception e) {throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);} finally {ErrorContext.instance().reset();}
}@Override
public int update(String statement, Object parameter) {try {dirty = true;MappedStatement ms = configuration.getMappedStatement(statement);return executor.update(ms, wrapCollection(parameter));} catch (Exception e) {throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);} finally {ErrorContext.instance().reset();}
}@Override
public void commit(boolean force) {try {// 提交事務。提交之后將dirty設為false// 此時的緩存中視為沒有臟數據executor.commit(isCommitOrRollbackRequired(force));dirty = false;} catch (Exception e) {throw ExceptionFactory.wrapException("Error committing transaction.  Cause: " + e, e);} finally {ErrorContext.instance().reset();}
}@Override
public void rollback(boolean force) {try {executor.rollback(isCommitOrRollbackRequired(force));dirty = false;} catch (Exception e) {throw ExceptionFactory.wrapException("Error rolling back transaction.  Cause: " + e, e);} finally {ErrorContext.instance().reset();}
}@Override
public List<BatchResult> flushStatements() {try {return executor.flushStatements();} catch (Exception e) {throw ExceptionFactory.wrapException("Error flushing statements.  Cause: " + e, e);} finally {ErrorContext.instance().reset();}
}

代碼比較簡單,就不做過多的介紹。
DefaultSqlSessionFactory

DefaultSqlSessionFactory是一個工廠類,提供了兩種創建DefaultSqlSession的方式,一種是通過數據源創建SqlSession,一種是通過用戶傳入的數據庫連接對象來創建SqlSession。另外代碼里有大量的openSession都是用于創建SqlSession對象的,但是其實現都是基于這兩種方式,因此這里只把兩種創建SqlSession的方式的代碼貼出來,如下。

/*** 通過數據源去創建SqlSession* @param execType* @param level* @param autoCommit* @return*/
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {Transaction tx = null;try {// 獲取environment。這個是mybatis中配置的環境final Environment environment = configuration.getEnvironment();// 根據環境去獲取TransactionFactory對象final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);// 創建Transaction對象tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);// 創建Executor對象final Executor executor = configuration.newExecutor(tx, execType);// 創建DefaultSqlSessionreturn new DefaultSqlSession(configuration, executor, autoCommit);} catch (Exception e) {closeTransaction(tx);// may have fetched a connection so lets call close()throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);} finally {ErrorContext.instance().reset();}
}/*** 通過用戶提供的Connection對象去創建* @param execType* @param connection* @return*/
private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {try {boolean autoCommit;try {autoCommit = connection.getAutoCommit();} catch (SQLException e) {// Failover to true, as most poor drivers// or databases won't support transactionsautoCommit = true;}final Environment environment = configuration.getEnvironment();final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);final Transaction tx = transactionFactory.newTransaction(connection);final Executor executor = configuration.newExecutor(tx, execType);return new DefaultSqlSession(configuration, executor, autoCommit);} catch (Exception e) {throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);} finally {ErrorContext.instance().reset();}
}

SqlSessionManager

SqlSessionManager同時實現了SqlSession接口和SQLSessionFactory接口,因此它擁有操作數據庫的能力以及創建SqlSession的功能。

SQLSessionManager核心字段如下

private final SqlSessionFactory sqlSessionFactory;/*** localSqlSession中記錄的SqlSession對象的代理對象*/
private final SqlSession sqlSessionProxy;/*** 記錄當前線程的SqlSession對象*/
private final ThreadLocal<SqlSession> localSqlSession = new ThreadLocal<>();

其中ThreadLocal的作用往往是作為當前線程的上下文,可以為當前線程提供全局變量。對ThreadLocal不了解的朋友也請查看我以前的文章。

SqlSessionManager提供了兩種模式。一種是同一線程每次通過SqlSessionManager對象訪問數據庫時,都會創建一個DefaultSqlSession對象完成數據庫操作,另一種則是使用localSqlSession綁定當前線程的SqlSession,在當前線程中循環使用同一個SqlSession。后者使用往往居多,這也是大家經常說的“SqlSession與線程綁定 ,每個請求都會創建SqlSession”的原因。

sqlSessionProxy是一個代理對象,在SqlSessionmanager的構造方法中使用JDK的動態代理創建完成,代碼如下。

private SqlSessionManager(SqlSessionFactory sqlSessionFactory) {this.sqlSessionFactory = sqlSessionFactory;// 使用動態代理去創建 SqlSessionthis.sqlSessionProxy = (SqlSession) Proxy.newProxyInstance(SqlSessionFactory.class.getClassLoader(),new Class[]{SqlSession.class},new SqlSessionInterceptor());
}

SqlSessionManager中實現的SqlSession 接口方法,都是直接調用sqlSessionProxy字段記錄的SqlSession代理對象的方法實現的。在創建該代理對象時使用到的SqlSessionInterceptor是SqlSessionManager的內部類,代碼如下。

private class SqlSessionInterceptor implements InvocationHandler {public SqlSessionInterceptor() {// Prevent Synthetic Access}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 獲取當前線程的SqlSessionfinal SqlSession sqlSession = SqlSessionManager.this.localSqlSession.get();if (sqlSession != null) {try {// SqlSession不為空就調用真正的SqlSession去完成數據庫的操作return method.invoke(sqlSession, args);} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}} else {try (SqlSession autoSqlSession = openSession()) {// 如果當前線程的SqlSession為空,就創建新的SqlSession對象try {// 使用創建的SqlSession對象完成數據庫操作final Object result = method.invoke(autoSqlSession, args);// 提交事務autoSqlSession.commit();return result;} catch (Throwable t) {autoSqlSession.rollback();throw ExceptionUtil.unwrapThrowable(t);}}}}
}

總結

SqlSession是單體mybatis使用最多的一個接口,可能我們在整合SSM之后就看不到這個接口了,但是其底層實現的時候也是會創建SqlSession的,雖然這個比較簡單,但是也是相當重要的一個模塊。
*************************************優雅的分割線 **********************************

分享一波:程序員賺外快-必看的巔峰干貨

如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程

請關注微信公眾號:HB荷包
在這里插入圖片描述
一個能讓你學習技術和賺錢方法的公眾號,持續更新
*************************************優雅的分割線 **********************************
SimpleExecutor

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

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

相關文章

插入公式_一個小工具,徹底幫你搞定在Markdown中插入公式的問題

在編輯Markdown文檔時&#xff0c;插入公式是一個挺麻煩的活兒。需要掌握LaTex語法。我自己看完語法后&#xff0c;直接放棄&#xff0c;這絕對是反人類的語法。&#xff08;好吧&#xff0c;是我不會用...&#xff09;但是&#xff0c;我相信你看了這篇文章后&#xff0c;絕對…

JavaScript數據結構與算法——字典

1.字典數據結構 在字典中&#xff0c;存儲的是【鍵&#xff0c;值】對&#xff0c;其中鍵名是用來查詢特定元素的。字典和集合很相似&#xff0c;集合以【值&#xff0c;值】的形式存儲&#xff0c;字典則是用【鍵&#xff0c;值】對的形式存儲。字典也稱作映射。 2.創建字典 f…

Mybatis源碼閱讀(一):Mybatis初始化1.2 —— 解析別名、插件、對象工廠、反射工具箱、環境

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

中西方對時間的差異_中西方時間觀念差異 英文

The concept of time(時間觀念)①Inchina&#xff0c;words and phrases about time are very general. Forexample&#xff0c;ifyoudatewithsomeone,mostofChineseusedtoanswer: in the afternoon /at night/after a while and so on.Butinwestern,peoplehaveaverystrongconc…

Google 修改 Chrome API,防止隱身模式檢測

開發四年只會寫業務代碼&#xff0c;分布式高并發都不會還做程序員&#xff1f; 在使用 Chrome 瀏覽網頁時&#xff0c;某些網站會使用某種方法來確定訪問者是否處于隱身模式&#xff0c;這是一種隱私泄漏行為。Google 目前正在考慮修改 Chrome 的相關 API&#xff0c;來杜絕…

Mybatis源碼閱讀(一):Mybatis初始化1.1 解析properties、settings

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

亞馬遜推薦python_使用python查找amazon類別

我想得到amazon的類別&#xff0c;我計劃廢棄不用API。我已經取消了http://www.amazon.com&#xff0c;我已經在Shop By Department下拉列表中抓取了所有的類別和子類別&#xff0c;我創建了一個web服務來完成這項工作&#xff0c;代碼就在這里route(/hello)def hello():textli…

JavaScript異步基礎

唯一比不知道代碼為什么崩潰更可怕的事情是&#xff0c;不知道為什么一開始它是工作的&#xff01;在 ECMA 規范的最近幾次版本里不斷有新成員加入&#xff0c;尤其在處理異步的問題上&#xff0c;更是不斷推陳出新。然而&#xff0c;我們在享受便利的同時&#xff0c;也應該了…

Flutter、ReactNative、uniapp對比

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

JavaScript數組方法

一、基本類型和引用類型 數值、字符串、布爾值、undefined、null可以直接寫出來&#xff0c;比較簡單的數據稱為基本類型&#xff0c;在比較的時候&#xff0c;是直接按值比較。對象、函數、數組復雜的數據是引用類型&#xff0c;在比較的時候&#xff0c;是按照地址比較。cons…

nodejs mysql模塊_NodeJs使用Mysql模塊實現事務處理

依賴模塊&#xff1a;1. mysql&#xff1a;https://github.com/felixge/node-mysqlnpm install mysql --save2. async&#xff1a;https://github.com/caolan/asyncnpm install async --save(ps: async模塊可換成其它Promise模塊如bluebird、q等)因為Node.js的mysql模塊本身對于…

計數排序vs基數排序vs桶排序

從計數排序說起 計數排序是一種非基于元素比較的排序算法&#xff0c;而是將待排序數組元素轉化為計數數組的索引值&#xff0c;從而間接使待排序數組具有順序性。 計數排序的實現一般有兩種形式&#xff1a;基于輔助數組和基于桶排序。 基于輔助數組 整個過程包含三個數組&…

多線程中ThreadLocal的使用

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

mysql 查看所有表的引擎_MySQL查看數據庫、表的占用空間大小以及某個庫中所有表的引擎類型...

本文章來給大家介紹一些常用的MySQL查看數據庫、表的占用空間大小sql命令吧&#xff0c;希望此教程 對各位同學會有所幫助。查看各庫的大小代碼如下復制代碼SELECT SUM(DATA_LENGTH)SUM(INDEX_LENGTH) FROM information_schema.tables WHERE TABLE_SCHEMAdatabase_name;結果是以…

Fusion組件庫是如何支持多語言能力的

隨著國際化發展&#xff0c;多語言的需求越來越常見&#xff0c;單一的語言已經遠不能滿足需求了。作為一個組件庫&#xff0c;支持多語言也是基本能力。 多語言功能的本質其實是文本的替換&#xff0c;一個詞匯“OK”&#xff0c;在英文語境下是“OK”&#xff0c;日語語境下是…

mysql 存儲過程 replace_mysql replace存儲過程

{"moduleinfo":{"card_count":[{"count_phone":1,"count":1}],"search_count":[{"count_phone":4,"count":4}]},"card":[{"des":"阿里云數據庫專家保駕護航&#xff0c;為用戶…

注解版poi操作工具

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

Kali Linux 2019.1 發布,Metasploit 更新到 5.0 版本

百度智能云 云生態狂歡季 熱門云產品1折起>>> Kali Linux 2019.1 發布了&#xff0c;Kali 前身 BackTrack&#xff0c;它是一個基于 Debian 的 Linux 發行版&#xff0c;主要用于信息安全行業&#xff0c;其包含了一系列安全、滲透測試和取證工具。此版本 Linux 內核…

peewee mysql_scrapy中利用peewee插入Mysql

前兩天老大布置一個任務&#xff0c;說爬下來的數據要存入數據庫中&#xff0c;丟給我一個peewee&#xff0c;說用這個。當時的我兩眼一抹黑&#xff0c;這是個什么東西呀&#xff0c;我知道scrapy的數據存入數據庫是在pipelines中進行設置但是peewee是什么東西呢。經過兩天不懈…

Java版數據結構與算法——線性表

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…