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

資訊專欄INFORMATION COLUMN

MyBatis原理概括

mikasa / 969人閱讀

摘要:避免了幾乎所有的代碼和手動設(shè)置參數(shù)以及獲取結(jié)果集。這個對象主要是獲取方法對應(yīng)的命令和執(zhí)行相應(yīng)操作等的處理,具體細(xì)節(jié)同學(xué)們可以抽空研究。所以這里的方法主要使用了和對象幫助我們處理語句集和參數(shù)的處理。

博文目標(biāo):希望大家看了這篇博文后,對Mybatis整體運行過程有一個清晰的認(rèn)識和把握。 1.什么是 MyBatis ?

MyBatis 是一款優(yōu)秀的持久層框架,它支持定制化 SQL、存儲過程以及高級映射。MyBatis 避免了幾乎所有的 JDBC 代碼和手動設(shè)置參數(shù)以及獲取結(jié)果集。MyBatis 可以使用簡單的 XML 或注解來配置和映射原生信息,將接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java對象)映射成數(shù)據(jù)庫中的記錄。(這是官網(wǎng)解釋)

2.MyBatis運行原理

框架圖解釋說明

當(dāng)框架啟動時,通過configuration解析config.xml配置文件和mapper.xml映射文件,映射文件可以使用xml方式或者注解方式,然后由configuration獲得sqlsessionfactory對象,再由sqlsessionfactory獲得sqlsession數(shù)據(jù)庫訪問會話對象,通過會話對象獲得對應(yīng)DAO層的mapper對象,通過調(diào)用mapper對象相應(yīng)方法,框架就會自動執(zhí)行SQL語句從而獲得結(jié)果。
講完了,6不6,可以,牛逼,就這么簡單。此時心中是否有千萬只草泥馬奔涌而出,別急,對于上述,我會在下面針對重點進(jìn)行一一講解。

3.xml解析&配置解析

這里請大家自行百度解決,網(wǎng)上也有比較多的解析庫,對于大家來說應(yīng)該是沒有什么問題,我們這邊主要抓住框架運行的總體過程。對于細(xì)節(jié)大家可以課后慢慢研究。

mybatis啟動(編程式)

String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

我們再來看下這個build操作在底層做了什么

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    SqlSessionFactory var5;
    try {
        XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
        var5 = this.build(parser.parse());
    } catch (Exception var14) {
        throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
    } finally {
        ErrorContext.instance().reset();

        try {
            inputStream.close();
        } catch (IOException var13) {
            ;
        }
    }
    return var5;
}

public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
}

我們可以很明顯的看到mybatis通過XMLConfigBuilder初始化并且解析了我們的配置文件,最后得到一個Configuration類型的對象傳給另外一個build操作,這個build操作最后直接new了一個DefaultSqlSessionFactory對象并且返回。

4.何為Mapper對象?

通過上面的敘述我們已經(jīng)知道我們與mybatis交互主要是通過配置文件或者配置對象,但是我們最終的目的是要操作數(shù)據(jù)庫的,所以mybatis為我們提供了sqlSession這個對象來進(jìn)行所有的操作,也就是說我們真正通過mybatis操作數(shù)據(jù)庫只要對接sqlSession這個對象就可以了。那么問題來了,我們怎么樣通過sqlSession來了操作數(shù)據(jù)庫的呢?

問題1:如何獲取sqlSession?

public SqlSession openSession() {
    return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, false);
}
    
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;

    DefaultSqlSession var8;
    try {
        Environment environment = this.configuration.getEnvironment();
        TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
        tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
        Executor executor = this.configuration.newExecutor(tx, execType);
        var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
    } catch (Exception var12) {
        this.closeTransaction(tx);
        throw ExceptionFactory.wrapException("Error opening session.  Cause: " + var12, var12);
    } finally {
        ErrorContext.instance().reset();
    }
    
    return var8;
}

由上面代碼我們可知我們可以通過SqlSessionFactory的openSession去獲取我們的sqlSession,也就是默認(rèn)得到一個DefaultSqlSession對象。

問題2:Mapper對象怎么來的?

平時我們使用如下代碼獲得一個Mapper對象。

public  T getMapper(Class type) {
    return this.configuration.getMapper(type, this);
}

通過調(diào)用DefaultSqlSession的getMapper方法并且傳入一個類型對象獲取,底層呢調(diào)用的是配置對象configuration的getMapper方法,configuration對象是我們在加載DefaultSqlSessionFactory時傳入的。

然后我們再來看下這個配置對象的getMapper,傳入的是類型對象(補(bǔ)充一點這個類型對象就是我們平時寫的DAO層接口,里面是一些數(shù)據(jù)庫操作的接口方法。),和自身也就是DefaultSqlSession。

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

我們看到這個configuration的getMapper方法里調(diào)用的是mapperRegistry的getMapper方法,參數(shù)依然是類型對象和sqlSession。這里呢,我們要先來看下這個MapperRegistry即所謂Mapper注冊器是什么。

public class MapperRegistry {
    private final Configuration config;
    private final Map, MapperProxyFactory> knownMappers = new HashMap();

    public MapperRegistry(Configuration config) {
        this.config = config;
    }
    ....
}

從這里我們可以知道其實啊這個MapperRegistry就是保持了一個Configuration對象和一個HashMap,而這個HashMap的key是類型對象,value呢是MapperProxyFactory。我們這里先不管MapperProxyFactory是什么東西,我們現(xiàn)在只需要知道MapperRegistry是這么一個東西就可以了。這里有人會問MapperRegistry對象是怎么來的,這里呢是在初始化Configuration對象時初始化了這個MapperRegistry對象的,代碼大家可以去看,為了避免混亂,保持貼出來的代碼是一條線走下來的,這里就不貼出來了。接下來我們繼續(xù)看下這個MapperRegistry的getMapper方法。

public  T getMapper(Class type, SqlSession sqlSession) {
    MapperProxyFactory mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
    if (mapperProxyFactory == null) {
        throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    } else {
        try {
            return mapperProxyFactory.newInstance(sqlSession);
        } catch (Exception var5) {
            throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
        }
    }
}

這里我們可以看到從knownMappers中獲取key為類型對象的MapperProxyFactory對象。然后調(diào)用MapperProxyFactory對象的newInstance方法返回,newInstance方法傳入sqlSession對象。到這里我們可能看不出什么端倪,那我們就繼續(xù)往下看這個newInstance方法做的什么事情吧。

public class MapperProxyFactory {
    private final Class mapperInterface;
    private final Map methodCache = new ConcurrentHashMap();

    public MapperProxyFactory(Class mapperInterface) {
        this.mapperInterface = mapperInterface;
    }

    public Class getMapperInterface() {
        return this.mapperInterface;
    }

    public Map getMethodCache() {
        return this.methodCache;
    }

    protected T newInstance(MapperProxy mapperProxy) {
        return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
    }

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

這里我們可以看到MapperProxyFactory直接new了一個MapperProxy對象,然后調(diào)用另外一重載的newInstance方法傳入MapperProxy對象。這里我們可以看出一些東西了,通過調(diào)用Proxy.newProxyInstance動態(tài)代理了我們的mapperProxy對象!這里的mapperInterface即我們的dao層(持久層)接口的類型對象。
所以總結(jié)下就是我們通過sqlSesssion.getMapper(clazz)得到的Mapper對象是一個mapperProxy的代理類!
所以也就引出下面的問題。

問題3:為什么我調(diào)用mapper對象方法就能發(fā)出sql操作數(shù)據(jù)庫?

通過上面的講解,我們知道了這個mapper對象其實是一個一個mapperProxy的代理類!所以呢這個mapperProxy必然實現(xiàn)了InvocationHandler接口。

public class MapperProxy implements InvocationHandler, Serializable {
    private static final long serialVersionUID = -6424540398559729838L;
    private final SqlSession sqlSession;
    private final Class mapperInterface;
    private final Map methodCache;

    public MapperProxy(SqlSession sqlSession, Class mapperInterface, Map methodCache) {
        this.sqlSession = sqlSession;
        this.mapperInterface = mapperInterface;
        this.methodCache = methodCache;
    }
    ....
}

所以當(dāng)我們調(diào)用我們的持久層接口的方法時必然就會調(diào)用到這個MapperProxy對象的invoke方法,所以接下來我們進(jìn)入這個方法看看具體mybatis為我們做了什么。

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

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

    return mapperMethod;
}

從代碼中我們可以看到前面做了一個判斷,這個判斷主要是防止我們調(diào)用像toString方法或者equals方法時也能正常調(diào)用。然后我們可以看到它調(diào)用cachedMapperMethod返回MapperMethod對象,接著就執(zhí)行這個MapperMethod對象的execute方法。這個cachedMapperMethod方法主要是能緩存我們使用過的一些mapperMethod對象,方便下次使用。這個MapperMethod對象主要是獲取方法對應(yīng)的sql命令和執(zhí)行相應(yīng)SQL操作等的處理,具體細(xì)節(jié)同學(xué)們可以抽空研究。

public class MapperMethod {
    private final MapperMethod.SqlCommand command;
    private final MapperMethod.MethodSignature method;

    public MapperMethod(Class mapperInterface, Method method, Configuration config) {
        this.command = new MapperMethod.SqlCommand(config, mapperInterface, method);
        this.method = new MapperMethod.MethodSignature(config, mapperInterface, method);
    }
    ....
}

說到這個mapperMethod對象的execute方法,我們看下代碼具體做了什么事情吧。

public Object execute(SqlSession sqlSession, Object[] args) {
    Object param;
    Object result;
    switch(this.command.getType()) {
    case INSERT:
        param = this.method.convertArgsToSqlCommandParam(args);
        result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
        break;
    case UPDATE:
        param = this.method.convertArgsToSqlCommandParam(args);
        result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
        break;
    case DELETE:
        param = this.method.convertArgsToSqlCommandParam(args);
        result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
        break;
    case SELECT:
        if (this.method.returnsVoid() && this.method.hasResultHandler()) {
            this.executeWithResultHandler(sqlSession, args);
            result = null;
        } else if (this.method.returnsMany()) {
            result = this.executeForMany(sqlSession, args);
        } else if (this.method.returnsMap()) {
            result = this.executeForMap(sqlSession, args);
        } else if (this.method.returnsCursor()) {
            result = this.executeForCursor(sqlSession, args);
        } else {
            param = this.method.convertArgsToSqlCommandParam(args);
            result = sqlSession.selectOne(this.command.getName(), param);
        }
        break;
    case FLUSH:
        result = sqlSession.flushStatements();
        break;
    default:
        throw new BindingException("Unknown execution method for: " + this.command.getName());
    }

    if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {
        throw new BindingException("Mapper method "" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");
    } else {
        return result;
    }
}

我們可以清晰的看到這里針對數(shù)據(jù)庫的增刪改查做了對應(yīng)的操作,這里我們可以看下查詢操作。我們可以看到這里針對方法的不同返回值作了不同的處理,我們看下其中一種情況。

param = this.method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(this.command.getName(), param);

這里我們可以看到它將方法參數(shù)類型轉(zhuǎn)換成數(shù)據(jù)庫層面上的參數(shù)類型,最后調(diào)用sqlSession對象的selectOne方法執(zhí)行。所以我們看到最后還是回到sqlSession對象上來,也就是前面所說的sqlSession是mybatis提供的與數(shù)據(jù)庫交互的唯一對象。

接下來我們看下這個selectOne方法做了什么事,這里我們看的是defaultSqlSession的selectOne方法。

public  T selectOne(String statement, Object parameter) {
    List list = this.selectList(statement, parameter);
    if (list.size() == 1) {
        return list.get(0);
    } else if (list.size() > 1) {
        throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
    } else {
        return null;
    }
}

我們看到它調(diào)用selectList方法,通過去返回值的第一個值作為結(jié)果返回。那么我們來看下這個selectList方法。

public  List selectList(String statement, Object parameter) {
    return this.selectList(statement, parameter, RowBounds.DEFAULT);
}

public  List selectList(String statement, Object parameter, RowBounds rowBounds) {
    List var5;
    try {
        MappedStatement ms = this.configuration.getMappedStatement(statement);
        var5 = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
    } catch (Exception var9) {
        throw ExceptionFactory.wrapException("Error querying database.  Cause: " + var9, var9);
    } finally {
        ErrorContext.instance().reset();
    }

    return var5;
}

我們可以看到這里調(diào)用了executor的query方法,我們再進(jìn)入到query里看看。這里我們看的是BaseExecutor的query方法。

public  List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    BoundSql boundSql = ms.getBoundSql(parameter);
    CacheKey key = this.createCacheKey(ms, parameter, rowBounds, boundSql);
    return this.query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}

public  List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
    if (this.closed) {
        throw new ExecutorException("Executor was closed.");
    } else {
        if (this.queryStack == 0 && ms.isFlushCacheRequired()) {
            this.clearLocalCache();
        }

        List list;
        try {
            ++this.queryStack;
            list = resultHandler == null ? (List)this.localCache.getObject(key) : null;
            if (list != null) {
                this.handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
            } else {
                list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
            }
        } finally {
            --this.queryStack;
        }

        if (this.queryStack == 0) {
            Iterator i$ = this.deferredLoads.iterator();

            while(i$.hasNext()) {
                BaseExecutor.DeferredLoad deferredLoad = (BaseExecutor.DeferredLoad)i$.next();
                deferredLoad.load();
            }

            this.deferredLoads.clear();
            if (this.configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
                this.clearLocalCache();
            }
        }

        return list;
    }
}

這里我們抓住這樣的一句話

list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);

進(jìn)入這個方法

private  List queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    this.localCache.putObject(key, ExecutionPlaceholder.EXECUTION_PLACEHOLDER);

    List list;
    try {
        list = this.doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    } finally {
        this.localCache.removeObject(key);
    }

    this.localCache.putObject(key, list);
    if (ms.getStatementType() == StatementType.CALLABLE) {
        this.localOutputParameterCache.putObject(key, parameter);
    }

    return list;
}

我們看到有個一個方法doQuery,進(jìn)入方法看看做了什么。點進(jìn)去后我們發(fā)現(xiàn)是抽象方法,我們選擇simpleExecutor子類查看實現(xiàn)。

public  List doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;

    List var9;
    try {
        Configuration configuration = ms.getConfiguration();
        StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
        stmt = this.prepareStatement(handler, ms.getStatementLog());
        var9 = handler.query(stmt, resultHandler);
    } finally {
        this.closeStatement(stmt);
    }

    return var9;
}

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Connection connection = this.getConnection(statementLog);
    Statement stmt = handler.prepare(connection, this.transaction.getTimeout());
    handler.parameterize(stmt);
    return stmt;
}

我們可以看到通過configuration對象的newStatementHandler方法構(gòu)建了一個StatementHandler,然后在調(diào)用prepareStatement方法中獲取連接對象,通過StatementHandler得到Statement對象。另外我們注意到在獲取了Statement對象后調(diào)用了parameterize方法。繼續(xù)跟蹤下去(自行跟蹤哈)我們可以發(fā)現(xiàn)會調(diào)用到ParameterHandler對象的setParameters去處理我們的參數(shù)。所以這里的prepareStatement方法主要使用了StatementHandler和ParameterHandler對象幫助我們處理語句集和參數(shù)的處理。最后還調(diào)用了StatementHandler的query方法,我們繼續(xù)跟蹤下去。

這里我們進(jìn)入到PreparedStatementHandler這個handler查看代碼。

public  List query(Statement statement, ResultHandler resultHandler) throws SQLException {
    c ps = (PreparedStatement)statement;
    ps.execute();
    return this.resultSetHandler.handleResultSets(ps);
}

看到這里,我們終于找到了操作數(shù)據(jù)庫的地方了,就是ps.execute()這句代碼。底層我們可以發(fā)現(xiàn)就是我們平時寫的JDBC!然后將這個執(zhí)行后的PreparedStatement交給resultSetHandler處理結(jié)果集,最后返回我們需要的結(jié)果集。

以上,我們將mybatis的總體運行思路跟大家講解了一遍,很多地方?jīng)]有講到細(xì)節(jié)上,因為本篇主要目的就是帶大家熟悉mybatis總體流程的,細(xì)節(jié)大家可以私底下結(jié)合mybatis的執(zhí)行流程去梳理和理解。

好啦,大家有什么問題都可以在評論區(qū)評論,我看到會盡快回復(fù)噠。哎呀,終于寫完了。拜拜。

噢,對了,這里預(yù)告下,下下篇我將帶大家手寫一遍mybatis!沒錯,純手寫還能跑起來的那種!那下篇呢,下篇當(dāng)然還是講mybatis啦,不過是spring-mybatis!

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

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

相關(guān)文章

  • Spring-Mybatis運行機(jī)制概括

    摘要:使用這個類庫中的類將會加載必要的工廠類和類。最終它并不會依賴于或來構(gòu)建應(yīng)用程序代碼。下面對各部分作用總結(jié)下。和無縫整合的機(jī)制和的認(rèn)識在講如何無縫整合進(jìn)之前,我們先認(rèn)識下和這兩個接口的作用。附上上篇博文地址原理概括。 前言 本篇是繼上篇MyBatis原理概括延伸的,所以如果有小伙伴還沒看上篇博文的話,可以先去看下,也不會浪費大家太多的時間,因為本篇會結(jié)合到上篇敘述的相關(guān)內(nèi)容。 好,切入正...

    qieangel2013 評論0 收藏0
  • 教你手寫Mybatis框架

    摘要:前言嗨,小伙伴們,這篇博文將帶大家手寫,讓大家對的核心原理以及工作流程有更加深刻的理解。模塊顧名思義,就是框架配置類,用于解析配置文件加載相關(guān)環(huán)境。配置模塊這里的對框架的配置使用了簡單的,主要原因還是簡單易懂然后節(jié)省時間。 前言 (????)??嗨,小伙伴們,這篇博文將帶大家手寫mybatis,讓大家對mybaits的核心原理以及工作流程有更加深刻的理解。在上篇Spring-Mybat...

    antyiwei 評論0 收藏0

發(fā)表評論

0條評論

mikasa

|高級講師

TA的文章

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