MyBatis框架及原理分析

MyBatis的配置

MyBatis框架和其他絕大部分框架一樣,需要一個(gè)配置文件,其配置文件大致如下:

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>

<settings>

<setting name="cacheEnabled" value="true"/>

<setting name="lazyLoadingEnabled" value="false"/>

<!--<setting name="logImpl" value="STDOUT_LOGGING"/> <!– 打印日志信息 –>-->

</settings>

<typeAliases>

<typeAlias type="com.luo.dao.UserDao" alias="User"/>

</typeAliases>

<environments default="development">

<environment id="development">

<transactionManager type="JDBC"/> <!--事務(wù)管理類型-->

<dataSource type="POOLED">

<property name="username" value="luoxn28"/>

<property name="password" value="123456"/>

<property name="driver" value="com.mysql.jdbc.Driver"/>

<property name="url" value="jdbc:mysql://192.168.1.150/ssh_study"/>

</dataSource>

</environment>

</environments>

<mappers>

<mapper resource="userMapper.xml"/>

</mappers>

</configuration>

以上配置中,最重要的是數(shù)據(jù)庫(kù)參數(shù)的配置,比如用戶名密碼等,如果配置了數(shù)據(jù)表對(duì)應(yīng)的mapper文件,則需要將其加入到<mappers>節(jié)點(diǎn)下。

MyBatis的主要成員

Configuration:MyBatis所有的配置信息都保存在Configuration對(duì)象之中,配置文件中的大部分配置都會(huì)存儲(chǔ)到該類中。

SqlSession:作為MyBatis工作的主要頂層API,表示和數(shù)據(jù)庫(kù)交互時(shí)的會(huì)話,完成必要數(shù)據(jù)庫(kù)增刪改查功能。

Executor:MyBatis執(zhí)行器,是MyBatis 調(diào)度的核心,負(fù)責(zé)SQL語(yǔ)句的生成和查詢緩存的維護(hù)。

StatementHandler:封裝了JDBC Statement操作,負(fù)責(zé)對(duì)JDBC statement 的操作,如設(shè)置參數(shù)等。

ParameterHandler:負(fù)責(zé)對(duì)用戶傳遞的參數(shù)轉(zhuǎn)換成JDBC Statement所對(duì)應(yīng)的數(shù)據(jù)類型。

ResultSetHandler:負(fù)責(zé)將JDBC返回的ResultSet結(jié)果集對(duì)象轉(zhuǎn)換成List類型的集合。

TypeHandler:負(fù)責(zé)java數(shù)據(jù)類型和jdbc數(shù)據(jù)類型(也可以說(shuō)是數(shù)據(jù)表列類型)之間的映射和轉(zhuǎn)換。

MappedStatement:MappedStatement維護(hù)一條<select|update|delete|insert>節(jié)點(diǎn)的封裝。

SqlSource:負(fù)責(zé)根據(jù)用戶傳遞的parameterObject,動(dòng)態(tài)地生成SQL語(yǔ)句,將信息封裝到BoundSql對(duì)象中,并返回。

BoundSql:表示動(dòng)態(tài)生成的SQL語(yǔ)句以及相應(yīng)的參數(shù)信息。

以上主要成員在一次數(shù)據(jù)庫(kù)操作中基本都會(huì)涉及,在SQL操作中重點(diǎn)需要關(guān)注的是SQL參數(shù)什么時(shí)候被設(shè)置和結(jié)果集怎么轉(zhuǎn)換為JavaBean對(duì)象的,這兩個(gè)過(guò)程正好對(duì)應(yīng)StatementHandler和ResultSetHandler類中的處理邏輯。




MyBatis的初始化

MyBatis的初始化的過(guò)程其實(shí)就是解析配置文件和初始化Configuration的過(guò)程,MyBatis的初始化過(guò)程可用以下幾行代碼來(lái)表述:

String resource = "mybatis.xml";

// 加載mybatis的配置文件(它也加載關(guān)聯(lián)的映射文件)

InputStream inputStream = null;

try {

inputStream = Resources.getResourceAsStream(resource);

} catch (IOException e) {

e.printStackTrace();

}

// 構(gòu)建sqlSession的工廠

sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

首先會(huì)創(chuàng)建SqlSessionFactory建造者對(duì)象,然后由它進(jìn)行創(chuàng)建SqlSessionFactory。這里用到的是建造者模式,建造者模式最簡(jiǎn)單的理解就是不手動(dòng)new對(duì)象,而是由其他類來(lái)進(jìn)行對(duì)象的創(chuàng)建。

// SqlSessionFactoryBuilder類

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {

try {

XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);

return build(parser.parse()); // 開(kāi)始進(jìn)行解析了 :)

} catch (Exception e) {

throw ExceptionFactory.wrapException("Error building SqlSession.", e);

} finally {

ErrorContext.instance().reset();

try {

inputStream.close();

} catch (IOException e) {

// Intentionally ignore. Prefer previous error.

}

}

}

XMLConfigBuilder對(duì)象會(huì)進(jìn)行XML配置文件的解析,實(shí)際為configuration節(jié)點(diǎn)的解析操作。

// XMLConfigBuilder類

public Configuration parse() {

if (parsed) {

throw new BuilderException("Each XMLConfigBuilder can only be used once.");

}

parsed = true;

parseConfiguration(parser.evalNode("/configuration"));

return configuration;

}

private void parseConfiguration(XNode root) {

try {

//issue #117 read properties first

propertiesElement(root.evalNode("properties"));

Properties settings = settingsAsProperties(root.evalNode("settings"));

loadCustomVfs(settings);

typeAliasesElement(root.evalNode("typeAliases"));

pluginElement(root.evalNode("plugins"));

objectFactoryElement(root.evalNode("objectFactory"));

objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));

reflectorFactoryElement(root.evalNode("reflectorFactory"));

settingsElement(settings);

// read it after objectFactory and objectWrapperFactory issue #631

/* 處理environments節(jié)點(diǎn)數(shù)據(jù) */

environmentsElement(root.evalNode("environments"));

databaseIdProviderElement(root.evalNode("databaseIdProvider"));

typeHandlerElement(root.evalNode("typeHandlers"));

mapperElement(root.evalNode("mappers"));

} catch (Exception e) {

throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);

}

}

在configuration節(jié)點(diǎn)下會(huì)依次解析properties/settings/.../mappers等節(jié)點(diǎn)配置。在解析environments節(jié)點(diǎn)時(shí),會(huì)根據(jù)transactionManager的配置來(lái)創(chuàng)建事務(wù)管理器,根據(jù)dataSource的配置來(lái)創(chuàng)建DataSource對(duì)象,這里面包含了數(shù)據(jù)庫(kù)登錄的相關(guān)信息。在解析mappers節(jié)點(diǎn)時(shí),會(huì)讀取該節(jié)點(diǎn)下所有的mapper文件,然后進(jìn)行解析,并將解析后的結(jié)果存到configuration對(duì)象中。

// XMLConfigBuilder類

private void environmentsElement(XNode context) throws Exception {

if (context != null) {

if (environment == null) {

environment = context.getStringAttribute("default");

}

for (XNode child : context.getChildren()) {

String id = child.getStringAttribute("id");

if (isSpecifiedEnvironment(id)) {

/* 創(chuàng)建事務(wù)管理器 */

TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));

DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));

DataSource dataSource = dsFactory.getDataSource();

/* 建造者模式 設(shè)計(jì)模式 */

Environment.Builder environmentBuilder = new Environment.Builder(id)

.transactionFactory(txFactory)

.dataSource(dataSource);

configuration.setEnvironment(environmentBuilder.build());

}

}

}

}

// 解析單獨(dú)的mapper文件

private void mapperElement(XNode parent) throws Exception {

if (parent != null) {

for (XNode child : parent.getChildren()) {

if ("package".equals(child.getName())) {

String mapperPackage = child.getStringAttribute("name");

configuration.addMappers(mapperPackage);

} else {

String resource = child.getStringAttribute("resource");

String url = child.getStringAttribute("url");

String mapperClass = child.getStringAttribute("class");

if (resource != null && url == null && mapperClass == null) {

ErrorContext.instance().resource(resource);

InputStream inputStream = Resources.getResourceAsStream(resource);

XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());

mapperParser.parse(); // 開(kāi)始解析mapper文件了 :)

} else if (resource == null && url != null && mapperClass == null) {

ErrorContext.instance().resource(url);

InputStream inputStream = Resources.getUrlAsStream(url);

XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());

mapperParser.parse();

} else if (resource == null && url == null && mapperClass != null) {

Class<?> mapperInterface = Resources.classForName(mapperClass);

configuration.addMapper(mapperInterface);

} else {

throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");

}

}

}

}

}

解析完MyBatis配置文件后,configuration就初始化完成了,然后根據(jù)configuration對(duì)象來(lái)創(chuàng)建SqlSession,到這里時(shí),MyBatis的初始化的征程已經(jīng)走完了。

如果想學(xué)習(xí)Java工程化、高性能及分布式、深入淺出。微服務(wù)、Spring,MyBatis,Netty源碼分析的朋友可以加我的Java高級(jí)交流:787707172,群里有阿里大牛直播講解技術(shù),以及Java大型互聯(lián)網(wǎng)技術(shù)的視頻免費(fèi)分享給大家。

// SqlSessionFactoryBuilder類

public SqlSessionFactory build(Configuration config) {

return new DefaultSqlSessionFactory(config);

}

MyBatis的SQL查詢流程

SQL語(yǔ)句的執(zhí)行才是MyBatis的重要職責(zé),該過(guò)程就是通過(guò)封裝JDBC進(jìn)行操作,然后使用Java反射技術(shù)完成JavaBean對(duì)象到數(shù)據(jù)庫(kù)參數(shù)之間的相互轉(zhuǎn)換,這種映射關(guān)系就是有TypeHandler對(duì)象來(lái)完成的,在獲取數(shù)據(jù)表對(duì)應(yīng)的元數(shù)據(jù)時(shí),會(huì)保存該表所有列的數(shù)據(jù)庫(kù)類型,大致邏輯如下所示:

/* Get resultSet metadata */

ResultSetMetaData metaData = resultSet.getMetaData();

int column = metaData.getColumnCount();

for (int i = 1; i <= column; i++) {

JdbcType jdbcType = JdbcType.forCode(metaData.getColumnType(i));

typeHandlers.add(TypeHandlerRegistry.getTypeHandler(jdbcType));

columnNames.add(metaData.getColumnName(i));

}

使用如下代碼進(jìn)行SQL查詢操作:

sqlSession = sessionFactory.openSession();

User user = sqlSession.selectOne("com.luo.dao.UserDao.getUserById", 1);

System.out.println(user);

創(chuàng)建sqlSession的過(guò)程其實(shí)就是根據(jù)configuration中的配置來(lái)創(chuàng)建對(duì)應(yīng)的類,然后返回創(chuàng)建的sqlSession對(duì)象。調(diào)用selectOne方法進(jìn)行SQL查詢,selectOne方法最后調(diào)用的是selectList,在selectList中,會(huì)查詢configuration中存儲(chǔ)的MappedStatement對(duì)象,mapper文件中一個(gè)sql語(yǔ)句的配置對(duì)應(yīng)一個(gè)MappedStatement對(duì)象,然后調(diào)用執(zhí)行器進(jìn)行查詢操作。

// DefaultSqlSession類

public <T> T selectOne(String statement, Object parameter) {

// Popular vote was to return null on 0 results and throw exception on too many.

List<T> list = this.<T>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;

}

}

public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {

try {

MappedStatement ms = configuration.getMappedStatement(statement);

return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);

} catch (Exception e) {

throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);

} finally {

ErrorContext.instance().reset();

}

}

執(zhí)行器在query操作中,優(yōu)先會(huì)查詢緩存是否命中,命中則直接返回,否則從數(shù)據(jù)庫(kù)中查詢。

// CachingExecutor類

public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {

/* 獲取關(guān)聯(lián)參數(shù)的sql,boundSql */

BoundSql boundSql = ms.getBoundSql(parameterObject);

/* 創(chuàng)建cache key值 */

CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);

return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);

}

public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)

throws SQLException {

/* 獲取二級(jí)緩存實(shí)例 */

Cache cache = ms.getCache();

if (cache != null) {

flushCacheIfRequired(ms);

if (ms.isUseCache() && resultHandler == null) {

ensureNoOutParams(ms, parameterObject, boundSql);

@SuppressWarnings("unchecked")

List<E> list = (List<E>) tcm.getObject(cache, key);

if (list == null) {

list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);

tcm.putObject(cache, key, list); // issue #578 and #116

}

return list;

}

}

return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);

}

private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {

List<E> list;

/**

* 先往localCache中插入一個(gè)占位對(duì)象,這個(gè)地方

*/

localCache.putObject(key, EXECUTION_PLACEHOLDER);

try {

list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);

} finally {

localCache.removeObject(key);

}

/* 往緩存中寫(xiě)入數(shù)據(jù),也就是緩存查詢結(jié)果 */

localCache.putObject(key, list);

if (ms.getStatementType() == StatementType.CALLABLE) {

localOutputParameterCache.putObject(key, parameter);

}

return list;

}

真正的doQuery操作是由SimplyExecutor代理來(lái)完成的,該方法中有2個(gè)子流程,一個(gè)是SQL參數(shù)的設(shè)置,另一個(gè)是SQL查詢操作和結(jié)果集的封裝。

// SimpleExecutor類

public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {

Statement stmt = null;

try {

Configuration configuration = ms.getConfiguration();

StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);

/* 子流程1: SQL查詢參數(shù)的設(shè)置 */

stmt = prepareStatement(handler, ms.getStatementLog());

/* 子流程2: SQL查詢操作和結(jié)果集封裝 */

return handler.<E>query(stmt, resultHandler);

} finally {

closeStatement(stmt);

}

}

子流程1 SQL查詢參數(shù)的設(shè)置:

首先獲取數(shù)據(jù)庫(kù)connection連接,然后準(zhǔn)備statement,然后就設(shè)置SQL查詢中的參數(shù)值。打開(kāi)一個(gè)connection連接,在使用完后不會(huì)close,而是存儲(chǔ)下來(lái),當(dāng)下次需要打開(kāi)連接時(shí)就直接返回。

// SimpleExecutor類

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {

Statement stmt;

/* 獲取Connection連接 */

Connection connection = getConnection(statementLog);

/* 準(zhǔn)備Statement */

stmt = handler.prepare(connection, transaction.getTimeout());

/* 設(shè)置SQL查詢中的參數(shù)值 */

handler.parameterize(stmt);

return stmt;

}

// DefaultParameterHandler類

public void setParameters(PreparedStatement ps) {

/**

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

*/

ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());

List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();

if (parameterMappings != null) {

for (int i = 0; i < parameterMappings.size(); i++) {

ParameterMapping parameterMapping = parameterMappings.get(i);

if (parameterMapping.getMode() != ParameterMode.OUT) {

Object value;

String propertyName = parameterMapping.getProperty();

if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params

value = boundSql.getAdditionalParameter(propertyName);

} else if (parameterObject == null) {

value = null;

} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {

value = parameterObject;

} else {

MetaObject metaObject = configuration.newMetaObject(parameterObject);

value = metaObject.getValue(propertyName);

}

TypeHandler typeHandler = parameterMapping.getTypeHandler();

JdbcType jdbcType = parameterMapping.getJdbcType();

if (value == null && jdbcType == null) {

jdbcType = configuration.getJdbcTypeForNull();

}

try {

typeHandler.setParameter(ps, i + 1, value, jdbcType);

} catch (TypeException e) {

throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);

} catch (SQLException e) {

throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);

}

}

}

}

}

子流程2 SQL查詢結(jié)果集的封裝:

// SimpleExecutor類

public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {

PreparedStatement ps = (PreparedStatement) statement;

// 執(zhí)行查詢操作

ps.execute();

// 執(zhí)行結(jié)果集封裝

return resultSetHandler.<E> handleResultSets(ps);

}

// DefaultReseltSetHandler類

public List<Object> handleResultSets(Statement stmt) throws SQLException {

ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

final List<Object> multipleResults = new ArrayList<Object>();

int resultSetCount = 0;

/**

* 獲取第一個(gè)ResultSet,同時(shí)獲取數(shù)據(jù)庫(kù)的MetaData數(shù)據(jù),包括數(shù)據(jù)表列名、列的類型、類序號(hào)等。

* 這些信息都存儲(chǔ)在了ResultSetWrapper中了

*/

ResultSetWrapper rsw = getFirstResultSet(stmt);

List<ResultMap> resultMaps = mappedStatement.getResultMaps();

int resultMapCount = resultMaps.size();

validateResultMapsCount(rsw, resultMapCount);

while (rsw != null && resultMapCount > resultSetCount) {

ResultMap resultMap = resultMaps.get(resultSetCount);

handleResultSet(rsw, resultMap, multipleResults, null);

rsw = getNextResultSet(stmt);

cleanUpAfterHandlingResultSet();

resultSetCount++;

}

String[] resultSets = mappedStatement.getResultSets();

if (resultSets != null) {

while (rsw != null && resultSetCount < resultSets.length) {

ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);

if (parentMapping != null) {

String nestedResultMapId = parentMapping.getNestedResultMapId();

ResultMap resultMap = configuration.getResultMap(nestedResultMapId);

handleResultSet(rsw, resultMap, null, parentMapping);

}

rsw = getNextResultSet(stmt);

cleanUpAfterHandlingResultSet();

resultSetCount++;

}

}

return collapseSingleResultList(multipleResults);

}

ResultSetWrapper是ResultSet的包裝類,調(diào)用getFirstResultSet方法獲取第一個(gè)ResultSet,同時(shí)獲取數(shù)據(jù)庫(kù)的MetaData數(shù)據(jù),包括數(shù)據(jù)表列名、列的類型、類序號(hào)等,這些信息都存儲(chǔ)在ResultSetWrapper類中了。然后調(diào)用handleResultSet方法來(lái)來(lái)進(jìn)行結(jié)果集的封裝。

// DefaultResultSetHandler類

private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {

try {

if (parentMapping != null) {

handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);

} else {

if (resultHandler == null) {

DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);

handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);

multipleResults.add(defaultResultHandler.getResultList());

} else {

handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);

}

}

} finally {

// issue #228 (close resultsets)

closeResultSet(rsw.getResultSet());

}

}

這里調(diào)用handleRowValues方法進(jìn)行結(jié)果值的設(shè)置。

// DefaultResultSetHandler類

public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {

if (resultMap.hasNestedResultMaps()) {

ensureNoRowBounds();

checkResultHandler();

handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);

} else {

// 封裝數(shù)據(jù)

handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);

}

}

private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)

throws SQLException {

DefaultResultContext<Object> resultContext = new DefaultResultContext<Object>();

skipRows(rsw.getResultSet(), rowBounds);

while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {

ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);

Object rowValue = getRowValue(rsw, discriminatedResultMap);

storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());

}

}

private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {

final ResultLoaderMap lazyLoader = new ResultLoaderMap();

// createResultObject為新創(chuàng)建的對(duì)象,數(shù)據(jù)表對(duì)應(yīng)的類

Object rowValue = createResultObject(rsw, resultMap, lazyLoader, null);

if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {

final MetaObject metaObject = configuration.newMetaObject(rowValue);

boolean foundValues = this.useConstructorMappings;

if (shouldApplyAutomaticMappings(resultMap, false)) {

// 這里把數(shù)據(jù)填充進(jìn)去,metaObject中包含了resultObject信息

foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;

}

foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;

foundValues = lazyLoader.size() > 0 || foundValues;

rowValue = (foundValues || configuration.isReturnInstanceForEmptyRow()) ? rowValue : null;

}

return rowValue;

}

private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {

List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);

boolean foundValues = false;

if (autoMapping.size() > 0) {

// 這里進(jìn)行for循環(huán)調(diào)用,因?yàn)閡ser表中總共有7列,所以也就調(diào)用7次

for (UnMappedColumnAutoMapping mapping : autoMapping) {

// 這里將esultSet中查詢結(jié)果轉(zhuǎn)換為對(duì)應(yīng)的實(shí)際類型

final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);

if (value != null) {

foundValues = true;

}

if (value != null || (configuration.isCallSettersOnNulls() && !mapping.primitive)) {

// gcode issue #377, call setter on nulls (value is not 'found')

metaObject.setValue(mapping.property, value);

}

}

}

return foundValues;

}

mapping.typeHandler.getResult會(huì)獲取查詢結(jié)果值的實(shí)際類型,比如我們user表中id字段為int類型,那么它就對(duì)應(yīng)Java中的Integer類型,然后通過(guò)調(diào)用statement.getInt("id")來(lái)獲取其int值,其類型為Integer。metaObject.setValue方法會(huì)把獲取到的Integer值設(shè)置到Java類中的對(duì)應(yīng)字段。

// MetaObject類

public void setValue(String name, Object value) {

PropertyTokenizer prop = new PropertyTokenizer(name);

if (prop.hasNext()) {

MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());

if (metaValue == SystemMetaObject.NULL_META_OBJECT) {

if (value == null && prop.getChildren() != null) {

// don't instantiate child path if value is null

return;

} else {

metaValue = objectWrapper.instantiatePropertyValue(name, prop, objectFactory);

}

}

metaValue.setValue(prop.getChildren(), value);

} else {

objectWrapper.set(prop, value);

}

}

metaValue.setValue方法最后會(huì)調(diào)用到Java類中對(duì)應(yīng)數(shù)據(jù)域的set方法,這樣也就完成了SQL查詢結(jié)果集的Java類封裝過(guò)程。最后貼一張調(diào)用棧到達(dá)Java類的set方法中的快照:


MyBatis緩存

MyBatis提供查詢緩存,用于減輕數(shù)據(jù)庫(kù)壓力,提高性能。MyBatis提供了一級(jí)緩存和二級(jí)緩存。


一級(jí)緩存是SqlSession級(jí)別的緩存,每個(gè)SqlSession對(duì)象都有一個(gè)哈希表用于緩存數(shù)據(jù),不同SqlSession對(duì)象之間緩存不共享。同一個(gè)SqlSession對(duì)象對(duì)象執(zhí)行2遍相同的SQL查詢,在第一次查詢執(zhí)行完畢后將結(jié)果緩存起來(lái),這樣第二遍查詢就不用向數(shù)據(jù)庫(kù)查詢了,直接返回緩存結(jié)果即可。MyBatis默認(rèn)是開(kāi)啟一級(jí)緩存的。

二級(jí)緩存是mapper級(jí)別的緩存,二級(jí)緩存是跨SqlSession的,多個(gè)SqlSession對(duì)象可以共享同一個(gè)二級(jí)緩存。不同的SqlSession對(duì)象執(zhí)行兩次相同的SQL語(yǔ)句,第一次會(huì)將查詢結(jié)果進(jìn)行緩存,第二次查詢直接返回二級(jí)緩存中的結(jié)果即可。MyBatis默認(rèn)是不開(kāi)啟二級(jí)緩存的,可以在配置文件中使用如下配置來(lái)開(kāi)啟二級(jí)緩存:

<settings>

<setting name="cacheEnabled" value="true"/>

</settings>

當(dāng)SQL語(yǔ)句進(jìn)行更新操作(刪除/添加/更新)時(shí),會(huì)清空對(duì)應(yīng)的緩存,保證緩存中存儲(chǔ)的都是最新的數(shù)據(jù)。MyBatis的二級(jí)緩存對(duì)細(xì)粒度的數(shù)據(jù)級(jí)別的緩存實(shí)現(xiàn)不友好。

比如如下需求:對(duì)商品信息進(jìn)行緩存,由于商品信息查詢?cè)L問(wèn)量大,但是要求用戶每次都能查詢最新的商品信息,此時(shí)如果使用mybatis的二級(jí)緩存就無(wú)法實(shí)現(xiàn)當(dāng)一個(gè)商品變化時(shí)只刷新該商品的緩存信息而不刷新其它商品的信息,因?yàn)閙ybaits的二級(jí)緩存區(qū)域以mapper為單位劃分,當(dāng)一個(gè)商品信息變化會(huì)將所有商品信息的緩存數(shù)據(jù)全部清空。

解決此類問(wèn)題需要在業(yè)務(wù)層根據(jù)需求對(duì)數(shù)據(jù)有針對(duì)性緩存,具體業(yè)務(wù)具體實(shí)現(xiàn)。

歡迎工作一到八年的Java工程師朋友們加入Java高級(jí)交流:787707172

本群提供免費(fèi)的學(xué)習(xí)指導(dǎo) 架構(gòu)資料 以及免費(fèi)的解答

不懂得問(wèn)題都可以在本群提出來(lái) 之后還會(huì)有直播平臺(tái)和講師直接交流噢

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • MyBatis 是支持定制化 SQL、存儲(chǔ)過(guò)程以及高級(jí)映射的優(yōu)秀的持久層框架,其主要就完成2件事情: 封裝JDBC...
    慕容小偉閱讀 1,078評(píng)論 0 2
  • 簡(jiǎn)介 MyBatis是一個(gè)輕量級(jí)的ORM框架,它簡(jiǎn)化了對(duì)關(guān)系數(shù)據(jù)庫(kù)的使用,開(kāi)發(fā)人員可以在XML或注解中編寫(xiě)SQL來(lái)...
    敲程序的人生閱讀 709評(píng)論 0 0
  • 前言 主題是Mybatis一級(jí)和二級(jí)緩存的應(yīng)用及源碼分析。希望在本場(chǎng)chat結(jié)束后,能夠幫助讀者朋友明白以下三點(diǎn)。...
    余平的余_余平的平閱讀 1,355評(píng)論 0 12
  • 1. 簡(jiǎn)介 1.1 什么是 MyBatis ? MyBatis 是支持定制化 SQL、存儲(chǔ)過(guò)程以及高級(jí)映射的優(yōu)秀的...
    笨鳥(niǎo)慢飛閱讀 5,622評(píng)論 0 4
  • pyspark.sql模塊 模塊上下文 Spark SQL和DataFrames的重要類: pyspark.sql...
    mpro閱讀 9,504評(píng)論 0 13