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

資訊專欄INFORMATION COLUMN

mybatis-spring原理解析

why_rookie / 2665人閱讀

摘要:創(chuàng)建出的是對(duì)象,持有這個(gè)對(duì)象。根據(jù)接口名和方法名從對(duì)象的中檢查并獲取方法對(duì)應(yīng)的語(yǔ)句解析成的對(duì)象,保存它的和命令類型。實(shí)現(xiàn)類攔截映射接口的自定義方法,讓去處理方法對(duì)應(yīng)的解析成的。

前言

Mybatis是目前主流的Java ORM框架之一。
mybatis-spring包則是為了讓Mybatis更好得整合進(jìn)Spring的衍生產(chǎn)品。
本文就從Mybatis和mybatis-spring源碼著手,以目前較為流行的用法,探究Mybatis的工作原理以及mybatis-spring是如何做到“迎合”Spring的。

一切都從配置開始

首先在pom.xml文件中引入Mybatis包和mybatis-spring包(如果是SpringBoot,引入mybatis-spring-boot-starter即可):


  org.mybatis
  mybatis
  3.5.1


  org.mybatis
  mybatis-spring
  2.0.1

然后在Spring的配置xml文件中聲明以下bean:



      
          
              classpath*:xxx/*.xml
          
      
      
  
  
      
      
  

下面我們研究每個(gè)配置的作用,進(jìn)而了解mybatis-spring的工作方式。

SqlSessionFactoryBean

一個(gè)FactoryBean,負(fù)責(zé)創(chuàng)建SqlSessionFactory,而SqlSessionFactory是創(chuàng)建SqlSession的工廠類。

它在初始化時(shí)會(huì)解析基本配置和XML映射文件,然后全部封裝到一個(gè)Configuration對(duì)象中。創(chuàng)建出的SqlSessionFactory是DefaultSqlSessionFactory對(duì)象,持有這個(gè)Configuration對(duì)象。

一般來(lái)說(shuō)一個(gè)應(yīng)用只需要?jiǎng)?chuàng)建一個(gè)SqlSessionFactory。

這里重點(diǎn)關(guān)注下XML映射文件的解析,確切的說(shuō)應(yīng)該是解析的結(jié)果如何處理(畢竟解析的過(guò)程太復(fù)雜)。

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"));
    parameterMapElement(context.evalNodes("/mapper/parameterMap"));
    resultMapElements(context.evalNodes("/mapper/resultMap"));
    sqlElement(context.evalNodes("/mapper/sql"));
    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);
  }
}

常用的幾個(gè)節(jié)點(diǎn):

parameterMap節(jié)點(diǎn)解析成ParameterMap對(duì)象保存在Configuration的parameterMaps屬性中;

resultMap節(jié)點(diǎn)解析成ResultMap對(duì)象保存在Configuration的resultMaps屬性中;

select|insert|update|delete節(jié)點(diǎn)解析成MappedStatement保存在Configuration的mappedStatements屬性中。

光解析完還不夠,還得和映射接口關(guān)聯(lián)起來(lái)。XML文件的mapper節(jié)點(diǎn)會(huì)有namespace屬性,它的值就是映射接口的全類名。根據(jù)全類名獲取到Class對(duì)象,然后Configuration對(duì)象中的MapperRegistry屬性負(fù)責(zé)注冊(cè)該類,就是將類對(duì)象和由它初始化的MapperProxyFactory對(duì)象組成鍵值對(duì)放入knownMappers屬性。后面創(chuàng)建映射接口的實(shí)現(xiàn)類對(duì)象時(shí)會(huì)用到。

總結(jié)下SqlSessionFactoryBean的作用,就是創(chuàng)建一個(gè)SqlSessionFactory類型的單例,持有所有的配置信息和解析結(jié)果。

MapperScannerConfigurer

實(shí)現(xiàn)了BeanDefinitionRegistryPostProcessor,負(fù)責(zé)掃描指定包下的映射接口并向容器中注冊(cè)對(duì)應(yīng)的bean。

注冊(cè)過(guò)程中有一些細(xì)節(jié)需要提一下,注冊(cè)的bean的beanClass并不是映射接口本身,而統(tǒng)一是MapperFactoryBean。同時(shí)MapperScannerConfigurer創(chuàng)建時(shí)傳入的sqlSessionFactoryBeanName所代表的SqlSessionFactory會(huì)設(shè)置到這些bean中去。

MapperFactoryBean

一個(gè)FactoryBean,負(fù)責(zé)創(chuàng)建對(duì)應(yīng)映射接口的實(shí)現(xiàn)類對(duì)象,這個(gè)實(shí)現(xiàn)類負(fù)責(zé)完成映射接口的方法和XML定義的SQL語(yǔ)句的映射關(guān)系。

Mybatis通過(guò)SqlSession接口執(zhí)行SQL語(yǔ)句,所以MapperFactoryBean會(huì)在初始化時(shí)通過(guò)持有的SqlSessionFactory對(duì)象創(chuàng)建一個(gè)SqlSessionTemplate(它實(shí)現(xiàn)了SqlSession)對(duì)象。這個(gè)SqlSessionTemplate是mybatis-spring的核心,它給常規(guī)的SqlSession賦予了更多的功能,特別是迎合Spring的功能,后面會(huì)詳細(xì)描述。

我們來(lái)看一下MapperFactoryBean是如何創(chuàng)建映射接口的實(shí)現(xiàn)類對(duì)象的。
既然是FactoryBean,就是通過(guò)getObject創(chuàng)建需要的bean對(duì)象。跟蹤方法調(diào)用,發(fā)現(xiàn)最終委托給了Configuration對(duì)象中MapperRegistry屬性。上面簡(jiǎn)述XML解析過(guò)程時(shí)已知,MapperRegistry對(duì)象的knownMappers屬性保存了映射接口的類對(duì)象和一個(gè)MapperProxyFactory對(duì)象組成的鍵值對(duì)。

MapperProxyFactory就是一個(gè)代理工廠類,它創(chuàng)建實(shí)現(xiàn)類對(duì)象的方式就是創(chuàng)建以映射接口為實(shí)現(xiàn)接口、MapperProxy為InvocationHandler的JDK動(dòng)態(tài)代理。代理的邏輯都在MapperProxy#invoke方法中:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  try {
    if (Object.class.equals(method.getDeclaringClass())) {
      return method.invoke(this, args);
    } else if (isDefaultMethod(method)) {
      return invokeDefaultMethod(proxy, method, args);
    }
  } catch (Throwable t) {
    throw ExceptionUtil.unwrapThrowable(t);
  }
  final MapperMethod mapperMethod = cachedMapperMethod(method);
  return mapperMethod.execute(sqlSession, args);
}

可以看到,我們想要實(shí)現(xiàn)的方法(即排除Object方法和接口的默認(rèn)方法),都委托給了對(duì)應(yīng)的MapperMethod去實(shí)現(xiàn)。方法第一次調(diào)用時(shí),新建MapperMethod,然后放入緩存。MapperMethod包含了兩個(gè)內(nèi)部類屬性:

SqlCommand:負(fù)責(zé)關(guān)聯(lián)SQL命令。根據(jù)接口名和方法名從Configuration對(duì)象的mappedStatements中檢查并獲取方法對(duì)應(yīng)的SQL語(yǔ)句解析成的MappedStatement對(duì)象,保存它的id和SQL命令類型。

MethodSignature:負(fù)責(zé)解析和保存方法簽名信息。解析方法的參數(shù)和返回類型,保存解析后的信息。

獲取MapperMethod后就是調(diào)用它的execute方法:

public Object execute(SqlSession sqlSession, Object[] args) {
  Object result;
  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;
}

方法根據(jù)SQL命令類型的不同進(jìn)行不同的操作,一樣的地方是都會(huì)先把方法參數(shù)轉(zhuǎn)化為SQL參數(shù)形式,然后執(zhí)行傳進(jìn)execute方法的SqlSession對(duì)象(即MapperFactoryBean對(duì)象持有的SqlSessionTemplate對(duì)象)的對(duì)應(yīng)的方法。

總結(jié)下MapperScannerConfigurer和MapperFactoryBean的作用:MapperScannerConfigurer負(fù)責(zé)把配置路徑下的映射接口注冊(cè)為Spring容器的MapperFactoryBean類型的bean。這個(gè)工廠bean通過(guò)代理方式創(chuàng)建對(duì)應(yīng)映射接口的實(shí)現(xiàn)類對(duì)象。實(shí)現(xiàn)類攔截映射接口的自定義方法,讓SqlSessionTemplate去處理方法對(duì)應(yīng)的SQL解析成的MappedStatement。

SqlSessionTemplate

實(shí)現(xiàn)了SqlSession,但和SqlSession默認(rèn)實(shí)現(xiàn)類DefaultSqlSession不同的是,它是線程安全的,這意味著一個(gè)SqlSessionTemplate實(shí)例可以在多個(gè)Dao之間共享;它和Spring的事務(wù)管理緊密關(guān)聯(lián),可以實(shí)現(xiàn)多線程下各個(gè)事務(wù)之間的相互隔離;另外,它會(huì)把Mybatis返回的異常轉(zhuǎn)化為Spring的DataAccessException。下面我們來(lái)探究它是如何做到這幾點(diǎn)的。

SqlSessionTemplate在初始化時(shí)會(huì)通過(guò)JDK動(dòng)態(tài)代理的方式創(chuàng)建一個(gè)實(shí)現(xiàn)SqlSession、以SqlSessionInterceptor為InvocationHandler的代理對(duì)象,SqlSessionTemplate的大多數(shù)方法調(diào)用都轉(zhuǎn)發(fā)給這個(gè)代理。攔截的邏輯在SqlSessionInterceptor#invoke中:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    SqlSession sqlSession = getSqlSession(
        SqlSessionTemplate.this.sqlSessionFactory,
        SqlSessionTemplate.this.executorType,
        SqlSessionTemplate.this.exceptionTranslator);
    try {
      Object result = method.invoke(sqlSession, args);
      if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
        // force commit even on non-dirty sessions because some databases require
        // a commit/rollback before calling close()
        sqlSession.commit(true);
      }
      return result;
    } catch (Throwable t) {
      Throwable unwrapped = unwrapThrowable(t);
      if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
        // release the connection to avoid a deadlock if the translator is no loaded. See issue #22
        closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
        sqlSession = null;
        Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
        if (translated != null) {
          unwrapped = translated;
        }
      }
      throw unwrapped;
    } finally {
      if (sqlSession != null) {
        closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
      }
    }
  }
}

首先獲取真正用來(lái)工作的SqlSession,SqlSessionUtils#getSqlSession:

public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {

  notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
  notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);

  SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);

  SqlSession session = sessionHolder(executorType, holder);
  if (session != null) {
    return session;
  }

  if (LOGGER.isDebugEnabled()) {
    LOGGER.debug("Creating a new SqlSession");
  }

  session = sessionFactory.openSession(executorType);

  registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);

  return session;
}

這里包含了與Spring事務(wù)關(guān)聯(lián)的邏輯。先嘗試從事務(wù)同步管理類中獲取傳入的SqlSessionFactory對(duì)象在當(dāng)前線程綁定的SqlSessionHolder對(duì)象,如果存在就直接返回SqlSessionHolder對(duì)象持有的SqlSession對(duì)象,否則就用SqlSessionFactory創(chuàng)建一個(gè)新的SqlSession,調(diào)用DefaultSqlSessionFactory#openSessionFromDataSource,level默認(rèn)是null,autoCommit默認(rèn)false:

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
  Transaction tx = null;
  try {
    final Environment environment = configuration.getEnvironment();
    final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
    tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
    final Executor executor = configuration.newExecutor(tx, execType);
    return 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();
  }
}

可以看到最終創(chuàng)建了一個(gè)DefaultSqlSession對(duì)象,這里需要注意的一點(diǎn)是,這里創(chuàng)建了Transaction和Executor,在繼續(xù)往底層探索時(shí)會(huì)再提及到。

創(chuàng)建完之后,會(huì)根據(jù)當(dāng)前線程是否存在Spring事務(wù)而選擇是否封裝成SqlSessionHolder放入事務(wù)同步管理類,這樣以來(lái),同線程同事務(wù)下對(duì)映射接口的調(diào)用,實(shí)際工作的都是同一個(gè)SqlSession。

我們回到SqlSessionInterceptor,獲取到實(shí)際工作的DefaultSqlSession會(huì)去執(zhí)行當(dāng)前攔截的方法(具體我們稍后探究),如果拋出Mybatis的PersistenceException異常,初始化時(shí)設(shè)置的PersistenceExceptionTranslator對(duì)象(默認(rèn)是MyBatisExceptionTranslator對(duì)象)會(huì)對(duì)異常進(jìn)行轉(zhuǎn)化為DataAccessException。

總結(jié)下SqlSessionTemplate的作用,它通過(guò)動(dòng)態(tài)代理對(duì)方法進(jìn)行攔截,然后根據(jù)當(dāng)前Spring事務(wù)狀態(tài)獲取或創(chuàng)建SqlSession來(lái)進(jìn)行實(shí)際的工作。

DefaultSqlSession

我們現(xiàn)在知道SqlSessionTemplate最終還是依賴一個(gè)DefaultSqlSession對(duì)象去處理映射接口方法對(duì)應(yīng)的MappedStatement。下面我們以selectList方法為例探究具體的處理過(guò)程:

public  List 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();
  }
}

首先從configuration中獲取到MappedStatement對(duì)象,然后讓Executor對(duì)象調(diào)用query方法。

Executor

Executor是Mybatis的執(zhí)行器,負(fù)責(zé)SQL語(yǔ)句的生成和查詢緩存的維護(hù)。

前面在創(chuàng)建DefaultSqlSession的時(shí)候,會(huì)先讓configuration創(chuàng)建一個(gè)Executor,根據(jù)配置的ExecutorType選擇具體的Executor實(shí)現(xiàn),默認(rèn)是SimpleExecutor,然后如果配置緩存開啟(默認(rèn)開啟),則還要封裝成CachingExecutor。

CachingExecutor的query方法會(huì)先從MappedStatement對(duì)象動(dòng)態(tài)生成sql語(yǔ)句,和參數(shù)一起封裝在BoundSql對(duì)象中;再根據(jù)sql、參數(shù)和返回映射等信息創(chuàng)建一個(gè)緩存鍵;然后檢查XML里有沒有配置二級(jí)緩存,有的話就用緩存鍵去查找,否則就執(zhí)行它代理的Executor對(duì)象的query方法,先用緩存鍵去一級(jí)緩存也叫本地緩存中去查找,如果沒有的話就執(zhí)行doQuery方法。不同Executor實(shí)現(xiàn)的doQuery有所不同,但核心都是創(chuàng)建一個(gè)StatementHandler,然后通過(guò)它對(duì)底層JDBC Statement進(jìn)行操作,最后對(duì)查詢的結(jié)果集進(jìn)行轉(zhuǎn)化。

限于篇幅,就不繼續(xù)探究StatementHandler及更底層的操作了,就再看下Mybatis是怎么管理數(shù)據(jù)庫(kù)連接的。

Transaction

先回顧下這個(gè)Transaction對(duì)象是怎么來(lái)的:前面創(chuàng)建實(shí)際工作的DefaultSqlSession時(shí)會(huì)讓TransactionFactory對(duì)象創(chuàng)建一個(gè)Transactio對(duì)象作為Executor對(duì)象的屬性。而這個(gè)TransactionFactory對(duì)象,如何沒有指定的話,默認(rèn)是SpringManagedTransactionFactory對(duì)象。它接受一個(gè)DataSource創(chuàng)建SpringManagedTransaction,可以看到這里把事務(wù)隔離級(jí)別和是否自動(dòng)提交兩個(gè)參數(shù)都忽略了,那是因?yàn)閙ybatis-spring把事務(wù)都交給Spring去管理了。

Executor在執(zhí)行doQuery方法,創(chuàng)建JDBC Statement對(duì)象時(shí)需要先獲取到數(shù)據(jù)庫(kù)連接:

protected Connection getConnection(Log statementLog) throws SQLException {
  Connection connection = transaction.getConnection();
  if (statementLog.isDebugEnabled()) {
    return ConnectionLogger.newInstance(connection, statementLog, queryStack);
  } else {
    return connection;
  }
}

繼續(xù)看到SpringManagedTransaction,它的Connection是通過(guò)DataSourceUtils調(diào)用getConnection方法獲取的,核心邏輯在doGetConnection方法中:

public static Connection doGetConnection(DataSource dataSource) throws SQLException {
   Assert.notNull(dataSource, "No DataSource specified");

   ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
   if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {
      conHolder.requested();
      if (!conHolder.hasConnection()) {
         logger.debug("Fetching resumed JDBC Connection from DataSource");
         conHolder.setConnection(fetchConnection(dataSource));
      }
      return conHolder.getConnection();
   }
   // Else we either got no holder or an empty thread-bound holder here.

   logger.debug("Fetching JDBC Connection from DataSource");
   Connection con = fetchConnection(dataSource);

   if (TransactionSynchronizationManager.isSynchronizationActive()) {
      try {
         // Use same Connection for further JDBC actions within the transaction.
         // Thread-bound object will get removed by synchronization at transaction completion.
         ConnectionHolder holderToUse = conHolder;
         if (holderToUse == null) {
            holderToUse = new ConnectionHolder(con);
         }
         else {
            holderToUse.setConnection(con);
         }
         holderToUse.requested();
         TransactionSynchronizationManager.registerSynchronization(
               new ConnectionSynchronization(holderToUse, dataSource));
         holderToUse.setSynchronizedWithTransaction(true);
         if (holderToUse != conHolder) {
            TransactionSynchronizationManager.bindResource(dataSource, holderToUse);
         }
      }
      catch (RuntimeException ex) {
         // Unexpected exception from external delegation call -> close Connection and rethrow.
         releaseConnection(con, dataSource);
         throw ex;
      }
   }

   return con;
}

可以看到,Spring的事務(wù)管理器不僅保存了事務(wù)環(huán)境下當(dāng)前線程的SqlSession,還以dataSource為鍵保存了Connection。如果從事務(wù)管理器沒有獲取到,就需要通過(guò)從SpringManagedTransaction傳遞過(guò)來(lái)的dataSource獲取Connection對(duì)象,獲取到之后判斷當(dāng)前是否在事務(wù)環(huán)境,是的話就把Connection對(duì)象封裝成ConnectionHolder保存在事務(wù)管理器中,這樣的話就能保證一個(gè)事務(wù)中的數(shù)據(jù)庫(kù)連接是同一個(gè)。

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/74587.html

相關(guān)文章

  • spring和mybatis的整合

    摘要:第一是手動(dòng)在的配置文件中使用部分來(lái)指定類路徑。第二是使用工廠的屬性。注解和樣式的配置都是支持的。在事務(wù)處理期間一個(gè)單獨(dú)的對(duì)象將會(huì)被創(chuàng)建和使用。創(chuàng)建的代理控制開放和關(guān)閉翻譯任意的異常到的異常中。每個(gè)映射器將會(huì)在指定的包路徑中遞歸地被搜索到。 mybatis-spring 若要整合spring和mybatis就需要一個(gè)插件即mybatis-spring-x.x.x.jar。具體的安裝如下所...

    vspiders 評(píng)論0 收藏0
  • SSM : 環(huán)境搭建

    摘要:這個(gè)文件包含對(duì)對(duì)數(shù)據(jù)訪問(wèn)進(jìn)行封裝的所有類。為等提供的一致的聲明式和編程式事務(wù)管理。 SSM 環(huán)境搭建 目錄創(chuàng)建 pom.xml SSM 逐層配置 一、目錄 1.1 src/main/java 目錄下的包(以下包要放在項(xiàng)目包下,如:com.imooc.項(xiàng)目名) entity: 存放實(shí)體類 web: 存放controller,相當(dāng)于Struts中的action service: 業(yè)務(wù)...

    MonoLog 評(píng)論0 收藏0
  • MyBatis的原理

    摘要:不是線程安全的,所以在使用的時(shí)候一定要保證他是局部變量。他對(duì)應(yīng)的類圖如下有幾種常見的實(shí)現(xiàn)是默認(rèn)的非線程安全的實(shí)現(xiàn)是中對(duì)的線程安全實(shí)現(xiàn),在內(nèi)部是使用的的形式來(lái)保證線程安全的是的核心。是線程安全的,可以被多個(gè)或映射器所共享使用。 MyBatis核心類 SqlSessionFactory 每一個(gè)MyBatis應(yīng)用都是以一個(gè)SqlSessionFactory的實(shí)例為核心構(gòu)建的。SqlSessi...

    Yu_Huang 評(píng)論0 收藏0
  • intelliJ idea 使用maven創(chuàng)建spring+springMVC+mybatis(SS

    摘要:配置用到的框架和版本配置數(shù)據(jù)庫(kù)核心依賴依賴依賴擴(kuò)展依賴層依賴相關(guān)依賴相關(guān)依賴依賴依賴的包如果你的項(xiàng)目中 ssm demo github : https://github.com/rongyaya10... 配置pom用到的框架和版本:spring 5.0.5.RELEASEmybatis 3.4.0mysql 5.1.35log4j 1.2.17mybatis-spring 1.3.2...

    godlong_X 評(píng)論0 收藏0
  • ibatis.type.TypeException: Could not resolve type

    摘要:結(jié)果描述主要的問(wèn)題是掃描的時(shí)候異常了,通過(guò)增加配置,確保被找到即可。 問(wèn)題:ibatis.type.TypeException: Could not resolve type aliasspring-boot jar包啟動(dòng)異常,idea啟動(dòng)沒有任何問(wèn)題 showImg(https://segmentfault.com/img/bV7ycX?w=643&h=409); pom信息: ...

    sf190404 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<