亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專(zhuān)欄INFORMATION COLUMN

MyBatis原理概括

mikasa / 1032人閱讀

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

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

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

框架圖解釋說(shuō)明

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

3.xml解析&配置解析

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

mybatis啟動(dòng)(編程式)

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

我們?cè)賮?lái)看下這個(gè)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通過(guò)XMLConfigBuilder初始化并且解析了我們的配置文件,最后得到一個(gè)Configuration類(lèi)型的對(duì)象傳給另外一個(gè)build操作,這個(gè)build操作最后直接new了一個(gè)DefaultSqlSessionFactory對(duì)象并且返回。

4.何為Mapper對(duì)象?

通過(guò)上面的敘述我們已經(jīng)知道我們與mybatis交互主要是通過(guò)配置文件或者配置對(duì)象,但是我們最終的目的是要操作數(shù)據(jù)庫(kù)的,所以mybatis為我們提供了sqlSession這個(gè)對(duì)象來(lái)進(jìn)行所有的操作,也就是說(shuō)我們真正通過(guò)mybatis操作數(shù)據(jù)庫(kù)只要對(duì)接sqlSession這個(gè)對(duì)象就可以了。那么問(wèn)題來(lái)了,我們?cè)趺礃油ㄟ^(guò)sqlSession來(lái)了操作數(shù)據(jù)庫(kù)的呢?

問(wèn)題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;
}

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

問(wèn)題2:Mapper對(duì)象怎么來(lái)的?

平時(shí)我們使用如下代碼獲得一個(gè)Mapper對(duì)象。

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

通過(guò)調(diào)用DefaultSqlSession的getMapper方法并且傳入一個(gè)類(lèi)型對(duì)象獲取,底層呢調(diào)用的是配置對(duì)象configuration的getMapper方法,configuration對(duì)象是我們?cè)诩虞dDefaultSqlSessionFactory時(shí)傳入的。

然后我們?cè)賮?lái)看下這個(gè)配置對(duì)象的getMapper,傳入的是類(lèi)型對(duì)象(補(bǔ)充一點(diǎn)這個(gè)類(lèi)型對(duì)象就是我們平時(shí)寫(xiě)的DAO層接口,里面是一些數(shù)據(jù)庫(kù)操作的接口方法。),和自身也就是DefaultSqlSession。

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

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

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

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

從這里我們可以知道其實(shí)啊這個(gè)MapperRegistry就是保持了一個(gè)Configuration對(duì)象和一個(gè)HashMap,而這個(gè)HashMap的key是類(lèi)型對(duì)象,value呢是MapperProxyFactory。我們這里先不管MapperProxyFactory是什么東西,我們現(xiàn)在只需要知道MapperRegistry是這么一個(gè)東西就可以了。這里有人會(huì)問(wèn)MapperRegistry對(duì)象是怎么來(lái)的,這里呢是在初始化Configuration對(duì)象時(shí)初始化了這個(gè)MapperRegistry對(duì)象的,代碼大家可以去看,為了避免混亂,保持貼出來(lái)的代碼是一條線(xiàn)走下來(lái)的,這里就不貼出來(lái)了。接下來(lái)我們繼續(xù)看下這個(gè)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為類(lèi)型對(duì)象的MapperProxyFactory對(duì)象。然后調(diào)用MapperProxyFactory對(duì)象的newInstance方法返回,newInstance方法傳入sqlSession對(duì)象。到這里我們可能看不出什么端倪,那我們就繼續(xù)往下看這個(gè)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了一個(gè)MapperProxy對(duì)象,然后調(diào)用另外一重載的newInstance方法傳入MapperProxy對(duì)象。這里我們可以看出一些東西了,通過(guò)調(diào)用Proxy.newProxyInstance動(dòng)態(tài)代理了我們的mapperProxy對(duì)象!這里的mapperInterface即我們的dao層(持久層)接口的類(lèi)型對(duì)象。
所以總結(jié)下就是我們通過(guò)sqlSesssion.getMapper(clazz)得到的Mapper對(duì)象是一個(gè)mapperProxy的代理類(lèi)!
所以也就引出下面的問(wèn)題。

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

通過(guò)上面的講解,我們知道了這個(gè)mapper對(duì)象其實(shí)是一個(gè)一個(gè)mapperProxy的代理類(lèi)!所以呢這個(gè)mapperProxy必然實(shí)現(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)用我們的持久層接口的方法時(shí)必然就會(huì)調(diào)用到這個(gè)MapperProxy對(duì)象的invoke方法,所以接下來(lái)我們進(jìn)入這個(gè)方法看看具體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;
}

從代碼中我們可以看到前面做了一個(gè)判斷,這個(gè)判斷主要是防止我們調(diào)用像toString方法或者equals方法時(shí)也能正常調(diào)用。然后我們可以看到它調(diào)用cachedMapperMethod返回MapperMethod對(duì)象,接著就執(zhí)行這個(gè)MapperMethod對(duì)象的execute方法。這個(gè)cachedMapperMethod方法主要是能緩存我們使用過(guò)的一些mapperMethod對(duì)象,方便下次使用。這個(gè)MapperMethod對(duì)象主要是獲取方法對(duì)應(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);
    }
    ....
}

說(shuō)到這個(gè)mapperMethod對(duì)象的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;
    }
}

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

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

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

接下來(lái)我們看下這個(gè)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方法,通過(guò)去返回值的第一個(gè)值作為結(jié)果返回。那么我們來(lái)看下這個(gè)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方法,我們?cè)龠M(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;
    }
}

這里我們抓住這樣的一句話(huà)

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

進(jìn)入這個(gè)方法

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;
}

我們看到有個(gè)一個(gè)方法doQuery,進(jìn)入方法看看做了什么。點(diǎn)進(jìn)去后我們發(fā)現(xiàn)是抽象方法,我們選擇simpleExecutor子類(lèi)查看實(shí)現(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;
}

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

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

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

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

以上,我們將mybatis的總體運(yùn)行思路跟大家講解了一遍,很多地方?jīng)]有講到細(xì)節(jié)上,因?yàn)楸酒饕康木褪菐Т蠹沂煜ybatis總體流程的,細(xì)節(jié)大家可以私底下結(jié)合mybatis的執(zhí)行流程去梳理和理解。

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

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

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/69593.html

相關(guān)文章

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

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

    qieangel2013 評(píng)論0 收藏0
  • 教你手寫(xiě)Mybatis框架

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

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

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

0條評(píng)論

mikasa

|高級(jí)講師

TA的文章

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