日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

mybatis返回null_面试官:你分析过mybatis工作原理吗?

發(fā)布時(shí)間:2023/12/15 编程问答 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 mybatis返回null_面试官:你分析过mybatis工作原理吗? 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

Mybatis工作原理也是面試的一大考點(diǎn),必須要對(duì)其非常清晰,這樣才能懟回去。本文建立在Spring+SpringMVC+Mybatis整合的項(xiàng)目之上。

我將其工作原理分為六個(gè)部分:

  • 讀取核心配置文件并返回InputStream流對(duì)象。

  • 根據(jù)InputStream流對(duì)象解析出Configuration對(duì)象,然后創(chuàng)建SqlSessionFactory工廠對(duì)象

  • 根據(jù)一系列屬性從SqlSessionFactory工廠中創(chuàng)建SqlSession

  • 從SqlSession中調(diào)用Executor執(zhí)行數(shù)據(jù)庫(kù)操作&&生成具體SQL指令

  • 對(duì)執(zhí)行結(jié)果進(jìn)行二次封裝

  • 提交與事務(wù)

  • 先給大家看看我的實(shí)體類(lèi):

    /**
    ?*?圖書(shū)實(shí)體
    ?*/
    public?class?Book?{

    ????private?long?bookId;//?圖書(shū)ID

    ????private?String?name;//?圖書(shū)名稱(chēng)

    ????private?int?number;//?館藏?cái)?shù)量

    ????????getter?and?setter?...
    }

    1. 讀取核心配置文件

    1.1 配置文件mybatis-config.xml

    <?xml ?version="1.0"?encoding="UTF-8"??>
    configuration
    ??PUBLIC?"-//mybatis.org//DTD?Config?3.0//EN"
    ??"http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
    ????<environments?default="development">
    ????????<environment?id="development">
    ????????????<transactionManager?type="JDBC"/>
    ????????????<dataSource?type="POOLED">
    ????????????????<property?name="driver"?value="com.mysql.jdbc.Driver"/>
    ????????????????<property?name="url"?value="jdbc:mysql://xxx.xxx:3306/ssm"?/>
    ????????????????<property?name="username"?value="root"/>
    ????????????????<property?name="password"?value="root"/>
    ????????????dataSource>
    ????????environment>
    ????environments>
    ????????<mappers>
    ????????<mapper?resource="BookMapper.xml"/>
    ????mappers>
    configuration>

    當(dāng)然,還有很多可以在XML 文件中進(jìn)行配置,上面的示例指出的則是最關(guān)鍵的部分。要注意 XML 頭部的聲明,用來(lái)驗(yàn)證 XML 文檔正確性。environment 元素體中包含了事務(wù)管理和連接池的配置。mappers 元素則是包含一組 mapper 映射器(這些 mapper 的 XML 文件包含了 SQL 代碼和映射定義信息)。

    1.2 BookMapper.xml

    <?xml ?version="1.0"?encoding="UTF-8"?>
    mapper
    ????PUBLIC?"-//mybatis.org//DTD?Mapper?3.0//EN"
    ????"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper?namespace="Book">
    ????
    ????<insert?id="insert"?>
    ????????insert?into?book?(name,number)?values?(#{name},#{number})
    ????insert>
    mapper>

    就是一個(gè)普通的mapper.xml文件。

    1.3 Main方法

    從 XML 文件中構(gòu)建 SqlSessionFactory 的實(shí)例非常簡(jiǎn)單,建議使用類(lèi)路徑下的資源文件進(jìn)行配置。但是也可以使用任意的輸入流(InputStream)實(shí)例,包括字符串形式的文件路徑或者 file:// 的 URL 形式的文件路徑來(lái)配置。

    MyBatis 包含一個(gè)名叫 Resources 的工具類(lèi),它包含一些實(shí)用方法,可使從 classpath 或其他位置加載資源文件更加容易。

    public?class?Main?{
    ????public?static?void?main(String[]?args)?throws?IOException?{
    ????????//?創(chuàng)建一個(gè)book對(duì)象
    ????????Book?book?=?new?Book();
    ????????book.setBookId(1006);
    ????????book.setName("Easy?Coding");
    ????????book.setNumber(110);
    ????????//?加載配置文件?并構(gòu)建SqlSessionFactory對(duì)象
    ????????String?resource?=?"mybatis-config.xml";
    ????????InputStream?inputStream?=?Resources.getResourceAsStream(resource);
    ????????SqlSessionFactory?factory?=?new?SqlSessionFactoryBuilder().build(inputStream);
    ????????//?從SqlSessionFactory對(duì)象中獲取?SqlSession對(duì)象
    ????????SqlSession?sqlSession?=?factory.openSession();
    ????????//?執(zhí)行操作
    ????????sqlSession.insert("insert",?book);
    ????????//?提交操作
    ????????sqlSession.commit();
    ????????//?關(guān)閉SqlSession
    ????????sqlSession.close();
    ????}
    }

    這個(gè)代碼是根據(jù)Mybatis官方提供的一個(gè)不使用 XML 構(gòu)建 SqlSessionFactory的一個(gè)Demo改編的。

    注意:是官方給的一個(gè)不使用 XML 構(gòu)建 SqlSessionFactory的例子,那么我們就從這個(gè)例子中查找入口來(lái)分析。

    2. 根據(jù)配置文件生成SqlSessionFactory工廠對(duì)象

    2.1 Resources.getResourceAsStream(resource);源碼分析

    Resources是mybatis提供的一個(gè)加載資源文件的工具類(lèi)。

    我們只看getResourceAsStream方法:

    public?static?InputStream?getResourceAsStream(String?resource)?throws?IOException?{
    ????return?getResourceAsStream((ClassLoader)null,?resource);
    }

    getResourceAsStream調(diào)用下面的方法:

    public?static?InputStream?getResourceAsStream(ClassLoader?loader,?String?resource)?throws?IOException?{
    ????InputStream?in?=?classLoaderWrapper.getResourceAsStream(resource,?loader);
    ????if?(in?==?null)?{
    ????????throw?new?IOException("Could?not?find?resource?"?+?resource);
    ????}?else?{
    ????????return?in;
    ????}
    }

    獲取到自身的ClassLoader對(duì)象,然后交給ClassLoader(lang包下的)來(lái)加載:

    InputStream?getResourceAsStream(String?resource,?ClassLoader[]?classLoader)?{
    ????ClassLoader[]?arr$?=?classLoader;
    ????int?len$?=?classLoader.length;

    ????for(int?i$?=?0;?i$?????????ClassLoader?cl?=?arr$[i$];
    ????????if?(null?!=?cl)?{
    ????????????InputStream?returnValue?=?cl.getResourceAsStream(resource);
    ????????????if?(null?==?returnValue)?{
    ????????????????returnValue?=?cl.getResourceAsStream("/"?+?resource);
    ????????????}

    ????????????if?(null?!=?returnValue)?{
    ????????????????return?returnValue;
    ????????????}
    ????????}
    ????}

    值的注意的是,它返回了一個(gè)InputStream對(duì)象。

    2.2 new SqlSessionFactoryBuilder().build(inputStream);源碼分析

    public?SqlSessionFactoryBuilder()?{
    }

    所以new SqlSessionFactoryBuilder()只是創(chuàng)建一個(gè)對(duì)象實(shí)例,而沒(méi)有對(duì)象返回(建造者模式),對(duì)象的返回交給build()方法。

    public?SqlSessionFactory?build(InputStream?inputStream)?{
    ????return?this.build((InputStream)inputStream,?(String)null,?(Properties)null);
    }

    這里要傳入一個(gè)inputStream對(duì)象,就是將我們上一步獲取到的InputStream對(duì)象傳入。

    public?SqlSessionFactory?build(InputStream?inputStream,?String?environment,?Properties?properties)?{
    ????SqlSessionFactory?var5;
    ????try?{
    ????????//?進(jìn)行XML配置文件的解析
    ????????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;
    }

    如何解析的就大概說(shuō)下,通過(guò)Document對(duì)象來(lái)解析,然后返回InputStream對(duì)象,然后交給XMLConfigBuilder構(gòu)造成org.apache.ibatis.session.Configuration對(duì)象,然后交給build()方法構(gòu)造程SqlSessionFactory:

    public?SqlSessionFactory?build(Configuration?config)?{
    ????return?new?DefaultSqlSessionFactory(config);
    }
    public?DefaultSqlSessionFactory(Configuration?configuration)?{
    ????this.configuration?=?configuration;
    }

    3. 創(chuàng)建SqlSession

    SqlSession 完全包含了面向數(shù)據(jù)庫(kù)執(zhí)行 SQL 命令所需的所有方法。你可以通過(guò) SqlSession 實(shí)例來(lái)直接執(zhí)行已映射的 SQL 語(yǔ)句。

    public?SqlSession?openSession()?{
    ????return?this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(),?(TransactionIsolationLevel)null,?false);
    }

    調(diào)用自身的openSessionFromDataSource方法:

  • getDefaultExecutorType()默認(rèn)是SIMPLE。

  • 注意TX等級(jí)是 Null, autoCommit是false。

  • private?SqlSession?openSessionFromDataSource(ExecutorType?execType,?TransactionIsolationLevel?level,?boolean?autoCommit)?{
    ????Transaction?tx?=?null;

    ????DefaultSqlSession?var8;
    ????try?{
    ????????Environment?environment?=?this.configuration.getEnvironment();
    ????????//?根據(jù)Configuration的Environment屬性來(lái)創(chuàng)建事務(wù)工廠
    ????????TransactionFactory?transactionFactory?=?this.getTransactionFactoryFromEnvironment(environment);
    ????????//?從事務(wù)工廠中創(chuàng)建事務(wù),默認(rèn)等級(jí)為null,autoCommit=false
    ????????tx?=?transactionFactory.newTransaction(environment.getDataSource(),?level,?autoCommit);
    ????????//?創(chuàng)建執(zhí)行器
    ????????Executor?executor?=?this.configuration.newExecutor(tx,?execType);
    ????????//?根據(jù)執(zhí)行器創(chuàng)建返回對(duì)象?SqlSession
    ????????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;
    }

    構(gòu)建步驟:Environment>>TransactionFactory+autoCommit+tx-level>>Transaction+ExecType>>Executor+Configuration+autoCommit>>SqlSession

    其中,Environment是Configuration中的屬性。

    4. 調(diào)用Executor執(zhí)行數(shù)據(jù)庫(kù)操作&&生成具體SQL指令

    在拿到SqlSession對(duì)象后,我們調(diào)用它的insert方法。

    public?int?insert(String?statement,?Object?parameter)?{
    ????return?this.update(statement,?parameter);
    }

    它調(diào)用了自身的update(statement, parameter)方法:

    public?int?update(String?statement,?Object?parameter)?{
    ????int?var4;
    ????try?{
    ????????this.dirty?=?true;
    ????????MappedStatement?ms?=?this.configuration.getMappedStatement(statement);
    ????????//?wrapCollection(parameter)判斷?param對(duì)象是否是集合
    ????????var4?=?this.executor.update(ms,?this.wrapCollection(parameter));
    ????}?catch?(Exception?var8)?{
    ????????throw?ExceptionFactory.wrapException("Error?updating?database.??Cause:?"?+?var8,?var8);
    ????}?finally?{
    ????????ErrorContext.instance().reset();
    ????}

    ????return?var4;
    }

    mappedStatements就是我們平時(shí)說(shuō)的sql映射對(duì)象.

    源碼如下:protected final Map mappedStatements;

    可見(jiàn)它是一個(gè)Map集合,在我們加載xml配置的時(shí)候,mapping.xml的namespace和id信息就會(huì)存放為mappedStatements的key,對(duì)應(yīng)的,sql語(yǔ)句就是對(duì)應(yīng)的value.

    然后調(diào)用BaseExecutor中的update方法:

    public?int?update(MappedStatement?ms,?Object?parameter)?throws?SQLException?{
    ????ErrorContext.instance().resource(ms.getResource()).activity("executing?an?update").object(ms.getId());
    ????if?(this.closed)?{
    ????????throw?new?ExecutorException("Executor?was?closed.");
    ????}?else?{
    ????????this.clearLocalCache();
    ????????//?真正做執(zhí)行操作的方法
    ????????return?this.doUpdate(ms,?parameter);
    ????}
    }

    doUpdate才是真正做執(zhí)行操作的方法:

    public?int?doUpdate(MappedStatement?ms,?Object?parameter)?throws?SQLException?{
    ????Statement?stmt?=?null;

    ????int?var6;
    ????try?{
    ????????Configuration?configuration?=?ms.getConfiguration();
    ????????//?創(chuàng)建StatementHandler對(duì)象,從而創(chuàng)建Statement對(duì)象
    ????????StatementHandler?handler?=?configuration.newStatementHandler(this,?ms,?parameter,?RowBounds.DEFAULT,?(ResultHandler)null,?(BoundSql)null);
    ????????//?將sql語(yǔ)句和參數(shù)綁定并生成SQL指令
    ????????stmt?=?this.prepareStatement(handler,?ms.getStatementLog());
    ????????var6?=?handler.update(stmt);
    ????}?finally?{
    ????????this.closeStatement(stmt);
    ????}

    ????return?var6;
    }

    先來(lái)看看prepareStatement方法,看看mybatis是如何將sql拼接合成的:

    private?Statement?prepareStatement(StatementHandler?handler,?Log?statementLog)?throws?SQLException?{
    ????Connection?connection?=?this.getConnection(statementLog);
    ????//?準(zhǔn)備Statement
    ????Statement?stmt?=?handler.prepare(connection);
    ????//?設(shè)置SQL查詢(xún)中的參數(shù)值
    ????handler.parameterize(stmt);
    ????return?stmt;
    }

    來(lái)看看parameterize方法:

    public?void?parameterize(Statement?statement)?throws?SQLException?{
    ????this.parameterHandler.setParameters((PreparedStatement)statement);
    }

    這里把statement轉(zhuǎn)換程PreparedStatement對(duì)象,它比Statement更快更安全。
    這都是我們?cè)贘DBC中熟用的對(duì)象,就不做介紹了,所以也能看出來(lái)Mybatis是對(duì)JDBC的封裝。

    從ParameterMapping中讀取參數(shù)值和類(lèi)型,然后設(shè)置到SQL語(yǔ)句中:

    public?void?setParameters(PreparedStatement?ps)?{
    ????ErrorContext.instance().activity("setting?parameters").object(this.mappedStatement.getParameterMap().getId());
    ????List?parameterMappings?=?this.boundSql.getParameterMappings();if?(parameterMappings?!=?null)?{for(int?i?=?0;?i?????????????ParameterMapping?parameterMapping?=?(ParameterMapping)parameterMappings.get(i);if?(parameterMapping.getMode()?!=?ParameterMode.OUT)?{
    ????????????????String?propertyName?=?parameterMapping.getProperty();
    ????????????????Object?value;if?(this.boundSql.hasAdditionalParameter(propertyName))?{value?=?this.boundSql.getAdditionalParameter(propertyName);
    ????????????????}?else?if?(this.parameterObject?==?null)?{value?=?null;
    ????????????????}?else?if?(this.typeHandlerRegistry.hasTypeHandler(this.parameterObject.getClass()))?{value?=?this.parameterObject;
    ????????????????}?else?{
    ????????????????????MetaObject?metaObject?=?this.configuration.newMetaObject(this.parameterObject);value?=?metaObject.getValue(propertyName);
    ????????????????}
    ????????????????TypeHandler?typeHandler?=?parameterMapping.getTypeHandler();
    ????????????????JdbcType?jdbcType?=?parameterMapping.getJdbcType();if?(value?==?null?&&?jdbcType?==?null)?{
    ????????????????????jdbcType?=?this.configuration.getJdbcTypeForNull();
    ????????????????}try?{
    ????????????????????typeHandler.setParameter(ps,?i?+?1,?value,?jdbcType);
    ????????????????}?catch?(TypeException?var10)?{throw?new?TypeException("Could?not?set?parameters?for?mapping:?"?+?parameterMapping?+?".?Cause:?"?+?var10,?var10);
    ????????????????}?catch?(SQLException?var11)?{throw?new?TypeException("Could?not?set?parameters?for?mapping:?"?+?parameterMapping?+?".?Cause:?"?+?var11,?var11);
    ????????????????}
    ????????????}
    ????????}
    ????}
    }

    5. 對(duì)查詢(xún)結(jié)果二次封裝

    在doUpdate方法中,解析生成完新的SQL后,然后執(zhí)行var6 = handler.update(stmt);我們來(lái)看看它的源碼。

    public?int?update(Statement?statement)?throws?SQLException?{
    ????PreparedStatement?ps?=?(PreparedStatement)statement;
    ?????//?執(zhí)行sql
    ????ps.execute();
    ????//?獲取返回值
    ????int?rows?=?ps.getUpdateCount();
    ????Object?parameterObject?=?this.boundSql.getParameterObject();
    ????KeyGenerator?keyGenerator?=?this.mappedStatement.getKeyGenerator();
    ????keyGenerator.processAfter(this.executor,?this.mappedStatement,?ps,?parameterObject);
    ????return?rows;
    }

    因?yàn)槲覀兪遣迦氩僮?#xff0c;返回的是一個(gè)int類(lèi)型的值,所以這里mybatis給我們直接返回int。

    如果是query操作,返回的是一個(gè)ResultSet,mybatis將查詢(xún)結(jié)果包裝程ResultSetWrapper類(lèi)型,然后一步步對(duì)應(yīng)java類(lèi)型賦值等…有興趣的可以自己去看看。

    6. 提交與事務(wù)

    最后,來(lái)看看commit()方法的源碼。

    public?void?commit()?{
    ????this.commit(false);
    }

    調(diào)用其對(duì)象本身的commit()方法:

    public?void?commit(boolean?force)?{
    ????try?{
    ????????//?是否提交(判斷是提交還是回滾)
    ????????this.executor.commit(this.isCommitOrRollbackRequired(force));
    ????????this.dirty?=?false;
    ????}?catch?(Exception?var6)?{
    ????????throw?ExceptionFactory.wrapException("Error?committing?transaction.??Cause:?"?+?var6,?var6);
    ????}?finally?{
    ????????ErrorContext.instance().reset();
    ????}
    }

    如果dirty是false,則進(jìn)行回滾;如果是true,則正常提交。

    private?boolean?isCommitOrRollbackRequired(boolean?force)?{
    ????return?!this.autoCommit?&&?this.dirty?||?force;
    }

    調(diào)用CachingExecutor的commit方法:

    public?void?commit(boolean?required)?throws?SQLException?{
    ????this.delegate.commit(required);
    ????this.tcm.commit();
    }

    調(diào)用BaseExecutor的commit方法:

    public?void?commit(boolean?required)?throws?SQLException?{
    ????if?(this.closed)?{
    ????????throw?new?ExecutorException("Cannot?commit,?transaction?is?already?closed");
    ????}?else?{
    ????????this.clearLocalCache();
    ????????this.flushStatements();
    ????????if?(required)?{
    ????????????this.transaction.commit();
    ????????}

    ????}
    }

    最后調(diào)用JDBCTransaction的commit方法:

    public?void?commit()?throws?SQLException?{
    ????if?(this.connection?!=?null?&&?!this.connection.getAutoCommit())?{
    ????????if?(log.isDebugEnabled())?{
    ????????????log.debug("Committing?JDBC?Connection?["?+?this.connection?+?"]");
    ????????}
    ????????//?提交連接
    ????????this.connection.commit();
    ????}
    }

    Demo參考文檔

    http://www.mybatis.org/mybatis-3/zh/getting-started.html

    END

    Java面試題專(zhuān)欄

    【20期】你知道為什么HashMap是線程不安全的嗎?

    【19期】為什么Java線程沒(méi)有Running狀態(tài)?

    【18期】Java序列化與反序列化三連問(wèn):是什么?為什么要?如何做?

    【17期】什么情況用ArrayList or LinkedList呢?

    【16期】你能談?wù)凥ashMap怎樣解決hash沖突嗎

    【15期】談?wù)勥@幾個(gè)常見(jiàn)的多線程面試題

    【14期】你能說(shuō)說(shuō)進(jìn)程與線程的區(qū)別嗎

    【13期】談?wù)?Redis 的過(guò)期策略

    【12期】談?wù)勴?xiàng)目中單點(diǎn)登錄的實(shí)現(xiàn)原理?

    【11期】分布式系統(tǒng)接口,如何避免表單的重復(fù)提交?

    我知道你 “在看

    創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)

    總結(jié)

    以上是生活随笔為你收集整理的mybatis返回null_面试官:你分析过mybatis工作原理吗?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。