您现在的位置是:主页 > news > 网站设计行业资讯/今天的国际新闻
网站设计行业资讯/今天的国际新闻
admin2025/5/14 12:37:35【news】
简介网站设计行业资讯,今天的国际新闻,网站建设讯美,印刷厂网站源码第⼗部分:Mybatis源码剖析 10.1传统⽅式源码剖析: 源码剖析-初始化 //读取配置文件,读成字节输入流的,注意:现在还没解析 InputStream resourceAsStream Resources.getResourceAsStream("sqlMapConfig.xml&qu…
第⼗部分:Mybatis源码剖析
10.1传统⽅式源码剖析:
//读取配置文件,读成字节输入流的,注意:现在还没解析 InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml"); //这才是一切工作的开始 //2.解析配置文件,封装成Configuration对象,创建DefaultSqlSessionFactory对象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream); //3.生产了DefaultSqlSession实例对象,设置了事务不自动提交,完成了executor对象的创建 SqlSession sqlSession = sqlSessionFactory.openSession();
//(1)根据statementid来Configuration中map集合中获取到了指定的MappedStatement对象
//(2)将查询操作委派给Executor
List<Object>objects=sqlSession.selectList("namespace.id");
进⼊源码分析:
// 1.我们最初调用的build public SqlSessionFactory build(InputStream inputStream) {//调用了重载方法return build(inputStream, null, null); }
// 2.调用的重载方法 public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {try {// 创建 XMLConfigBuilder, XMLConfigBuilder是专门解析mybatis的配置文件的类XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);// 执行 XML 解析// 创建 DefaultSqlSessionFactory 对象return build(parser.parse());} 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.}} }
Configuration对象的结构和xml配置⽂件的对象⼏乎相同。回顾⼀下xml中的配置标签有哪些:properties (属性),settings (设置),typeAliases (类型别名),typeHandlers (类型处理器),objectFactory (对象⼯⼚),mappers (映射器)等 Configuration也有对应的对象属性来封装它们也就是说,初始化配置⽂件信息的本质就是创建Configuration对象,将解析的xml数据封装到Configuration内部属性中
/*** 解析 XML 成 Configuration 对象。*/public Configuration parse () {//若已解析,抛出BuilderException异常if (parsed) {throw new BuilderException("Each XMLConfigBuilder can only beused once.");}//标记已解析parsed = true;// 解析 XML configuration 节点parseConfiguration(parser.evalNode("/configuration")); return configuration;}/***解析XML*/private void parseConfiguration (XNode root){try {//issue #117 read properties first// 解析 <properties /> 标签propertiesElement(root.evalNode("properties"));// 解析〈settings /> 标签Properties settings =settingsAsProperties(root.evalNode("settings"));//加载⾃定义的VFS实现类loadCustomVfs(settings);// 解析 <typeAliases /> 标签typeAliasesElement(root.evalNode("typeAliases"));//解析<plugins />标签pluginElement(root.evalNode("plugins"));// 解析 <objectFactory /> 标签objectFactoryElement(root.evalNode("objectFactory"));// 解析 <objectWrapperFactory /> 标签objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));// 解析 <reflectorFactory /> 标签reflectorFactoryElement(root.evalNode("reflectorFactory"));// 赋值 <settings /> ⾄ Configuration 属性settingsElement(settings);// read it after objectFactory and objectWrapperFactory issue#631// 解析〈environments /> 标签environmentsElement(root.evalNode("environments"));// 解析 <databaseIdProvider /> 标签databaseldProviderElement(root.evalNode("databaseldProvider"));// 解析 <typeHandlers /> 标签typeHandlerElement(root.evalNode("typeHandlers"));//解析<mappers />标签mapperElement(root.evalNode("mappers"));} catch (Exception e) {throw new BuilderException("Error parsing SQL MapperConfiguration.Cause:" + e, e);}}
<select id="getUser" resultType="user" >select * from user where id=#{id}</select>
样的⼀个select标签会在初始化配置⽂件时被解析封装成⼀个MappedStatement对象,然后存储在
Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection")
在 XMLConfigBuilder 中的处理:
private void parseConfiguration(XNode root) {try {//省略其他标签的处理mapperElement(root.evalNode("mappers"));} catch (Exception e) {throw new BuilderException("Error parsing SQL MapperConfiguration.Cause:" + e, e);}}
到此对xml配置⽂件的解析就结束了,回到步骤2.中调⽤的重载build⽅法
// 5.调⽤的重载⽅法public SqlSessionFactory build(Configuration config) {//创建了 DefaultSqlSessionFactory 对象,传⼊ Configuration 对象。return new DefaultSqlSessionFactory(config);}
public class DefaultSqlSession implements SqlSession {private final Configuration configuration;private final Executor executor;
SqlSession sqlSession = factory.openSession();List<User> list =sqlSession.selectList("com.lagou.mapper.UserMapper.getUserByName");
获得 sqlSession
//6. 进⼊ o penSession ⽅法。public SqlSession openSession() {//getDefaultExecutorType()传递的是SimpleExecutorreturnopenSessionFromDataSource(configuration.getDefaultExecutorType(), null,false);}//7. 进⼊penSessionFromDataSource。//ExecutorType 为Executor的类型,TransactionIsolationLevel为事务隔离级别,autoCommit是否开启事务//openSession的多个重载⽅法可以指定获得的SeqSession的Executor类型和事务的处理private SqlSession openSessionFromDataSource(ExecutorType execType,TransactionIsolationLevel level, boolean autoCommit) {Transaction tx = null;try{final Environment environment = configuration.getEnvironment();final TransactionFactory transactionFactory =getTransactionFactoryFromEnvironment(environment);tx = transactionFactory.newTransaction(environment.getDataSource(),level, autoCommit);//根据参数创建指定类型的Executorfinal Executor executor = configuration.newExecutor(tx, execType);//返回的是 DefaultSqlSessionreturn new DefaultSqlSession(configuration, executor, autoCommit);} catch(Exception e){closeTransaction(tx); // may have fetched a connection so lets callclose()}
//8.进⼊selectList⽅法,多个重载⽅法。public <E > List < E > selectList(String statement) {return this.selectList(statement, null);public <E > List < E > selectList(String statement, Object parameter){return this.selectList(statement, parameter, RowBounds.DEFAULT);public <E > List < E > selectList(String statement, Objectparameter, RowBounds rowBounds) {try {//根据传⼊的全限定名+⽅法名从映射的Map中取出MappedStatement对象MappedStatement ms =configuration.getMappedStatement(statement);//调⽤Executor中的⽅法处理//RowBounds是⽤来逻辑分⻚// wrapCollection(parameter)是⽤来装饰集合或者数组参数return executor.query(ms, wrapCollection(parameter),rowBounds, Executor.NO_RESULT_HANDLER);} catch (Exception e) {throw ExceptionFactory.wrapException("Error queryingdatabase. Cause: + e, e);} finally {ErrorContext.instance().reset();}
源码剖析-executor
//此⽅法在SimpleExecutor的⽗类BaseExecutor中实现public <E> List<E> query(MappedStatement ms, Object parameter, RowBoundsrowBounds, ResultHandler resultHandler) throws SQLException {//根据传⼊的参数动态获得SQL语句,最后返回⽤BoundSql对象表示BoundSql boundSql = ms.getBoundSql(parameter);//为本次查询创建缓存的KeyCacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);return query(ms, parameter, rowBounds, resultHandler, key, boundSql);}//进⼊query的重载⽅法中 public <E> List<E> query(MappedStatement ms, Object parameter, RowBoundsrowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)throws SQLException {ErrorContext.instance().resource(ms.getResource()).activity("executinga query").object(ms.getId());if (closed) {throw new ExecutorException("Executor was closed.");}if (queryStack == 0 && ms.isFlushCacheRequired()) {clearLocalCache();}List<E> list;try {queryStack++;list = resultHandler == null ? (List<E>) localCache.getObject(key): null;if (list != null) {handleLocallyCachedOutputParameters(ms, key, parameter,boundSql);} else {//如果缓存中没有本次查找的值,那么从数据库中查询list = queryFromDatabase(ms, parameter, rowBounds,resultHandler, key, boundSql);}} finally {queryStack--;}if (queryStack == 0) {for (DeferredLoad deferredLoad : deferredLoads) {deferredLoad.load();}// issue #601deferredLoads.clear();if (configuration.getLocalCacheScope() ==LocalCacheScope.STATEMENT) { // issue #482 clearLocalCache();}}return list;}//从数据库查询private <E> List<E> queryFromDatabase(MappedStatement ms, Objectparameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key,BoundSql boundSql) throws SQLException {List<E> list;localCache.putObject(key, EXECUTION_PLACEHOLDER);try {//查询的⽅法list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql); } finally {localCache.removeObject(key);}//将查询结果放⼊缓存localCache.putObject(key, list);if (ms.getStatementType() == StatementType.CALLABLE) {localOutputParameterCache.putObject(key, parameter);}return list;}// SimpleExecutor中实现⽗类的doQuery抽象⽅法public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBoundsrowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException{Statement stmt = null;try {Configuration configuration = ms.getConfiguration();//传⼊参数创建StatementHanlder对象来执⾏查询StatementHandler handler =configuration.newStatementHandler(wrapper, ms, parameter, rowBounds,resultHandler, boundSql);//创建jdbc中的statement对象stmt = prepareStatement(handler, ms.getStatementLog());// StatementHandler 进⾏处理return handler.query(stmt, resultHandler);} finally {closeStatement(stmt);}}//创建Statement的⽅法private Statement prepareStatement(StatementHandler handler, LogstatementLog) throws SQLException {Statement stmt;//条代码中的getConnection⽅法经过重重调⽤最后会调⽤openConnection⽅法,从连接池中获 得连接。Connection connection = getConnection(statementLog);stmt = handler.prepare(connection, transaction.getTimeout());handler.parameterize(stmt);return stmt;}//从连接池获得连接的⽅法protected void openConnection() throws SQLException {if (log.isDebugEnabled()) {log.debug("Opening JDBC Connection");}connection = dataSource.getConnection();if (level != null) {connection.setTransactionIsolation(level.getLevel());}}
(1、根据传递的参数,完成SQL语句的动态解析,⽣成BoundSql对象,供StatementHandler使⽤;(2、为查询创建缓存,以提⾼性能(3、创建JDBC的Statement连接对象,传递给*StatementHandler*对象,返回List查询结果。
public void parameterize(Statement statement) throws SQLException {//使⽤ParameterHandler对象来完成对Statement的设值parameterHandler.setParameters((PreparedStatement) statement);}
/** ParameterHandler 类的 setParameters(PreparedStatement ps) 实现* 对某⼀个Statement进⾏设置参数* */public void setParameters(PreparedStatement ps) throws SQLException {ErrorContext.instance().activity("settingparameters").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 #448ask first for additional paramsvalue = 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); }// 每⼀个 Mapping都有⼀个 TypeHandler,根据 TypeHandler 来对preparedStatement 进 ⾏设置参数TypeHandler typeHandler = parameterMapping.getTypeHandler();JdbcType jdbcType = parameterMapping.getJdbcType();if (value == null && jdbcType == null) jdbcType =configuration.getJdbcTypeForNull();//设置参数typeHandler.setParameter(ps, i + 1, value, jdbcType);}}}}
public <E> List<E> query(Statement statement, ResultHandler resultHandler)throws SQLException {// 1.调⽤preparedStatemnt。execute()⽅法,然后将resultSet交给ResultSetHandler处理PreparedStatement ps = (PreparedStatement) statement;ps.execute();//2.使⽤ ResultHandler 来处理 ResultSetreturn resultSetHandler.<E> handleResultSets(ps);}
从上述代码我们可以看出,StatementHandler 的List query(Statement statement, ResultHandler resultHandler)⽅法的实现,是调⽤了 ResultSetHandler 的 handleResultSets(Statement)⽅法。ResultSetHandler 的 handleResultSets(Statement)⽅法会将 Statement 语句执⾏后⽣成的 resultSet
public List<Object> handleResultSets(Statement stmt) throws SQLException {ErrorContext.instance().activity("handlingresults").object(mappedStatement.getId());//多ResultSet的结果集合,每个ResultSet对应⼀个Object对象。⽽实际上,每 个 Object 是List<Object> 对象。//在不考虑存储过程的多ResultSet的情况,普通的查询,实际就⼀个ResultSet,也 就是说,multipleResults最多就⼀个元素。final List<Object> multipleResults = new ArrayList<>();int resultSetCount = 0;//获得⾸个ResultSet对象,并封装成ResultSetWrapper对象ResultSetWrapper rsw = getFirstResultSet(stmt);//获得ResultMap数组//在不考虑存储过程的多ResultSet的情况,普通的查询,实际就⼀个ResultSet,也 就是说,resultMaps就⼀个元素。List<ResultMap> resultMaps = mappedStatement.getResultMaps();int resultMapCount = resultMaps.size();validateResultMapsCount(rsw, resultMapCount); // 校验while (rsw != null && resultMapCount > resultSetCount) {//获得ResultMap对象ResultMap resultMap = resultMaps.get(resultSetCount);//处理ResultSet,将结果添加到multipleResults中handleResultSet(rsw, resultMap, multipleResults, null);//获得下⼀个ResultSet对象,并封装成ResultSetWrapper对象rsw = getNextResultSet(stmt);//清理cleanUpAfterHandlingResultSet();// resultSetCount ++resultSetCount++;}}//因为'mappedStatement.resultSets'只在存储过程中使⽤,本系列暂时不考虑,忽略即可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++;}}//如果是multipleResults单元素,则取⾸元素返回return collapseSingleResultList(multipleResults);}
10.2 Mapper代理⽅式:
public static void main(String[] args) {//前三步都相同InputStream inputStream =Resources.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactory factory = newSqlSessionFactoryBuilder().build(inputStream);SqlSession sqlSession = factory.openSession();//这⾥不再调⽤SqlSession的api,⽽是获得了接⼝对象,调⽤接⼝中的⽅法。UserMapper mapper = sqlSession.getMapper(UserMapper.class); //使用jdk动态代理对mapper接口产生代理对象//代理对象调用接口中的任意方法,执行的都是动态代理中的invoke方法List<User> list = mapper.getUserByName("tom");}
思考⼀个问题,通常的Mapper接⼝我们都没有实现的⽅法却可以使⽤,是为什么呢?答案很简单动态 代理
<mappers><mapper class="com.lagou.mapper.UserMapper"/><package name="com.lagou.mapper"/></mappers>
进⼊ sqlSession.getMapper(UserMapper.class )中
//DefaultSqlSession 中的 getMapperpublic <T> T getMapper(Class<T> type) {return configuration.<T>getMapper(type, this);}//configuration 中的给 g etMapperpublic <T> T getMapper(Class<T> type, SqlSession sqlSession) {return mapperRegistry.getMapper(type, sqlSession);}//MapperRegistry 中的 g etMapperpublic <T> T getMapper(Class<T> type, SqlSession sqlSession) {//从 MapperRegistry 中的 HashMap 中拿 MapperProxyFactoryfinal MapperProxyFactory<T> mapperProxyFactory =(MapperProxyFactory<T>) knownMappers.get(type);if (mapperProxyFactory == null) {throw new BindingException("Type " + type + " is not known to theMapperRegistry.");}try {//通过动态代理⼯⼚⽣成示例。return mapperProxyFactory.newInstance(sqlSession);} catch (Exception e) {throw new BindingException("Error getting mapper instance. Cause:" + e, e);}}//MapperProxyFactory 类中的 newInstance ⽅法public T newInstance(SqlSession sqlSession) {//创建了 JDK动态代理的Handler类final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession,mapperInterface, methodCache);//调⽤了重载⽅法return newInstance(mapperProxy);}//MapperProxy 类,实现了 InvocationHandler 接⼝public class MapperProxy<T> implements InvocationHandler, Serializable {//省略部分源码private final SqlSession sqlSession;private final Class<T> mapperInterface;private final Map<Method, MapperMethod> methodCache;//构造,传⼊了 SqlSession,说明每个session中的代理对象的不同的!public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface,Map<Method, MapperMethod> methodCache) {this.sqlSession = sqlSession;this.mapperInterface = mapperInterface;this.methodCache = methodCache;}//省略部分源码
源码剖析-invoke()
public Object invoke(Object proxy, Method method, Object[] args) throwsThrowable {try {//如果是Object定义的⽅法,直接调⽤if (Object.class.equals(method.getDeclaringClass())) {return method.invoke(this, args);} else if (isDefaultMethod(method)) {return invokeDefaultMethod(proxy, method, args);}} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}// 获得 MapperMethod 对象final MapperMethod mapperMethod = cachedMapperMethod(method);//重点在这:MapperMethod最终调⽤了执⾏的⽅法return mapperMethod.execute(sqlSession, args);}
进⼊execute⽅法:
public Object execute(SqlSession sqlSession, Object[] args) {Object result;//判断mapper中的⽅法类型,最终调⽤的还是SqlSession中的⽅法 switch(command.getType()) {case INSERT: {//转换参数Object param = method.convertArgsToSqlCommandParam(args);//执⾏INSERT操作// 转换 rowCountresult = rowCountResult(sqlSession.insert(command.getName(),param));break;}case UPDATE: {//转换参数 Object param = method.convertArgsToSqlCommandParam(args);// 转换 rowCountresult = rowCountResult(sqlSession.update(command.getName(),param));break;}case DELETE: {//转换参数Object param = method.convertArgsToSqlCommandParam(args);// 转换 rowCountresult = rowCountResult(sqlSession.delete(command.getName(),param));break;}case SELECT://⽆返回,并且有ResultHandler⽅法参数,则将查询的结果,提交给 ResultHandler 进⾏处理if (method.returnsVoid() && method.hasResultHandler()) {executeWithResultHandler(sqlSession, args);result = null;//执⾏查询,返回列表} else if (method.returnsMany()) {result = executeForMany(sqlSession, args);//执⾏查询,返回Map} else if (method.returnsMap()) {result = executeForMap(sqlSession, args);//执⾏查询,返回Cursor} else if (method.returnsCursor()) {result = executeForCursor(sqlSession, args);//执⾏查询,返回单个对象} else {//转换参数Object param = method.convertArgsToSqlCommandParam(args);//查询单条result = sqlSession.selectOne(command.getName(), param);if (method.returnsOptional() &&(result == null ||!method.getReturnType().equals(result.getClass()))) {result = Optional.ofNullable(result);}}break;case FLUSH:result = sqlSession.flushStatements();break;default:throw new BindingException("Unknown execution method for: " +command.getName());}//返回结果为null,并且返回类型为基本类型,则抛出BindingException异常if(result ==null&&method.getReturnType().isPrimitive()&&!method.returnsVoid()){throw new BindingException("Mapper method '" + command.getName() + "attempted to return null from a method with a primitivereturn type(" + method.getReturnType() + "). ");}//返回结果return result;
10.3 ⼆级缓存源码剖析:
<settings><setting name="cacheEnabled" value="true"/></settings>
<cache></cache>
<select id="findById" resultType="com.lagou.pojo.User" useCache="true">select * from user where id = #{id}</select>
// XMLConfigBuilder.parse()public Configuration parse() {if (parsed) {throw new BuilderException("Each XMLConfigBuilder can only be usedonce.");}parsed = true;parseConfiguration(parser.evalNode("/configuration"));// 在这⾥return configuration;}// parseConfiguration()// 既然是在xml中添加的,那么我们就直接看关于mappers标签的解析private void parseConfiguration(XNode root) {try {Properties settings =settingsAsPropertiess(root.evalNode("settings"));propertiesElement(root.evalNode("properties"));loadCustomVfs(settings);typeAliasesElement(root.evalNode("typeAliases"));pluginElement(root.evalNode("plugins"));objectFactoryElement(root.evalNode("objectFactory"));objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));reflectionFactoryElement(root.evalNode("reflectionFactory"));settingsElement(settings);// read it after objectFactory and objectWrapperFactory issue #631environmentsElement(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);}}// mapperElement()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判断if (resource != null && url == null && mapperClass == null) {ErrorContext.instance().resource(resource);InputStream inputStream =Resources.getResourceAsStream(resource);XMLMapperBuilder mapperParser = newXMLMapperBuilder(inputStream, configuration, resource,configuration.getSqlFragments());// ⽣成XMLMapperBuilder,并执⾏其parse⽅法mapperParser.parse();} else if (resource == null && url != null && mapperClass ==null) {ErrorContext.instance().resource(url);InputStream inputStream = Resources.getUrlAsStream(url);XMLMapperBuilder mapperParser = newXMLMapperBuilder(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 onlyspecify a url, resource or class, but not more than one.");}}}}}
// XMLMapperBuilder.parse()public void parse() {if (!configuration.isResourceLoaded(resource)) {// 解析mapper属性configurationElement(parser.evalNode("/mapper"));configuration.addLoadedResource(resource);bindMapperForNamespace();}parsePendingResultMaps();parsePendingChacheRefs();parsePendingStatements();}// configurationElement()private void configurationElement(XNode context) {try {String namespace = context.getStringAttribute("namespace");if (namespace == null || namespace.equals("")) {throw new BuilderException("Mapper's namespace cannot be empty");}builderAssistant.setCurrentNamespace(namespace);cacheRefElement(context.evalNode("cache-ref"));// 最终在这⾥看到了关于cache属性的处理cacheElement(context.evalNode("cache"));parameterMapElement(context.evalNodes("/mapper/parameterMap"));resultMapElements(context.evalNodes("/mapper/resultMap"));sqlElement(context.evalNodes("/mapper/sql"));// 这⾥会将⽣成的Cache包装到对应的MappedStatementbuildStatementFromContext(context.evalNodes("select|insert|update|delete"));} catch (Exception e) {throw new BuilderException("Error parsing Mapper XML. Cause: " + e,e);}}// cacheElement()private void cacheElement(XNode context) throws Exception {if (context != null) {//解析<cache/>标签的type属性,这⾥我们可以⾃定义cache的实现类,⽐如redisCache,如果没有⾃定义,这⾥使⽤和⼀级缓存相同的PERPETUALString type = context.getStringAttribute("type", "PERPETUAL");Class<? extends Cache> typeClass =typeAliasRegistry.resolveAlias(type);String eviction = context.getStringAttribute("eviction", "LRU");Class<? extends Cache> evictionClass =typeAliasRegistry.resolveAlias(eviction);Long flushInterval = context.getLongAttribute("flushInterval");Integer size = context.getIntAttribute("size");boolean readWrite = !context.getBooleanAttribute("readOnly", false);boolean blocking = context.getBooleanAttribute("blocking", false);Properties props = context.getChildrenAsProperties();// 构建Cache对象builderAssistant.useNewCache(typeClass, evictionClass, flushInterval,size, readWrite, blocking, props);}}
先来看看是如何构建Cache对象的
public Cache useNewCache(Class<? extends Cache> typeClass, Class<? extends Cache> evictionClass,Long flushInterval,Integer size,boolean readWrite,boolean blocking,Properties props) {// 1.⽣成Cache对象Cache cache = new CacheBuilder(currentNamespace)//这⾥如果我们定义了<cache/>中的type,就使⽤⾃定义的Cache,否则使⽤和⼀级缓存相同的PerpetualCache.implementation(valueOrDefault(typeClass, PerpetualCache.class)).addDecorator(valueOrDefault(evictionClass, LruCache.class)).clearInterval(flushInterval).size(size).readWrite(readWrite).blocking(blocking).properties(props).build();// 2.添加到Configuration中configuration.addCache(cache);// 3.并将cache赋值给MapperBuilderAssistant.currentCachecurrentCache = cache;return cache;}
// buildStatementFromContext()private void buildStatementFromContext(List<XNode> list) {if (configuration.getDatabaseId() != null) {buildStatementFromContext(list, configuration.getDatabaseId());}buildStatementFromContext(list, null);}//buildStatementFromContext()private void buildStatementFromContext(List<XNode> list, StringrequiredDatabaseId) {for (XNode context : list) {final XMLStatementBuilder statementParser = newXMLStatementBuilder(configuration, builderAssistant, context,requiredDatabaseId);try {// 每⼀条执⾏语句转换成⼀个MappedStatementstatementParser.parseStatementNode(); } catch (IncompleteElementException e) {configuration.addIncompleteStatement(statementParser);}}}// XMLStatementBuilder.parseStatementNode();public void parseStatementNode() {String id = context.getStringAttribute("id");String databaseId = context.getStringAttribute("databaseId");...Integer fetchSize = context.getIntAttribute("fetchSize");Integer timeout = context.getIntAttribute("timeout");String parameterMap = context.getStringAttribute("parameterMap");String parameterType = context.getStringAttribute("parameterType");Class<?> parameterTypeClass = resolveClass(parameterType);String resultMap = context.getStringAttribute("resultMap");String resultType = context.getStringAttribute("resultType");String lang = context.getStringAttribute("lang");LanguageDriver langDriver = getLanguageDriver(lang);...// 创建MappedStatement对象builderAssistant.addMappedStatement(id, sqlSource, statementType,sqlCommandType,fetchSize, timeout, parameterMap,parameterTypeClass, resultMap, resultTypeClass,resultSetTypeEnum, flushCache,useCache, resultOrdered,keyGenerator, keyProperty, keyColumn,databaseId, langDriver, resultSets);}// builderAssistant.addMappedStatement()public MappedStatement addMappedStatement(String id,...) {if (unresolvedCacheRef) {throw new IncompleteElementException("Cache-ref not yet resolved");}id = applyCurrentNamespace(id, false);boolean isSelect = sqlCommandType == SqlCommandType.SELECT;//创建MappedStatement对象MappedStatement.Builder statementBuilder = newMappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)..flushCacheRequired(valueOrDefault(flushCache, !isSelect)).useCache(valueOrDefault(useCache, isSelect)).cache(currentCache);// 在这⾥将之前⽣成的Cache封装到MappedStatementParameterMap statementParameterMap =getStatementParameterMap(parameterMap, parameterType, id);if (statementParameterMap != null) {statementBuilder.parameterMap(statementParameterMap);}MappedStatement statement = statementBuilder.build();configuration.addMappedStatement(statement);return statement;}
// CachingExecutorpublic <E> List<E> query(MappedStatement ms, Object parameterObject, RowBoundsrowBounds, ResultHandler resultHandler) throws SQLException {BoundSql boundSql = ms.getBoundSql(parameterObject);// 创建 CacheKeyCacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);return query(ms, parameterObject, rowBounds, resultHandler, key,boundSql);}public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBoundsrowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)throws SQLException {// 从 MappedStatement 中获取 Cache,注意这⾥的 Cache 是从MappedStatement中获取的// 也就是我们上⾯解析Mapper中<cache/>标签中创建的,它保存在Configration中// 我们在上⾯解析blog.xml时分析过每⼀个MappedStatement都有⼀个Cache对象,就是这⾥Cache cache = ms.getCache();// 如果配置⽂件中没有配置 <cache>,则 cache 为空if (cache != null) {//如果需要刷新缓存的话就刷新:flushCache="true"flushCacheIfRequired(ms);if (ms.isUseCache() && resultHandler == null) {ensureNoOutParams(ms, boundSql);// 访问⼆级缓存List<E> list = (List<E>) tcm.getObject(cache, key);// 缓存未命中if (list == null) {// 如果没有值,则执⾏查询,这个查询实际也是先⾛⼀级缓存查询,⼀级缓存也没有的话,则进⾏DB查询list = delegate.<E>query(ms, parameterObject, rowBounds,resultHandler, key, boundSql);// 缓存查询结果tcm.putObject(cache, key, list);}return list;}}return delegate.<E>query(ms, parameterObject, rowBounds, resultHandler,key, boundSql);}
<!-- 执⾏此语句清空缓存 --><select id="findbyId" resultType="com.lagou.pojo.user" useCache="true"flushCache="true" >select * from t_demo</select>
/** 事务缓存管理器 */public class TransactionalCacheManager {// Cache 与 TransactionalCache 的映射关系表private final Map<Cache, TransactionalCache> transactionalCaches = newHashMap<Cache, TransactionalCache>();public void clear(Cache cache) {// 获取 TransactionalCache 对象,并调⽤该对象的 clear ⽅法,下同getTransactionalCache(cache).clear();}public Object getObject(Cache cache, CacheKey key) {// 直接从TransactionalCache中获取缓存return getTransactionalCache(cache).getObject(key);}public void putObject(Cache cache, CacheKey key, Object value) {// 直接存⼊TransactionalCache的缓存中getTransactionalCache(cache).putObject(key, value);}public void commit() {for (TransactionalCache txCache : transactionalCaches.values()) {txCache.commit();}}public void rollback() {for (TransactionalCache txCache : transactionalCaches.values()) {txCache.rollback();}}private TransactionalCache getTransactionalCache(Cache cache) {// 从映射表中获取 TransactionalCacheTransactionalCache txCache = transactionalCaches.get(cache);if (txCache == null) {// TransactionalCache 也是⼀种装饰类,为 Cache 增加事务功能// 创建⼀个新的TransactionalCache,并将真正的Cache对象存进去txCache = new TransactionalCache(cache);transactionalCaches.put(cache, txCache);}return txCache;}}
public class TransactionalCache implements Cache {//真正的缓存对象,和上⾯的Map<Cache, TransactionalCache>中的Cache是同⼀个private final Cache delegate;private boolean clearOnCommit;// 在事务被提交前,所有从数据库中查询的结果将缓存在此集合中private final Map<Object, Object> entriesToAddOnCommit;// 在事务被提交前,当缓存未命中时,CacheKey 将会被存储在此集合中private final Set<Object> entriesMissedInCache;@Overridepublic Object getObject(Object key) {// 查询的时候是直接从delegate中去查询的,也就是从真正的缓存对象中查询Object object = delegate.getObject(key); if (object == null) {// 缓存未命中,则将 key 存⼊到 entriesMissedInCache 中entriesMissedInCache.add(key);}if (clearOnCommit) {return null;} else {return object;}}@Overridepublic void putObject(Object key, Object object) {// 将键值对存⼊到 entriesToAddOnCommit 这个Map中中,⽽⾮真实的缓存对象delegate 中entriesToAddOnCommit.put(key, object);}@Overridepublic Object removeObject(Object key) {return null;}@Overridepublic void clear() {clearOnCommit = true;// 清空 entriesToAddOnCommit,但不清空 delegate 缓存entriesToAddOnCommit.clear();}public void commit() {// 根据 clearOnCommit 的值决定是否清空 delegateif (clearOnCommit) {delegate.clear();}// 刷新未缓存的结果到 delegate 缓存中flushPendingEntries();// 重置 entriesToAddOnCommit 和 entriesMissedInCachereset();}public void rollback() {unlockMissedEntries();reset();}private void reset() {clearOnCommit = false;// 清空集合entriesToAddOnCommit.clear();entriesMissedInCache.clear();}private void flushPendingEntries() {for (Map.Entry<Object, Object> entry :entriesToAddOnCommit.entrySet()) {// 将 entriesToAddOnCommit 中的内容转存到 delegate 中delegate.putObject(entry.getKey(), entry.getValue());}for (Object entry : entriesMissedInCache) {if (!entriesToAddOnCommit.containsKey(entry)) {// 存⼊空值delegate.putObject(entry, null);}}}private void unlockMissedEntries() {for (Object entry : entriesMissedInCache) {try {// 调⽤ removeObject 进⾏解锁delegate.removeObject(entry);} catch (Exception e) {log.warn("...");}}}}
@Overridepublic void commit(boolean force) {try {// 主要是这句executor.commit(isCommitOrRollbackRequired(force));dirty = false;} catch (Exception e) {throw ExceptionFactory.wrapException("Error committing transaction.Cause: " + e, e);} finally {ErrorContext.instance().reset();}}// CachingExecutor.commit()@Overridepublic void commit(boolean required) throws SQLException {delegate.commit(required);tcm.commit();// 在这⾥}// TransactionalCacheManager.commit()public void commit() {for (TransactionalCache txCache : transactionalCaches.values()) {txCache.commit();// 在这⾥}}// TransactionalCache.commit()public void commit() {if (clearOnCommit) {delegate.clear();}flushPendingEntries();//这⼀句reset();}// TransactionalCache.flushPendingEntries()private void flushPendingEntries() {for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) {// 在这⾥真正的将entriesToAddOnCommit的对象逐个添加到delegate中,只有这时,⼆级缓存才真正的⽣效delegate.putObject(entry.getKey(), entry.getValue());}for (Object entry : entriesMissedInCache) {if (!entriesToAddOnCommit.containsKey(entry)) {delegate.putObject(entry, null);}}}
public int update(String statement, Object parameter) {int var4;try {this.dirty = true;MappedStatement ms = this.configuration.getMappedStatement(statement);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;}public int update(MappedStatement ms, Object parameterObject) throwsSQLException {this.flushCacheIfRequired(ms);return this.delegate.update(ms, parameterObject);}private void flushCacheIfRequired(MappedStatement ms) {//获取MappedStatement对应的Cache,进⾏清空Cache cache = ms.getCache();//SQL需设置flushCache="true" 才会执⾏清空if (cache != null && ms.isFlushCacheRequired()) {this.tcm.clear(cache);}}
10.4 延迟加载源码剖析:
什么是延迟加载?
* 在⼀对多中,当我们有⼀个⽤户,它有个100个订单在查询⽤户的时候,要不要把关联的订单查出来?在查询订单的时候,要不要把关联的⽤户查出来?* 回答在查询⽤户时,⽤户下的订单应该是,什么时候⽤,什么时候查询。 (延迟加载)在查询订单时,订单所属的⽤户信息应该是随着订单⼀起查询出来。(立即加载)
优点:先从单表查询,需要时再从关联表去关联查询,⼤⼤提⾼数据库性能,因为查询单表要⽐关联查询多张表速度要快。* 缺点:因为只有当需要⽤到数据时,才会进⾏数据库查询,这样在⼤批量数据查询时,因为查询⼯作也要消耗时间,所以可能造成⽤户等待时间变⻓,造成⽤户体验下降。* 在多表中:⼀对多,多对多:通常情况下采⽤延迟加载⼀对⼀(多对⼀):通常情况下采⽤⽴即加载* 注意:延迟加载是基于嵌套查询来实现的
#联合查询
SELECT u.*,o.id oid,o.`ordertime`,o.`total`,o.`uid` FROM USER u LEFT JOIN orders o ON u.id=o.uid
#嵌套查询
#1.先查询用户信息
SELECT * FROM USER WHERE id=1
#2.再根据tom用户信息查询相关的订单信息
SELECT * FROM ORDER WHERE uid=1
<resultMap id="userMap" type="com.lagou.pojo.User"><id property="id" column="id"></id><result property="username" column="username"></result><collection property="orderList" ofType="com.lagou.pojo.Order"select="com.lagou.mapper.IOrderMapper.findOrderByUid" column="id" ><id property="id" column="oid"/><result property="orderTime" column="ordertime"/><result property="total" column="total"/></collection> </resultMap><select id="findById" resultMap="userMap" >select * from user where id = #{id} </select>IOrderMapper接口映射文件定义 <select id="findOrderByUid" resultType="com.lagou.pojo.Order">select * from orders where uid = #{uid} </select>
@Test public void test4() throws IOException {InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);SqlSession sqlSession = factory.openSession();User user = sqlSession.selectOne("com.lagou.mapper.IUserMapper.findById", 1);System.out.println(user.getUsername());}

实现
<resultMap id="userMap" type="com.lagou.pojo.User"><id property="id" column="id"></id><result property="username" column="username"></result> <--fetchType="lazy"懒加载策略fetchType="eager"立即加载策略 --><collection property="orderList" ofType="com.lagou.pojo.Order"select="com.lagou.mapper.IOrderMapper.findOrderByUid" column="id" fetchType="lazy"><id property="id" column="oid"/><result property="orderTime" column="ordertime"/><result property="total" column="total"/></collection> </resultMap><select id="findById" resultMap="userMap" >select * from user where id = #{id} </select>IOrderMapper接口映射文件定义 <select id="findOrderByUid" resultType="com.lagou.pojo.Order">select * from orders where uid = #{uid} </select>

System.out.println(user.getOrderList());
<!--开启全局的延迟加载配置--> <settings><setting name="lazyLoadingEnabled" value="true"/> </settings>
<!-- 关闭⼀对⼀ 延迟加载 --><resultMap id="orderMap" type="order"><id column="id" property="id"></id><result column="ordertime" property="ordertime"></result><result column="total" property="total"></result><!--fetchType="lazy" 懒加载策略fetchType="eager" ⽴即加载策略--><association property="user" column="uid" javaType="user"select="com.lagou.dao.UserMapper.findById" fetchType="eager"></association></resultMap><select id="findAll" resultMap="orderMap">SELECT * from orders</select>

public class Configuration {/** aggressiveLazyLoading:* 当开启时,任何⽅法的调⽤都会加载该对象的所有属性。否则,每个属性会按需加载(参考lazyLoadTriggerMethods).* 默认为true* */protected boolean aggressiveLazyLoading;/*** 延迟加载触发⽅法*/protected Set<String> lazyLoadTriggerMethods = new HashSet<String>(Arrays.asList(new String[] { "equals", "clone", "hashCode", "toString" }));/** 是否开启延迟加载 */protected boolean lazyLoadingEnabled = false;/*** 默认使⽤Javassist代理⼯⼚* @param proxyFactory*/public void setProxyFactory(ProxyFactory proxyFactory) {if (proxyFactory == null) {proxyFactory = new JavassistProxyFactory();}this.proxyFactory = proxyFactory;}//省略...}
<code class="language-Java">//#mark 创建结果对象private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap,ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {this.useConstructorMappings = false; // reset previous mapping resultfinal List<Class<?>> constructorArgTypes = newArrayList<Class<?>>();final List<Object> constructorArgs = new ArrayList<Object>();//#mark 创建返回的结果映射的真实对象Object resultObject = createResultObject(rsw, resultMap,constructorArgTypes, constructorArgs, columnPrefix);if (resultObject != null && !hasTypeHandlerForResultObject(rsw,resultMap.getType())) {final List<ResultMapping> propertyMappings =resultMap.getPropertyResultMappings();for (ResultMapping propertyMapping : propertyMappings) {// 判断属性有没配置嵌套查询,如果有就创建代理对象if (propertyMapping.getNestedQueryId() != null &&propertyMapping.isLazy()) {//#mark 创建延迟加载代理对象resultObject =configuration.getProxyFactory().createProxy(resultObject, lazyLoader,configuration, objectFactory, constructorArgTypes, constructorArgs);break;}}}this.useConstructorMappings = resultObject != null &&!constructorArgTypes.isEmpty(); // set current mapping resultreturn resultObject; }

JavasisstProxyFactory实现
public class JavassistProxyFactory implementsorg.apache.ibatis.executor.loader.ProxyFactory {/*** 接⼝实现* @param target ⽬标结果对象* @param lazyLoader 延迟加载对象* @param configuration 配置* @param objectFactory 对象⼯⼚* @param constructorArgTypes 构造参数类型* @param constructorArgs 构造参数值* @return*/@Overridepublic Object createProxy(Object target, ResultLoaderMap lazyLoader,Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {return EnhancedResultObjectProxyImpl.createProxy(target, lazyLoader,configuration, objectFactory, constructorArgTypes, constructorArgs);}//省略.../*** 代理对象实现,核⼼逻辑执⾏*/private static class EnhancedResultObjectProxyImpl implements MethodHandler{/*** 创建代理对象* @param type* @param callback* @param constructorArgTypes* @param constructorArgs* @return*/ static Object crateProxy(Class<?> type, MethodHandler callback,List<Class<?>> constructorArgTypes, List<Object>constructorArgs) {ProxyFactory enhancer = new ProxyFactory();enhancer.setSuperclass(type);try {//通过获取对象⽅法,判断是否存在该⽅法type.getDeclaredMethod(WRITE_REPLACE_METHOD);// ObjectOutputStream will call writeReplace of objects returned bywriteReplaceif (log.isDebugEnabled()) {log.debug(WRITE_REPLACE_METHOD + " method was found on bean" + type + ", make sure it returns this");}} catch (NoSuchMethodException e) {//没找到该⽅法,实现接⼝enhancer.setInterfaces(new Class[]{WriteReplaceInterface.class});} catch (SecurityException e) {// nothing to do here}Object enhanced;Class<?>[] typesArray = constructorArgTypes.toArray(newClass[constructorArgTypes.size()]);Object[] valuesArray = constructorArgs.toArray(newObject[constructorArgs.size()]);try {//创建新的代理对象enhanced = enhancer.create(typesArray, valuesArray);} catch (Exception e) {throw new ExecutorException("Error creating lazy proxy. Cause:" + e, e);}//设置代理执⾏器((Proxy) enhanced).setHandler(callback);return enhanced;}/*** 代理对象执⾏* @param enhanced 原对象* @param method 原对象⽅法* @param methodProxy 代理⽅法* @param args ⽅法参数* @return* @throws Throwable */@Overridepublic Object invoke(Object enhanced, Method method, Method methodProxy,Object[] args) throws Throwable {final String methodName = method.getName();try {synchronized (lazyLoader) {if (WRITE_REPLACE_METHOD.equals(methodName)) {//忽略暂未找到具体作⽤Object original;if (constructorArgTypes.isEmpty()) {original = objectFactory.create(type);} else {original = objectFactory.create(type, constructorArgTypes,constructorArgs);}PropertyCopier.copyBeanProperties(type, enhanced, original);if (lazyLoader.size() > 0) {return new JavassistSerialStateHolder(original,lazyLoader.getProperties(), objectFactory, constructorArgTypes,constructorArgs);} else {return original;}} else {//延迟加载数量⼤于0if (lazyLoader.size() > 0 &&!FINALIZE_METHOD.equals(methodName)) {//aggressive ⼀次加载性所有需要要延迟加载属性或者包含触发延迟加载⽅法if (aggressive || lazyLoadTriggerMethods.contains(methodName)) {log.debug("==> laze lod trigger method:" +methodName + ",proxy method:" + methodProxy.getName() + "class:" + enhanced.getClass());//⼀次全部加载lazyLoader.loadAll();} else if (PropertyNamer.isSetter(methodName)) {//判断是否为set⽅法,set⽅法不需要延迟加载final String property =PropertyNamer.methodToProperty(methodName);lazyLoader.remove(property);} else if (PropertyNamer.isGetter(methodName)) {final String property =PropertyNamer.methodToProperty(methodName);if (lazyLoader.hasLoader(property)) {//延迟加载单个属性lazyLoader.load(property);log.debug("load one :" + methodName);}}}}}return methodProxy.invoke(enhanced, args);} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}}}
注意事项