国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

【深入淺出MyBatis筆記】MyBatis的解析和運行原理

bitkylin / 1179人閱讀

摘要:的解析和運行原理構(gòu)建過程提供創(chuàng)建的核心接口。在構(gòu)造器初始化時會根據(jù)和的方法解析為命令。數(shù)據(jù)庫會話器定義了一個對象的適配器,它是一個接口對象,構(gòu)造器根據(jù)配置來適配對應(yīng)的對象。它的作用是給實現(xiàn)類對象的使用提供一個統(tǒng)一簡易的使用適配器。

MyBatis的解析和運行原理 構(gòu)建SqlSessionFactory過程

SqlSessionFactory提供創(chuàng)建MyBatis的核心接口SqlSession。MyBatis采用構(gòu)造模式去創(chuàng)建SqlSessionFactory,我們可以通過SqlSessionFactoryBuilder去構(gòu)建。

第一步,通過XMLConfigBuilder解析配置的XML文件,讀出配置參數(shù),并將讀取的數(shù)據(jù)存入這個Configuration類中。

第二步,使用Configuration對象去創(chuàng)建SqlSessionFactory。

SqlSessionFactoryBuilder的源碼:

public class SqlSessionFactoryBuilder {
  .....
  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
      try {
        XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
        // XMLConfigBuilder解析配置的XML文件,構(gòu)建Configuration
        return build(parser.parse());
      } catch (Exception e) {
        throw ExceptionFactory.wrapException("Error building SqlSession.", e);
      } finally {
        ErrorContext.instance().reset();
        try {
          inputStream.close();
        } catch (IOException e) {
          // Intentionally ignore. Prefer previous error.
        }
      }
  }

  // 使用Configuration對象去創(chuàng)建SqlSessionFactory
  public SqlSessionFactory build(Configuration config) {
      // SqlSessionFactory是一個接口,為此MyBatis提供了一個默認實現(xiàn)類
      return new DefaultSqlSessionFactory(config);
  }
}
構(gòu)建Configuration

在XMLConfigBuilder中,MyBatis會讀出所有XML配置的信息,然后將這些信息保存到Configuration類的單例中。
它會做如下初始化:

properties全局參數(shù)

setting設(shè)置

typeAliases別名

typeHandler類型處理器

ObjectFactory對象

plugin插件

environment環(huán)境

DatabaseIdProvider數(shù)據(jù)庫標(biāo)識

Mapper映射器

XMLConfigBuilder的源碼:

public class XMLConfigBuilder extends BaseBuilder {
  ...
  public Configuration parse() {
      if (parsed) {
        throw new BuilderException("Each XMLConfigBuilder can only be used once.");
      }
      parsed = true;
      // 解析配置文件,設(shè)置Configuration
      parseConfiguration(parser.evalNode("/configuration"));
      return configuration;
    }

  private void parseConfiguration(XNode root) {
    // 讀出MyBatis配置文件中的configuration下的各個子標(biāo)簽元素
    // 把全部信息保存到Configuration類的單例中
    try {
      //issue #117 read properties first
      propertiesElement(root.evalNode("properties"));
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      loadCustomVfs(settings);
      typeAliasesElement(root.evalNode("typeAliases"));
      pluginElement(root.evalNode("plugins"));
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      settingsElement(settings);
      // read it after objectFactory and objectWrapperFactory issue #631
      environmentsElement(root.evalNode("environments"));
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      typeHandlerElement(root.evalNode("typeHandlers"));
      // 設(shè)置mapper映射器
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }
}
映射器的內(nèi)部組成

XMLMapperBuilder負責(zé)對配置文件中的Mapper映射器進行解析,其中在configurationElement方法中可以看出來,會分別對配置文件中的parameterMap、resultMap、sql、select|insert|update|delete元素進行解析。

public class XMLMapperBuilder extends BaseBuilder {
  public void parse() {
    if (!configuration.isResourceLoaded(resource)) {
      // 解析配置文件中的mapper映射器
      configurationElement(parser.evalNode("/mapper"));
      configuration.addLoadedResource(resource);
      bindMapperForNamespace();
    }

    parsePendingResultMaps();
    parsePendingCacheRefs();
    parsePendingStatements();
  }

  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);
      cacheRefElement(context.evalNode("cache-ref"));
      cacheElement(context.evalNode("cache"));
      // 解析我們配置的parameterMap元素
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
      // 解析我們配置的resultMap元素
      resultMapElements(context.evalNodes("/mapper/resultMap"));
      // 解析我們配置的sql元素
      sqlElement(context.evalNodes("/mapper/sql"));
       // 解析我們配置的select、insert、update、delete元素
      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
    }
  }
}

在方法buildStatementFromContext()中,會根據(jù)配置信息創(chuàng)建一個MappedStatement對象。

MappedStatement,它保存映射器的一個節(jié)點(select|insert|update|delete)。包括許多我們配置的SQL、SQL的id、緩存信息、resultMap、parameterType、resultType、languageDriver等重要配置內(nèi)容。

public final class MappedStatement {
  private Configuration configuration;
  private String id;
  private StatementType statementType;
  private ResultSetType resultSetType;
  private SqlSource sqlSource;
  private Cache cache;
  private ParameterMap parameterMap;
  private List resultMaps;
  private boolean flushCacheRequired;
  private boolean useCache;
  private SqlCommandType sqlCommandType;
  private KeyGenerator keyGenerator;
  private String databaseId;
  private LanguageDriver lang;
  ......
}

SqlSource,它是提供BoundSql對象的地方,它是MappedStatement的一個屬性。

public interface SqlSource {

  BoundSql getBoundSql(Object parameterObject);

}

BoundSql,它是建立SQL和參數(shù)的地方。

public class BoundSql {

  private final String sql;
  private final List parameterMappings;
  private final Object parameterObject;
  private final Map additionalParameters;
  private final MetaObject metaParameters;
}
SqlSession運行過程

SqlSession是一個接口,在MyBatis中有一個默認實現(xiàn)DefaultSqlSession。我們構(gòu)建SqlSessionFactory就可以輕易地拿到SqlSession了。通過SqlSession,我們拿到Mapper,之后可以做查詢、插入、更新、刪除的方法。

UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

其實getMapper()方法拿到的mapper是通過Java動態(tài)代理實現(xiàn)的。從getMapper()方法逐級往下看,可以發(fā)現(xiàn)在MapperRegistry類的getMapper()方法中會拿到一個MapperProxyFactory的對象,最后是通過MapperProxyFactory對象去生成一個Mapper的。

public class DefaultSqlSession implements SqlSession {
  .....
  @Override
  public  T getMapper(Class type) {
    return configuration.getMapper(type, this);
  }
}

public class Configuration {
  .....
  public  T getMapper(Class type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
  }
}

public class MapperRegistry {
  ......
  public  T getMapper(Class type, SqlSession sqlSession) {
    final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) knownMappers.get(type);
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }
}
映射器的動態(tài)代理

Mapper映射是通過動態(tài)代理實現(xiàn)的,MapperProxyFactory用來生成動態(tài)代理對象。

public class MapperProxyFactory {
  ......
  protected T newInstance(MapperProxy mapperProxy) {
      // 動態(tài)代理
      return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

  public T newInstance(SqlSession sqlSession) {
      final MapperProxy mapperProxy = new MapperProxy(sqlSession, mapperInterface, methodCache);
      return newInstance(mapperProxy);
  }
}

在MapperProxyFactory的newInstance方法中可以看到有一個MapperProxy對象,MapperProxy實現(xiàn)InvocationHandler接口(動態(tài)代理需要實現(xiàn)這一接口)的代理方法invoke(), 這invoke()方法實現(xiàn)對被代理類的方法進行攔截。
而在invoke()方法中,MapperMethod對象會執(zhí)行Mapper接口的查詢或其他方法。

public class MapperProxy implements InvocationHandler, Serializable {
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
        // 先判斷是否一個類,在這里Mapper顯然是一個接口
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
        // 判斷是不是接口默認實現(xiàn)方法
      } else if (isDefaultMethod(method)) {
        return invokeDefaultMethod(proxy, method, args);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    // 緩存中取出MapperMethod,不存在的話,則根據(jù)Configuration初始化一個
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    // 執(zhí)行Mapper接口的查詢或其他方法
    return mapperMethod.execute(sqlSession, args);
}

private MapperMethod cachedMapperMethod(Method method) {
    MapperMethod mapperMethod = methodCache.get(method);
    if (mapperMethod == null) {
      mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
      methodCache.put(method, mapperMethod);
    }
    return mapperMethod;
  }
}

MapperMethod采用命令模式運行,并根據(jù)上下文跳轉(zhuǎn)。MapperMethod在構(gòu)造器初始化時會根據(jù)Configuration和Mapper的Method方法解析為SqlCommand命令。之后在execute方法,根據(jù)SqlCommand的Type進行跳轉(zhuǎn)。然后采用命令模式,SqlSession通過SqlCommand執(zhí)行插入、更新、查詢、選擇等方法。

public MapperMethod(Class mapperInterface, Method method, Configuration config) {
    // 根據(jù)Configuration和Mapper的Method方法解析為SqlCommand
    this.command = new SqlCommand(config, mapperInterface, method);
    this.method = new MethodSignature(config, mapperInterface, method);
}

public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    // 根據(jù)Type進行跳轉(zhuǎn),通過sqlSession執(zhí)行相關(guān)的操作
    switch (command.getType()) {
      case INSERT: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.insert(command.getName(), param));
        break;
      }
      case UPDATE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.update(command.getName(), param));
        break;
      }
      case DELETE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.delete(command.getName(), param));
        break;
      }
      case SELECT:
        if (method.returnsVoid() && method.hasResultHandler()) {
          executeWithResultHandler(sqlSession, args);
          result = null;
        } else if (method.returnsMany()) {
          result = executeForMany(sqlSession, args);
        } else if (method.returnsMap()) {
          result = executeForMap(sqlSession, args);
        } else if (method.returnsCursor()) {
          result = executeForCursor(sqlSession, args);
        } else {
          Object param = method.convertArgsToSqlCommandParam(args);
          result = sqlSession.selectOne(command.getName(), param);
        }
        break;
      case FLUSH:
        result = sqlSession.flushStatements();
        break;
      default:
        throw new BindingException("Unknown execution method for: " + command.getName());
    }
    if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
      throw new BindingException("Mapper method "" + command.getName() 
          + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
    }
    return result;
}

看到這里,應(yīng)該大概知道了MyBatis為什么只用Mapper接口便能夠運行SQL,因為映射器的XML文件的命名空間namespace對應(yīng)的便是這個接口的全路徑,那么它根據(jù)全路徑和方法名便能夠綁定起來,通過動態(tài)代理技術(shù),讓這個接口跑起來。而后采用命令模式,最后還是使用SqlSession接口的方法使得它能夠執(zhí)行查詢,有了這層封裝我們便可以使用這個接口編程。
不過還是可以看到,最后插入、更新、刪除、查詢操作還是會回到SqlSession中進行處理。

Sqlsession下的四大對象

我們已經(jīng)知道了映射器其實就是一個動態(tài)代理對象,進入到了MapperMethod的execute方法。它經(jīng)過簡單判斷就是進入了SqlSession的刪除、更新、插入、選擇等方法。sqlSession執(zhí)行一個查詢操作。可以看到是通過一個executor來執(zhí)行的。

其實SqlSession中的Executor執(zhí)行器負責(zé)調(diào)度StatementHandler、ParameterHandler、ResultHandler等來執(zhí)行相關(guān)的SQL。

StatementHandler:使用數(shù)據(jù)庫的Statement(PrepareStatement)執(zhí)行操作

ParameterHandler:用于SQL對參數(shù)的處理

ResultHandler:進行最后數(shù)據(jù)集(ResultSet)的封裝返回處理

Sqlsession其實是一個接口,它有一個DefaultSqlSession的默認實現(xiàn)類。

public class DefaultSqlSession implements SqlSession {
  private final Configuration configuration;
  // Executor執(zhí)行器,負責(zé)調(diào)度SQL的執(zhí)行
  private final Executor executor;

  ......
  @Override
  public  List selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      MappedStatement ms = configuration.getMappedStatement(statement);
      // 通過executor執(zhí)行查詢操作
      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();
    }
  }
}
Executor執(zhí)行器

執(zhí)行器起到了至關(guān)重要的作用,它是一個真正執(zhí)行Java和數(shù)據(jù)庫交互的東西。在MyBatis中存在三種執(zhí)行器,我們可以在MyBatis的配置文件中進行選擇。

SIMPLE,簡易執(zhí)行器

REUSE,是一種執(zhí)行器重用預(yù)處理語句

BATCH,執(zhí)行器重用語句和批量更新,她是針對批量專用的執(zhí)行器

它們都提供了查詢和更新方法,以及相關(guān)的事務(wù)方法。

Executor是通過Configuration類創(chuàng)建的,MyBatis將根據(jù)配置類型去確定你需要創(chuàng)建三種執(zhí)行器中的哪一種。

public class Configuration {
  ......
    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);
    }
    // MyBatis插件,構(gòu)建一層層的動態(tài)代理對象
    // 在調(diào)度真實的方法之前執(zhí)行配置插件的代碼
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }
}
顯然MyBatis根據(jù)Configuration來構(gòu)建StatementHandler,然后使用prepareStatement方法,對SQL編譯并對參數(shù)進行初始化,resultHandler再組裝查詢結(jié)果返回給調(diào)用者來完成一次查詢。
public class SimpleExecutor extends BaseExecutor {
    .....
    @Override
    public  List doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        Statement stmt = null;
        try {
            Configuration configuration = ms.getConfiguration();
            // 根據(jù)Configuration來構(gòu)建StatementHandler
            StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
            // 對SQL編譯并對參數(shù)進行初始化
            stmt = prepareStatement(handler, ms.getStatementLog());
            // 組裝查詢結(jié)果返回給調(diào)用者
            return handler.query(stmt, resultHandler);
        } finally {
            closeStatement(stmt);
        }
    }

  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    Connection connection = getConnection(statementLog);
    // 進行預(yù)編譯和基礎(chǔ)設(shè)置
    stmt = handler.prepare(connection, transaction.getTimeout());
    // 設(shè)置參數(shù)
    handler.parameterize(stmt);
    return stmt;
  }
}
StatementHandler數(shù)據(jù)庫會話器

StatementHandler就是專門處理數(shù)據(jù)庫會話的。

創(chuàng)建StatementHandler:

public class Configuration {
    ......
    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);
        // MyBatis插件,生成一層層的動態(tài)代理對象
        statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
        return statementHandler;
  }
}

RoutingStatementHandler其實不是我們真實的服務(wù)對象,它是通過適配模式找到對應(yīng)的StatementHandler來執(zhí)行。
StatementHandler分為三種:

SimleStatementHandler

PrepareStatementHandler

CallableStatementHandler

在初始化RoutingStatementHandler對象的時候它會根據(jù)上下文環(huán)境來決定創(chuàng)建哪個StatementHandler對象。

public class RoutingStatementHandler implements StatementHandler {
    ......
    private final StatementHandler delegate;

    public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    switch (ms.getStatementType()) {
      case STATEMENT:
        delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case PREPARED:
        delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case CALLABLE:
        delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      default:
        throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
    }
  }
}

數(shù)據(jù)庫會話器定義了一個對象的適配器delegate,它是一個StatementHandler接口對象,構(gòu)造器根據(jù)配置來適配對應(yīng)的StatementHandler對象。它的作用是給實現(xiàn)類對象的使用提供一個統(tǒng)一、簡易的使用適配器。此為對象的適配模式,可以讓我們使用現(xiàn)有的類和方法對外提供服務(wù),也可以根據(jù)實際的需求對外屏蔽一些方法,甚至加入新的服務(wù)。

在執(zhí)行器Executor執(zhí)行查詢操作的時候,我們看到PreparedStatementHandler的三個方法:prepare、parameterize和query。

public abstract class BaseStatementHandler implements StatementHandler {
  .....
    @Override
    public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
        ErrorContext.instance().sql(boundSql.getSql());
        Statement statement = null;
        try {
            // 對SQL進行了預(yù)編譯
            statement = instantiateStatement(connection);
            setStatementTimeout(statement, transactionTimeout);
            setFetchSize(statement);
            return statement;
        } catch (SQLException e) {
            closeStatement(statement);
        throw e;
        } catch (Exception e) {
            closeStatement(statement);
            throw new ExecutorException("Error preparing statement.  Cause: " + e, e);
    }
  }
}

public class PreparedStatementHandler extends BaseStatementHandler {
    @Override
    public void parameterize(Statement statement) throws SQLException {
        // 設(shè)置參數(shù)
        parameterHandler.setParameters((PreparedStatement) statement);
    }

    @Override
    protected Statement instantiateStatement(Connection connection) throws SQLException {
        String sql = boundSql.getSql();
        if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
        String[] keyColumnNames = mappedStatement.getKeyColumns();
        if (keyColumnNames == null) {
            return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
        } else {
            return connection.prepareStatement(sql, keyColumnNames);
        }
        } else if (mappedStatement.getResultSetType() != null) {
            return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
        } else {
            return connection.prepareStatement(sql);
        }
    }

    @Override
    public  List query(Statement statement, ResultHandler resultHandler) throws SQLException {
        PreparedStatement ps = (PreparedStatement) statement;
        // 執(zhí)行SQL
        ps.execute();
        // resultSetHandler封裝結(jié)果返回
        return resultSetHandler. handleResultSets(ps);
    } 
}
一條查詢SQL的執(zhí)行過程,Executor會先調(diào)用StatementHandler的prepare()方法預(yù)編譯SQL語句,同時設(shè)置一些基本運行的參數(shù)。然后用parameterize()方法啟動ParameterHandler設(shè)置參數(shù),完成預(yù)編譯,跟著就是執(zhí)行查詢。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/69531.html

相關(guān)文章

  • Java深入-框架技巧

    摘要:從使用到原理學(xué)習(xí)線程池關(guān)于線程池的使用,及原理分析分析角度新穎面向切面編程的基本用法基于注解的實現(xiàn)在軟件開發(fā)中,分散于應(yīng)用中多出的功能被稱為橫切關(guān)注點如事務(wù)安全緩存等。 Java 程序媛手把手教你設(shè)計模式中的撩妹神技 -- 上篇 遇一人白首,擇一城終老,是多么美好的人生境界,她和他歷經(jīng)風(fēng)雨慢慢變老,回首走過的點點滴滴,依然清楚的記得當(dāng)初愛情萌芽的模樣…… Java 進階面試問題列表 -...

    chengtao1633 評論0 收藏0
  • Java學(xué)習(xí)路線總結(jié),搬磚工逆襲Java架構(gòu)師(全網(wǎng)最強)

    摘要:哪吒社區(qū)技能樹打卡打卡貼函數(shù)式接口簡介領(lǐng)域優(yōu)質(zhì)創(chuàng)作者哪吒公眾號作者架構(gòu)師奮斗者掃描主頁左側(cè)二維碼,加入群聊,一起學(xué)習(xí)一起進步歡迎點贊收藏留言前情提要無意間聽到領(lǐng)導(dǎo)們的談話,現(xiàn)在公司的現(xiàn)狀是碼農(nóng)太多,但能獨立帶隊的人太少,簡而言之,不缺干 ? 哪吒社區(qū)Java技能樹打卡?【打卡貼 day2...

    Scorpion 評論0 收藏0
  • 深入淺出MyBatis筆記】插件

    摘要:插件插件接口在中使用插件,我們必須實現(xiàn)接口。它將直接覆蓋你所攔截對象原有的方法,因此它是插件的核心方法。插件在對象中的保存插件的代理和反射設(shè)計插件用的是責(zé)任鏈模式,的責(zé)任鏈?zhǔn)怯扇ザx的。 插件 1、插件接口 在MyBatis中使用插件,我們必須實現(xiàn)接口Interceptor。 public interface Interceptor { // 它將直接覆蓋你所攔截對象原有的方法,因...

    leon 評論0 收藏0
  • 帶你深入淺出MyBatis技術(shù)原理與實戰(zhàn)(PDF實戰(zhàn)實踐)

    摘要:目錄其中每個章節(jié)知識點都是相關(guān)連由淺入深的一步步全面分析了技術(shù)原理以及實戰(zhàn)由于文案較長想深入學(xué)習(xí)以及對于該文檔感興趣的朋友們可以加群免費獲取。這些場景在大量的編碼中使用,具備較強的實用價值,這些內(nèi)容都是通過實戰(zhàn)得來的,供讀者們參考。 前言系統(tǒng)掌握MyBatis編程技巧已經(jīng)成了用Java構(gòu)建移動互聯(lián)網(wǎng)網(wǎng)站的必要條件 本文主要講解了Mybatis的應(yīng)用,解析了其原理,從而形成一個完整的知識...

    MoAir 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<