欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Mybatis?MappedStatement類核心原理詳解

 更新時(shí)間:2022年11月18日 11:01:50   作者:氵奄不死的魚  
這篇文章主要介紹了Mybatis?MappedStatement類,mybatis的mapper文件最終會(huì)被解析器,解析成MappedStatement,其中insert|update|delete|select每一個(gè)標(biāo)簽分別對(duì)應(yīng)一個(gè)MappedStatement

MappedStatement

MappedStatement 類是 Mybatis 框架的核心類之一,它存儲(chǔ)了一個(gè) sql 對(duì)應(yīng)的所有信息

Mybatis 通過解析 XML 和 mapper 接口上的注解,生成 sql 對(duì)應(yīng)的 MappedStatement 實(shí)例,并放入 SqlSessionTemplate 中 configuration 類屬性中

正真執(zhí)行 mapper 接口中的方法時(shí),會(huì)從 configuration 中找到對(duì)應(yīng)的 mappedStatement,然后進(jìn)行后續(xù)的操作

MyBatis通過MappedStatement描述<select|update|insert|delete>或者@Select、@Update等注解配置的SQL信息。在介紹MappedStatement組件之前,我們先來了解一下MyBatis中SQL Mapper的配置。不同類型的SQL語句需要使用對(duì)應(yīng)的XML標(biāo)簽進(jìn)行配置。這些標(biāo)簽提供了很多屬性,用來控制每條SQL語句的執(zhí)行行為。下面是標(biāo)簽中的所有屬性:

<select

id="getUserById"

parameterType="int"

parameterMap="deprecated"

resultType="hashmap"

resultMap="userResultMap"

flushCache="false"

useCache="true"

timeout="10000"

fetchSize="256"

statementType="PREPARED"

resultSetType="FORWARD ONLY">

public final class MappedStatement {
  private String id;
  private Integer fetchSize;
  private Integer timeout;
  private StatementType statementType;
  private ResultSetType resultSetType;
  private SqlSource sqlSource;
  private Cache cache;
  private ParameterMap parameterMap;
  private List<ResultMap> resultMaps;
  private boolean flushCacheRequired;
  private boolean useCache;
  private boolean resultOrdered;
  private SqlCommandType sqlCommandType;
  private KeyGenerator keyGenerator;
  private String[] keyProperties;
  private String[] keyColumns;
  private boolean hasNestedResultMaps;
  private String databaseId;
  private Log statementLog;
  private LanguageDriver lang;
  private String[] resultSets;

對(duì)應(yīng)字段含義

id:在命名空間中唯一的標(biāo)識(shí)符,可以被用來引用這條配置信息。

parameterType:用于指定這條語句的參數(shù)類的完全限定名或別名。這個(gè)屬性是可選的,MyBatis能夠根據(jù)Mapper接口方法中的參數(shù)類型推斷出傳入語句的類型。

parameterMap:引用通過標(biāo)簽定義的參數(shù)映射,該屬性已經(jīng)廢棄。

resultType:從這條語句中返回的期望類型的類的完全限定名或別名。注意,如果返回結(jié)果是集合類型,則resultType屬性應(yīng)該指定集合中可以包含的類型,而不是集合本身。

resultMap:用于引用通過標(biāo)簽配置的實(shí)體屬性與數(shù)據(jù)庫(kù)字段之間建立的結(jié)果集的映射(注意:resultMap和resultType屬性不能同時(shí)使用)。

flushCache:用于控制是否刷新緩存。如果將其設(shè)置為true,則任何時(shí)候只要語句被調(diào)用,都會(huì)導(dǎo)致本地緩存和二級(jí)緩存被清空,默認(rèn)值為false。

useCache:是否使用二級(jí)緩存。如果將其設(shè)置為true,則會(huì)導(dǎo)致本條語句的結(jié)果被緩存在MyBatis的二級(jí)緩存中,對(duì)應(yīng)標(biāo)簽,該屬性的默認(rèn)值為true。

timeout:驅(qū)動(dòng)程序等待數(shù)據(jù)庫(kù)返回請(qǐng)求結(jié)果的秒數(shù),超時(shí)將會(huì)拋出異常。

fetchSize:用于設(shè)置JDBC中Statement對(duì)象的fetchSize屬性,該屬性用于指定SQL執(zhí)行后返回的最大行數(shù)。

statementType:參數(shù)可選值為STATEMENT、PREPARED或CALLABLE,這會(huì)讓MyBatis分別使用Statement、PreparedStatement或CallableStatement與數(shù)據(jù)庫(kù)交互,默認(rèn)值為PREPARED。

resultSetType:參數(shù)可選值為FORWARD_ONLY、SCROLL_SENSITIVE或SCROLL_INSENSITIVE,用于設(shè)置ResultSet對(duì)象的特征,具體可參考第2章JDBC規(guī)范的相關(guān)內(nèi)容。默認(rèn)未設(shè)置,由JDBC驅(qū)動(dòng)決定。

databaseId:如果配置了databaseIdProvider,MyBatis會(huì)加載所有不帶databaseId或匹配當(dāng)前databaseId的語句。

resultOrdered:這個(gè)設(shè)置僅針對(duì)嵌套結(jié)果select語句適用,如果為true,就是假定嵌套結(jié)果包含在一起或分組在一起,這樣的話,當(dāng)返回一個(gè)主結(jié)果行的時(shí)候,就不會(huì)發(fā)生對(duì)前面結(jié)果集引用的情況。這就使得在獲取嵌套結(jié)果集的時(shí)候不至于導(dǎo)致內(nèi)存不夠用,默認(rèn)值為false。

resultSets:這個(gè)設(shè)置僅對(duì)多結(jié)果集的情況適用,它將列出語句執(zhí)行后返回的結(jié)果集并每個(gè)結(jié)果集給一個(gè)名稱,名稱使用逗號(hào)分隔。

lang:該屬性用于指定LanguageDriver實(shí)現(xiàn),MyBatis中的LanguageDriver用于解析<select|update|insert|delete>標(biāo)簽中的SQL語句,生成SqlSource對(duì)象。

介紹jdbc的幾個(gè)相關(guān)屬性

resultSetType

在創(chuàng)建PreparedStatement時(shí),resultSetType參數(shù)設(shè)置的是TYPE_SCROLL_INSENSITIVE或TYPE_SCROLL_SENSITIVE,

這兩個(gè)參數(shù)的共同特點(diǎn)是允許結(jié)果集(ResultSet)的游標(biāo)可以上下移動(dòng)。而默認(rèn)的TYPE_FORWARD_ONLY參數(shù)只允許結(jié)果集的游標(biāo)向下移動(dòng)。

如果PreparedStatement對(duì)象初始化時(shí)resultSetType參數(shù)設(shè)置為TYPE_FORWARD_ONLY,在從ResultSet(結(jié)果集)中讀取記錄的時(shí),對(duì)于訪問過的記錄就自動(dòng)釋放了內(nèi)存。而設(shè)置為TYPE_SCROLL_INSENSITIVE或TYPE_SCROLL_SENSITIVE時(shí)為了保證能游標(biāo)能向上移動(dòng)到任意位置,已經(jīng)訪問過的所有都保留在內(nèi)存中不能釋放。所以大量數(shù)據(jù)加載的時(shí)候,就OOM了。

statement = conn.prepareStatement(querySql,ResultSet.TYPE_FORWARD_ONLY,
                        ResultSet.CONCUR_READ_ONLY);

resultSetType是設(shè)置ResultSet對(duì)象的類型標(biāo)示可滾動(dòng),或者是不可滾動(dòng)。取值如下:

默認(rèn)只能向前滾動(dòng)。

fetchSize

默認(rèn)情況下pgjdbc driver會(huì)一次性拉取所有結(jié)果集,也就是在executeQuery的時(shí)候。對(duì)于大數(shù)據(jù)量的查詢來說,非常容易造成OOM。這種場(chǎng)景就需要設(shè)置fetchSize,執(zhí)行query的時(shí)候先返回第一批數(shù)據(jù),之后next完一批數(shù)據(jù)之后再去拉取下一批。

場(chǎng)景與方案

場(chǎng)景:java端從數(shù)據(jù)庫(kù)讀取100W數(shù)據(jù)進(jìn)行后臺(tái)業(yè)務(wù)處理。

常規(guī)實(shí)現(xiàn)1:分頁讀取出來。缺點(diǎn):需要排序后分頁讀取,性能低下。

常規(guī)實(shí)現(xiàn)2:一次性讀取出來。缺點(diǎn):需要很大內(nèi)存,一般計(jì)算機(jī)不行。

非常規(guī)實(shí)現(xiàn):建立長(zhǎng)連接,利用服務(wù)端游標(biāo),一條一條流式返回給java端。

非常規(guī)實(shí)現(xiàn)優(yōu)化:jdbc中有個(gè)重要的參數(shù)fetchSize(它對(duì)業(yè)務(wù)實(shí)現(xiàn)無影響,即不會(huì)限制讀取條數(shù)等),優(yōu)化后可顯著提升性能。

public static void getAll(int fetchSize) {
        try {
            long beginTime=System.currentTimeMillis();
            Connection connection = DriverManager.getConnection(MYSQL_URL);
            connection.setAutoCommit(false); //為了設(shè)置fetchSize,必須設(shè)置為false
            String sql = "select * from test";
            PreparedStatement psst = connection.prepareStatement(sql,ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
            psst.setFetchSize(fetchSize);
            ResultSet rs = psst.executeQuery();
            int totalCount=0;
            while (rs.next()) {
                    totalCount++;
            }
            rs.close();
            psst.close();
            connection.close();
            long endTime=System.currentTimeMillis();
            System.out.println("totalCount:"+totalCount+";fetchSize:"+fetchSize+";耗時(shí):"+(endTime-beginTime)+"ms");
        } catch (SQLException e) {
            e.printStackTrace();
        } 
    }

原理分析

1、先在服務(wù)端執(zhí)行查詢后將數(shù)據(jù)緩存在服務(wù)端。(耗時(shí)相對(duì)較長(zhǎng))

2、java端獲取數(shù)據(jù)時(shí),利用服務(wù)端游標(biāo)進(jìn)行指針跳動(dòng),如果fetchSize為1000,則一次性跳動(dòng)1000條,返回給java端緩存起來。(耗時(shí)較短,跳動(dòng)次數(shù)為N/1000)

3、在調(diào)用next函數(shù)時(shí),優(yōu)先從緩存中取數(shù),其次執(zhí)行2過程。(內(nèi)存讀取,耗時(shí)可忽略)

MappedStatement是怎么來的

還是以XML配置方式為例進(jìn)行分析,簡(jiǎn)單說下源碼查找的過程。Mapper對(duì)應(yīng)的SQL語句定義在xml文件中,順著源碼會(huì)發(fā)現(xiàn)完成xml解析工作的是XMLMapperBuilder,其中對(duì)xml中“select|insert|update|delete”類型元素的解析方法為buildStatementFromContext;buildStatementFromContext使用了XMLStatementBuilder類對(duì)statement進(jìn)行解析,并最終創(chuàng)建了MappedStatement。

所以,XMLStatementBuilder#parseStatementNode方法就是我們分析的重點(diǎn)。但是,在此之前需要有一點(diǎn)準(zhǔn)備工作要做。由于MappedStatement最終是由MapperBuilderAssistant構(gòu)建的,它其中存儲(chǔ)了一些Mapper級(jí)別的共享信息并應(yīng)用到MappedStatement中。所以,先來簡(jiǎn)單了解下它的由來:

  public class XMLMapperBuilder extends BaseBuilder {
  private final XPathParser parser;
  private final MapperBuilderAssistant builderAssistant;
  //省略部分字段和方法
  public XMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
    this(new XPathParser(inputStream, true, configuration.getVariables(), new XMLMapperEntityResolver()),
        configuration, resource, sqlFragments);
  }
  private XMLMapperBuilder(XPathParser parser, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
    super(configuration);
    this.builderAssistant = new MapperBuilderAssistant(configuration, resource);
    this.parser = parser;
    this.sqlFragments = sqlFragments;
    this.resource = resource;
  }
  //省略部分字段和方法
  private void configurationElement(XNode context) {
    try {
      String namespace = context.getStringAttribute("namespace");
      if (namespace == null || namespace.equals("")) {
        throw new BuilderException("Mapper's namespace cannot be empty");
      }
      builderAssistant.setCurrentNamespace(namespace);
      //省略部分代碼
      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
    }
  } 

好了,下面正是開始XMLStatementBuilder#parseStatementNode的分析了。為了節(jié)省篇幅,我直接通過代碼注釋的方式進(jìn)行說明了,部分我認(rèn)為不關(guān)鍵或不常用的內(nèi)容沒有多說。

 /**
     * parseStatementNode方法是對(duì)select、insert、update、delete這四類元素進(jìn)行解析,大體分為三個(gè)過程:
     * 1、解析節(jié)點(diǎn)屬性:如我們最常用的id、resultMap等;
     * 2、解析節(jié)點(diǎn)內(nèi)的sql語句:首先把sql語句中包含的<include></include>等標(biāo)簽轉(zhuǎn)為實(shí)際的sql語句,然后執(zhí)行靜態(tài)或動(dòng)態(tài)節(jié)點(diǎn)處理;
     * 3、根據(jù)以上解析到的內(nèi)容,使用builderAssistant創(chuàng)建MappedStatement,并加入Configuration中。
     * <p>
     * 以上過程中最關(guān)鍵的是第二步,它會(huì)根據(jù)實(shí)際使用的標(biāo)簽,把sql片段轉(zhuǎn)為不同的SqlNode,以鏈表方式存儲(chǔ)到SqlSource中。
     */
    public void parseStatementNode() {
        //獲取標(biāo)簽的id屬性,如selectById,對(duì)應(yīng)Mapper接口中的方法名稱
        String id = context.getStringAttribute("id");
        //獲取databaseId屬性,我們一般都沒有寫。
        String databaseId = context.getStringAttribute("databaseId");
        /**
         * 這段代碼雖然不起眼,但是一定要進(jìn)去看一下:其內(nèi)部完成了對(duì)id的再次賦值,
         * 處理的方式是:id=namespace+"."+id,也就是當(dāng)前Mapper的完全限定名+"."+id,
         * 比如我們之前例子中的com.raysonxin.dao.CompanyDao.selectById
         * 這也是Mapper接口中不能存在重載方法的根本原因。
         * */
        if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
            return;
        }
        /**
         * 下面這塊代碼會(huì)依次獲取fetchSize、timeout、resultMap等屬性,
         * 需要注意的是,有些屬性雖然我們沒有設(shè)置,但是mybatis會(huì)設(shè)置默認(rèn)值,
         * 具體可以查看mybatis的官方說明。
         */
        Integer fetchSize = context.getIntAttribute("fetchSize");
        Integer timeout = context.getIntAttribute("timeout");
        String parameterMap = context.getStringAttribute("parameterMap");
        String parameterType = context.getStringAttribute("parameterType");
        Class<?> parameterTypeClass = resolveClass(parameterType);
        String resultMap = context.getStringAttribute("resultMap");
        String resultType = context.getStringAttribute("resultType");
        String lang = context.getStringAttribute("lang");
        //默認(rèn)值:XMLLanguageDriver
        LanguageDriver langDriver = getLanguageDriver(lang);
        Class<?> resultTypeClass = resolveClass(resultType);
        String resultSetType = context.getStringAttribute("resultSetType");
        //默認(rèn)值:PREPARED
        StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
        //默認(rèn)值:DEFAULT
        ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
        String nodeName = context.getNode().getNodeName();
        SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
        boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
        boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
        boolean useCache = context.getBooleanAttribute("useCache", isSelect);
        boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);
        // Include Fragments before parsing
        /**
         * 英文注釋也說了,在sql解析前處理 include 標(biāo)簽,比如說,我們include了BaseColumns,
         * 它會(huì)把這個(gè)include標(biāo)簽替換為BaseColumns內(nèi)的sql內(nèi)容
         * */
        XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
        includeParser.applyIncludes(context.getNode());
        // Parse selectKey after includes and remove them.
        //處理selectKey,主要針對(duì)不同的數(shù)據(jù)庫(kù)引擎做處理
        processSelectKeyNodes(id, parameterTypeClass, langDriver);
        // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
        /**
         * 到了關(guān)鍵步驟了:就是通過這句代碼完成了從xml標(biāo)簽到SqlSource的轉(zhuǎn)換,
         * SqlSource是一個(gè)接口,這里返回的可能是DynamicSqlSource、也可能是RawSqlSource,
         * 取決于xml標(biāo)簽中是否包含動(dòng)態(tài)元素,比如 <if test=""></if>
         * */
        SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
        String resultSets = context.getStringAttribute("resultSets");
        //下面這些是針對(duì)selectKey、KeyGenerator等進(jìn)行處理,暫時(shí)跳過了。
        String keyProperty = context.getStringAttribute("keyProperty");
        String keyColumn = context.getStringAttribute("keyColumn");
        KeyGenerator keyGenerator;
        String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
        keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
        if (configuration.hasKeyGenerator(keyStatementId)) {
            keyGenerator = configuration.getKeyGenerator(keyStatementId);
        } else {
            keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
                    configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
                    ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
        }
        /**
         * 節(jié)點(diǎn)及屬性都解析完成了,使用builderAssistant創(chuàng)建MappedStatement,
         * 并保存到Configuration#mappedStatements。
         * */
        builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
                fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
                resultSetTypeEnum, flushCache, useCache, resultOrdered,
                keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
    }

我們?cè)趚ml中定義的select等語句就是通過這個(gè)parseStatementNode方法解析為MappedStatement的,整體來看比較容易理解,核心就是SqlSource的創(chuàng)建過程,

SqlSource是什么

SqlSource是整個(gè)MappedStatement的核心,MappedStatement其他一大堆字段都是為了準(zhǔn)確的執(zhí)行它而定義的。SqlSource是個(gè)半成品的sql語句,因?yàn)閷?duì)于其中的動(dòng)態(tài)標(biāo)簽還沒靜態(tài)化,其中的參數(shù)也未賦值。正是如此,才為我們后續(xù)的調(diào)用執(zhí)行提供了基礎(chǔ),接下來重點(diǎn)看看SqlSource的構(gòu)建過程。為了先從整體上了解,我畫了一個(gè)時(shí)序圖來描述SqlSource的解析、創(chuàng)建過程。

sqlsource是mapped statement對(duì)象中的一個(gè)屬性, 是一對(duì)一關(guān)系, 在創(chuàng)建mapped statement對(duì)象時(shí)創(chuàng)建,

sqlsource的主要作用就是創(chuàng)建一個(gè)sql語句

SqlSource主要有四種實(shí)現(xiàn)類, 其主要作用的就是以下三種 :

RawSqlSource : 存儲(chǔ)的是只有“#{}”或者沒有標(biāo)簽的純文本sql信息

DynamicSqlSource : 存儲(chǔ)的是寫有“${}”或者具有動(dòng)態(tài)sql標(biāo)簽的sql信息

StaticSqlSource : 是DynamicSqlSource和RawSqlSource解析為BoundSql的一個(gè)中間環(huán)節(jié)

BoundSql

  private final String sql;
  private final List<ParameterMapping> parameterMappings;
  private final Object parameterObject;

oundSql類的作用就是生成我們最終執(zhí)行的sql語句, 里面包含一些屬性

  • sql : 執(zhí)行的sql語句 (包含 ?)
  • parameterMappings : 映射關(guān)系
  • parameterObject : 參數(shù)值

DynamicSqlSource

public class DynamicSqlSource implements SqlSource {
  private final Configuration configuration;
  private final SqlNode rootSqlNode;
  public DynamicSqlSource(Configuration configuration, SqlNode rootSqlNode) {
    this.configuration = configuration;
    this.rootSqlNode = rootSqlNode;
  }
  @Override
  public BoundSql getBoundSql(Object parameterObject) {
    DynamicContext context = new DynamicContext(configuration, parameterObject);
    rootSqlNode.apply(context);
    SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
    Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
    SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    context.getBindings().forEach(boundSql::setAdditionalParameter);
    return boundSql;
  }
}
  • field

configuration : 上下文, 里面包含了各種參數(shù), 方便使用

  • method

getBoundSql :

構(gòu)建DynamicContext對(duì)象, 里邊包含一個(gè)StringJoiner屬性, 遍歷sqlNode節(jié)點(diǎn)是用來拼接sql

將拼接的sql傳入, 將#{}替換成? , 并創(chuàng)建一個(gè)StaticSqlSource對(duì)象

通過StaticSqlSource對(duì)象獲取BoundSql對(duì)象

RawSqlSource

public class RawSqlSource implements SqlSource {
  private final SqlSource sqlSource;
  public RawSqlSource(Configuration configuration, SqlNode rootSqlNode, Class<?> parameterType) {
    this(configuration, getSql(configuration, rootSqlNode), parameterType);
  }
  public RawSqlSource(Configuration configuration, String sql, Class<?> parameterType) {
    SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
    Class<?> clazz = parameterType == null ? Object.class : parameterType;
    sqlSource = sqlSourceParser.parse(sql, clazz, new HashMap<>());
  }
  private static String getSql(Configuration configuration, SqlNode rootSqlNode) {
    DynamicContext context = new DynamicContext(configuration, null);
    rootSqlNode.apply(context);
    return context.getSql();
  }
  @Override
  public BoundSql getBoundSql(Object parameterObject) {
    return sqlSource.getBoundSql(parameterObject);
  }
}
  • field

sqlSource : 此屬性的類型為StaticSqlSource

  • method

getBoundSql : 獲取BoundSql對(duì)象

StaticSqlSource

只包含一個(gè)創(chuàng)建BoundSql對(duì)象的方法

public class StaticSqlSource implements SqlSource {
  private final String sql;
  private final List<ParameterMapping> parameterMappings;
  private final Configuration configuration;
  public StaticSqlSource(Configuration configuration, String sql) {
    this(configuration, sql, null);
  }
  public StaticSqlSource(Configuration configuration, String sql, List<ParameterMapping> parameterMappings) {
    this.sql = sql;
    this.parameterMappings = parameterMappings;
    this.configuration = configuration;
  }
  @Override
  public BoundSql getBoundSql(Object parameterObject) {
    return new BoundSql(configuration, sql, parameterMappings, parameterObject);
  }
  

到此這篇關(guān)于Mybatis MappedStatement類核心原理詳解的文章就介紹到這了,更多相關(guān)Mybatis MappedStatement內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot整合canal實(shí)現(xiàn)數(shù)據(jù)緩存一致性解決方案

    SpringBoot整合canal實(shí)現(xiàn)數(shù)據(jù)緩存一致性解決方案

    canal主要用途是基于?MySQL?數(shù)據(jù)庫(kù)增量日志解析,提供增量數(shù)據(jù)訂閱和消費(fèi),canal是借助于MySQL主從復(fù)制原理實(shí)現(xiàn),本文將給大家介紹SpringBoot整合canal實(shí)現(xiàn)數(shù)據(jù)緩存一致性解決方案,需要的朋友可以參考下
    2024-03-03
  • SpringBoot中MockMVC單元測(cè)試的實(shí)現(xiàn)

    SpringBoot中MockMVC單元測(cè)試的實(shí)現(xiàn)

    Mock是一種用于模擬和替換類的對(duì)象的方法,以便在單元測(cè)試中獨(dú)立于外部資源進(jìn)行測(cè)試,本文主要介紹了SpringBoot中MockMVC單元測(cè)試的實(shí)現(xiàn),具有應(yīng)該的參考價(jià)值,感興趣的可以了解一下
    2024-02-02
  • Java設(shè)計(jì)模式七大原則之里氏替換原則詳解

    Java設(shè)計(jì)模式七大原則之里氏替換原則詳解

    在面向?qū)ο蟮某绦蛟O(shè)計(jì)中,里氏替換原則(Liskov Substitution principle)是對(duì)子類型的特別定義。本文將為大家詳細(xì)介紹Java設(shè)計(jì)模式七大原則之一的里氏替換原則,需要的可以參考一下
    2022-02-02
  • Java  隊(duì)列實(shí)現(xiàn)原理及簡(jiǎn)單實(shí)現(xiàn)代碼

    Java 隊(duì)列實(shí)現(xiàn)原理及簡(jiǎn)單實(shí)現(xiàn)代碼

    這篇文章主要介紹了Java 隊(duì)列實(shí)現(xiàn)原理及簡(jiǎn)單實(shí)現(xiàn)代碼的相關(guān)資料,需要的朋友可以參考下
    2016-10-10
  • FluentMybatis實(shí)現(xiàn)mybatis動(dòng)態(tài)sql拼裝和fluent api語法

    FluentMybatis實(shí)現(xiàn)mybatis動(dòng)態(tài)sql拼裝和fluent api語法

    本文主要介紹了FluentMybatis實(shí)現(xiàn)mybatis動(dòng)態(tài)sql拼裝和fluent api語法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • idea安裝漢化插件的圖文教程

    idea安裝漢化插件的圖文教程

    本文主要介紹了idea安裝漢化插件的圖文教程,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07
  • Java設(shè)計(jì)模式之觀察者模式

    Java設(shè)計(jì)模式之觀察者模式

    這篇文章主要介紹了Java設(shè)計(jì)模式之觀察者模式,觀察者模式,是一種行為性模型,又叫發(fā)布-訂閱模式,他定義對(duì)象之間一種一對(duì)多的依賴關(guān)系,使得當(dāng)一個(gè)對(duì)象改變狀態(tài),則所有依賴于它的對(duì)象都會(huì)得到通知并自動(dòng)更新,需要的朋友可以參考下
    2023-11-11
  • java加載properties文件的六種方法總結(jié)

    java加載properties文件的六種方法總結(jié)

    這篇文章主要介紹了java加載properties文件的六種方法總結(jié)的相關(guān)資料,需要的朋友可以參考下
    2017-05-05
  • SpringBoot 使用 OpenAPI3 規(guī)范整合 knife4j的詳細(xì)過程

    SpringBoot 使用 OpenAPI3 規(guī)范整合 knife4j的詳細(xì)過程

    Swagger工具集使用OpenAPI規(guī)范,可以生成、展示和測(cè)試基于OpenAPI規(guī)范的API文檔,并提供了生成客戶端代碼的功能,本文給大家介紹SpringBoot使用OpenAPI3規(guī)范整合knife4j的詳細(xì)過程,感興趣的朋友跟隨小編一起看看吧
    2023-12-12
  • 使用MyBatis攔截器實(shí)現(xiàn)SQL的完整打印

    使用MyBatis攔截器實(shí)現(xiàn)SQL的完整打印

    當(dāng)我們使用Mybatis結(jié)合Mybatis-plus進(jìn)行開發(fā)時(shí),為了查看執(zhí)行sql的信息通常我們可以通過屬性配置的方式打印出執(zhí)行的sql語句,但這樣的打印出了sql語句常帶有占位符信息,不利于排錯(cuò),所以本文介紹了構(gòu)建MyBatis攔截器,實(shí)現(xiàn)SQL的完整打印,需要的朋友可以參考下
    2024-07-07

最新評(píng)論