国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - spring 整合mybatis后用不上session緩存的原因分析

spring 整合mybatis后用不上session緩存的原因分析

2020-08-12 10:58java教程網 Java教程

因為一直用spring整合了mybatis,所以很少用到mybatis的session緩存。什么原因呢?下面小編給大家介紹spring 整合mybatis后用不上session緩存的原因分析,需要的朋友可以參考下

因為一直用spring整合了mybatis,所以很少用到mybatis的session緩存。 習慣是本地緩存自己用map寫或者引入第三方的本地緩存框架ehcache,Guava

所以提出來糾結下

實驗下(spring整合mybatis略,網上一堆),先看看mybatis級別的session的緩存

放出打印sql語句

configuration.xml 加入

?
1
2
3
4
<settings>
    <!-- 打印查詢語句 -->
    <setting name="logImpl" value="STDOUT_LOGGING" />
  </settings>

測試源代碼如下:

dao類 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/**
 * 測試spring里的mybatis為啥用不上緩存
 *
 * @author 何錦彬 2017.02.15
 */
@Component
public class TestDao {
  private Logger logger = Logger.getLogger(TestDao.class.getName());
  @Autowired
  private SqlSessionTemplate sqlSessionTemplate;
  @Autowired
  private SqlSessionFactory sqlSessionFactory;
  /**
   * 兩次SQL
   *
   * @param id
   * @return
   */
  public TestDto selectBySpring(String id) {
    TestDto testDto = (TestDto) sqlSessionTemplate.selectOne("com.hejb.TestDto.selectByPrimaryKey", id);
    testDto = (TestDto) sqlSessionTemplate.selectOne("com.hejb.TestDto.selectByPrimaryKey", id);
    return testDto;
  }
  /**
   * 一次SQL
   *
   * @param id
   * @return
   */
  public TestDto selectByMybatis(String id) {
    SqlSession session = sqlSessionFactory.openSession();
    TestDto testDto = session.selectOne("com.hejb.TestDto.selectByPrimaryKey", id);
    testDto = session.selectOne("com.hejb.TestDto.selectByPrimaryKey", id);
    return testDto;
  }
}

測試service類

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Component
public class TestService {
  @Autowired
  private TestDao testDao;
  /**
   * 未開啟事務的spring Mybatis查詢
   */
  public void testSpringCashe() {
    //查詢了兩次SQL
    testDao.selectBySpring("1");
  }
  /**
   * 開啟事務的spring Mybatis查詢
   */
  @Transactional
  public void testSpringCasheWithTran() {
    //spring開啟事務后,查詢1次SQL
    testDao.selectBySpring("1");
  }
  /**
   * mybatis查詢
   */
  public void testCash4Mybatise() {
    //原生態mybatis,查詢了1次SQL
    testDao.selectByMybatis("1");
  }
}

輸出結果:

testSpringCashe()方法執行了兩次SQL, 其它都是一次

源碼追蹤:

先看mybatis里的sqlSession

跟蹤到最后 調用到 org.apache.ibatis.executor.BaseExecutor的query方法

?
1
2
3
4
5
6
7
8
try {
   queryStack++;
   list = resultHandler == null ? (List<E>) localCache.getObject(key) : null; //先從緩存中取
   if (list != null) {
    handleLocallyCachedOutputParameters(ms, key, parameter, boundSql); //注意里面的key是CacheKey
   } else {
    list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
   }

貼下是怎么取出緩存數據的代碼

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private void handleLocallyCachedOutputParameters(MappedStatement ms, CacheKey key, Object parameter, BoundSql boundSql) {
  if (ms.getStatementType() == StatementType.CALLABLE) {
   final Object cachedParameter = localOutputParameterCache.getObject(key);//從localOutputParameterCache取出緩存對象
   if (cachedParameter != null && parameter != null) {
    final MetaObject metaCachedParameter = configuration.newMetaObject(cachedParameter);
    final MetaObject metaParameter = configuration.newMetaObject(parameter);
    for (ParameterMapping parameterMapping : boundSql.getParameterMappings()) {
     if (parameterMapping.getMode() != ParameterMode.IN) {
      final String parameterName = parameterMapping.getProperty();
      final Object cachedValue = metaCachedParameter.getValue(parameterName);
      metaParameter.setValue(parameterName, cachedValue);
     }
    }
   }
  }
 }

 

發現就是從localOutputParameterCache就是一個PerpetualCache, PerpetualCache維護了個map,就是session的緩存本質了。

重點可以關注下面兩個累的邏輯

PerpetualCache , 兩個參數, id和map

CacheKey,map中存的key,它有覆蓋equas方法,當獲取緩存時調用.

這種本地map緩存獲取對象的缺點,就我踩坑經驗(以前我也用map去實現的本地緩存),就是獲取的對象非clone的,返回的兩個對象都是一個地址

而在spring中一般都是用sqlSessionTemplate,如下

?
1
2
3
4
5
6
7
8
9
10
11
12
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="configLocation" value="classpath:configuration.xml" />
    <property name="mapperLocations">
      <list>
        <value>classpath*:com/hejb/sqlmap/*.xml</value>
      </list>
    </property>
  </bean>
  <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
    <constructor-arg ref="sqlSessionFactory" />
  </bean>

在SqlSessionTemplate中執行SQL的session都是通過sqlSessionProxy來,sqlSessionProxy的生成在構造函數中賦值,如下:

?
1
2
3
4
this.sqlSessionProxy = (SqlSession) newProxyInstance(
    SqlSessionFactory.class.getClassLoader(),
    new Class[] { SqlSession.class },
    new SqlSessionInterceptor());

sqlSessionProxy通過JDK的動態代理方法生成的一個代理類,主要邏輯在InvocationHandler對執行的方法進行了前后攔截,主要邏輯在invoke中,包好了每次執行對sqlsesstion的創建,common,關閉

代碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
private class SqlSessionInterceptor implements InvocationHandler {
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
   // 每次執行前都創建一個新的sqlSession
   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);
    }
   }
  }
 }

因為每次都進行創建,所以就用不上sqlSession的緩存了.

對于開啟了事務為什么可以用上呢, 跟入getSqlSession方法

如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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);
  // 首先從SqlSessionHolder里取出session
  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;
 }

在里面維護了個SqlSessionHolder,關聯了事務與session,如果存在則直接取出,否則則新建個session,所以在有事務的里,每個session都是同一個,故能用上緩存了

以上所述是小編給大家介紹的spring 整合mybatis后用不上session緩存的原因分析,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對服務器之家網站的支持!

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 日本中文字幕一区 | 成人高清在线 | 欧美午夜精品久久久 | 在线一级视频 | 狼人狠狠干 | 日韩在线视频观看 | 亚洲精品国产综合99久久夜夜嗨 | 在线观看的av | 亚洲国产精品久久久 | 在线观看中文字幕亚洲 | 黄色在线免费看 | 欧洲精品视频在线观看 | 99视频在线 | 亚洲人一区二区 | 亚洲成人精品在线观看 | 久久久精品国产 | 97超碰青青草 | 日韩和欧美的一区二区 | 91在线视频在线 | 国产精品久久久久久久久 | 国产精品久久久久久久一区探花 | 看av的网址 | 91精品国产欧美一区二区成人 | 欧美特级 | 激情六月婷 | 欧美一区二区三区在线视频观看 | 成人免费毛片高清视频 | 99久久婷婷国产综合精品电影 | 成人片在线播放 | 日韩在线短视频 | 人人澡人人爽 | 亚洲精品久久久久久久久久久 | 亚洲精品久久久久久久久久久久久 | 亚洲国产精品99久久久久久久久 | 一区二区三区四区电影 | 一级片在线观看 | 亚洲一区二区在线播放 | 黄色影视网址 | 免费无遮挡www小视频 | 亚洲视频 欧美视频 | 精品日韩一区二区 |