天天看點

mybatis源碼解析(七)-當mybatis一級緩存遇上spring

mybatis源碼解析(一)-開篇

mybatis源碼解析(二)-加載過程

mybatis源碼解析(三)-SqlSession.selectOne類似方法調用過程

mybatis源碼解析(四)-Mapper方法調用過程

mybatis源碼解析(五)-mybatis如何實作的事務控制

mybatis源碼解析(六)-配合spring-tx實作事務的原理

mybatis源碼解析(七)-當mybatis一級緩存遇上spring

轉載請标明出處:

https://blog.csdn.net/bingospunky/article/details/79713284

本文出自馬彬彬的部落格

mybatis一級緩存

mybatis一級緩存的作用範圍是SqlSession,可以通過SqlSession的getMapper方法建立Mapper,可以通過是否是一個SqlSession來控制一級緩存的作用域。

當mybatis一級緩存遇上spring

考慮這樣的情況:controlle、service、mapper都是使用spring容器管理的,在spring容器中,這些bean都是單例的。由于mapper是單例,mapper中的SqlSession如果是同一個,那麼一級緩存就會生效。如果我們執行了一次查詢,且查詢結果已經被一級緩存了,這時候我們修改資料庫裡的内容,再去執行這個查詢的時候,查詢緩存在一級緩存裡的内容,這顯然是不可取的。事實是:當使用mybatis配合spring使用時,每次使用mapper查詢都是新的結果,不會使用一級緩存。那麼這是怎麼做到的呢?

Mapper中有一個屬性叫sqlSession,該屬性是SqlSessionTemplate類型的,當需要執行sql時,Mapper會調用這個SqlSessionTemplate對象去執行。

SqlSessionTemplate裡有一個屬性叫sqlSessionProxy,這個屬性是個jdbc代理對象,當SqlSessionTemplate需要去執行sql時,會調用這個jdbc代理去執行的。我們來看下這個jdbc代理的實作:

Code1

org.mybatis.spring.SqlSessionTemplate.SqlSessionInterceptor

private class SqlSessionInterceptor implements InvocationHandler {
        private SqlSessionInterceptor() {
        }
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            SqlSession sqlSession = SqlSessionUtils.getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
            Object unwrapped;
            try {
                Object result = method.invoke(sqlSession, args);
                if (!SqlSessionUtils.isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
                    sqlSession.commit(true);
                }
                unwrapped = result;
            } catch (Throwable var11) {
                unwrapped = ExceptionUtil.unwrapThrowable(var11);
                if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
                    SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
                    sqlSession = null;
                    Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException)unwrapped);
                    if (translated != null) {
                        unwrapped = translated;
                    }
                }
                throw (Throwable)unwrapped;
            } finally {
                if (sqlSession != null) {
                    SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
                }
            }
            return unwrapped;
        }
    }
           

Code1第5行,這個jdbc代理方法被調用時,每次都會建立新的SqlSession對象,這樣就可以使一級緩存失效。

下面是這個jdbc代理對象的建立過程:

Code2

org.mybatis.spring.SqlSessionTemplate

public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
        Assert.notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
        Assert.notNull(executorType, "Property 'executorType' is required");
        this.sqlSessionFactory = sqlSessionFactory;
        this.executorType = executorType;
        this.exceptionTranslator = exceptionTranslator;
        this.sqlSessionProxy = (SqlSession)Proxy.newProxyInstance(SqlSessionFactory.class.getClassLoader(), new Class[]{SqlSession.class}, new SqlSessionTemplate.SqlSessionInterceptor());
    }
           

Code2第7行,是這個jdbc代理對象的建立過程,可以看到這個代理過程,沒有被代理的對象,應該就是專門為了在配合spring時,實作每次調用SqlSession方法時都産生新的SqlSession吧。

繼續閱讀