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)和講師直接交流噢