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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

if mybatis tk 多个_面试题:mybatis 中的 DAO 接口和 XML 文件里的 SQL 是如何建立关系的?...

發(fā)布時間:2025/3/11 asp.net 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 if mybatis tk 多个_面试题:mybatis 中的 DAO 接口和 XML 文件里的 SQL 是如何建立关系的?... 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

前言

這是 mybatis 比較常問到的面試題,我自己在以前的面試過程中被問到了2次,2次都是非常重要的面試環(huán)節(jié),因此自己印象很深刻。

這個題目我很早就深入學習了,但是一直沒有整理出來,剛好最近一段時間由于工作太忙,大概有半年沒有技術(shù)文章產(chǎn)出,因此趁著五一有點時間,整理了下分享給大家。

另外,估計不少同學應(yīng)該也注意到了,DAO 接口的全路徑名和 XML 文件中的 SQL 的 namespace + id 是一樣的。其實,這也是建立關(guān)聯(lián)的根本原因。

本文中的源碼使用當前最新的版本,即:mybatis-spring 為 2.0.4,mybatis 為 3.5.4,引入這2個 jar 包即可查看到本文的所有代碼。

正文

當一個項目中使用了 Spring 和 Mybatis 時,通常會有以下配置。當然現(xiàn)在很多項目應(yīng)該都是 SpringBoot 了,可能沒有以下配置,但是究其底層原理都是類似的,無非是將掃描 bean 等一些工作通過注解來實現(xiàn)。

<!-- DAO接口所在包名,Spring會自動查找其下的類 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"><!--basePackage指定要掃描的包,在此包之下的映射器都會被搜索到。可指定多個包,包與包之間用逗號或分號分隔--><property name="basePackage" value="com.joonwhee.open.mapper"/><property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> </bean><!-- spring和MyBatis完美整合,不需要mybatis的配置映射文件 --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource" ref="dataSource"/><!-- 自動掃描mapping.xml文件 --><property name="mapperLocations" value="classpath:config/mapper/*.xml"/><property name="configLocation" value="classpath:config/mybatis/mybatis-config.xml"/><!--Entity package --><property name="typeAliasesPackage" value="com.joonwhee.open.po"/> </bean><!-- dataSource --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"><property name="driverClassName" value="${driver}"/><property name="url" value="${url}"/><property name="username" value="${username}"/><property name="password" value="${password}"/> </bean>

通常我們還會有 DAO 類和 對用的 mapper 文件,如下。

package com.joonwhee.open.mapper;import com.joonwhee.open.po.UserPO;public interface UserPOMapper {UserPO queryByPrimaryKey(Integer id); } <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace="com.joonwhee.open.mapper.UserPOMapper" ><resultMap id="BaseResultMap" type="com.joonwhee.open.po.UserPO"><result column="id" property="id" jdbcType="INTEGER" /><result column="name" property="name" jdbcType="VARCHAR" /></resultMap><select id="queryByPrimaryKey" resultMap="BaseResultMap"parameterType="java.lang.Integer">select id, namefrom userwhere id = #{id,jdbcType=INTEGER}</select> </mapper>

1、解析 MapperScannerConfigurer

MapperScannerConfigurer 是一個 BeanDefinitionRegistryPostProcessor,會在 Spring 構(gòu)建 IoC容器的早期被調(diào)用重寫的 postProcessBeanDefinitionRegistry 方法,參考:Spring IoC:invokeBeanFactoryPostProcessors 詳解

@Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {if (this.processPropertyPlaceHolders) {processPropertyPlaceHolders();}// 1.新建一個ClassPathMapperScanner,并填充相應(yīng)屬性ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);scanner.setAddToConfig(this.addToConfig);scanner.setAnnotationClass(this.annotationClass);scanner.setMarkerInterface(this.markerInterface);scanner.setSqlSessionFactory(this.sqlSessionFactory);scanner.setSqlSessionTemplate(this.sqlSessionTemplate);scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);scanner.setResourceLoader(this.applicationContext);scanner.setBeanNameGenerator(this.nameGenerator);scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);if (StringUtils.hasText(lazyInitialization)) {// 2.設(shè)置mapper bean是否需要懶加載scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));}// 3.注冊Filter,因為上面構(gòu)造函數(shù)我們沒有使用默認的Filter,// 有兩種Filter,includeFilters:要掃描的;excludeFilters:要排除的scanner.registerFilters();// 4.掃描basePackage,basePackage可通過",; tn"來填寫多個,// ClassPathMapperScanner重寫了doScan方法scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS)); }

3.注冊 Filter,見代碼塊1。

4.掃描 basePackage,這邊會走到 ClassPathBeanDefinitionScanner(ClassPathMapperScanner 的父類),然后在執(zhí)行 “doScan(basePackages)” 時回到 ClassPathMapperScanner 重寫的方法,見代碼塊2。

代碼塊1:registerFilters

public void registerFilters() {boolean acceptAllInterfaces = true;// if specified, use the given annotation and / or marker interface// 1.如果指定了注解,則將注解添加到includeFiltersif (this.annotationClass != null) {addIncludeFilter(new AnnotationTypeFilter(this.annotationClass));acceptAllInterfaces = false;}// override AssignableTypeFilter to ignore matches on the actual marker interface// 2.如果指定了標記接口,則將標記接口添加到includeFilters,// 但這邊重寫了matchClassName方法,并返回了false,// 相當于忽略了標記接口上的匹配項,所以該參數(shù)目前相當于沒有任何作用if (this.markerInterface != null) {addIncludeFilter(new AssignableTypeFilter(this.markerInterface) {@Overrideprotected boolean matchClassName(String className) {return false;}});acceptAllInterfaces = false;}// 3.如果沒有指定annotationClass和markerInterface,則// 添加默認的includeFilters,直接返回true,接受所有類if (acceptAllInterfaces) {// default include filter that accepts all classesaddIncludeFilter((metadataReader, metadataReaderFactory) -> true);}// exclude package-info.java// 4.添加默認的excludeFilters,排除以package-info結(jié)尾的類addExcludeFilter((metadataReader, metadataReaderFactory) -> {String className = metadataReader.getClassMetadata().getClassName();return className.endsWith("package-info");}); }

通常我們都不會指定 annotationClass 和 markerInterface,也就是會添加默認的 Filter,相當于會接受除了 package-info 結(jié)尾的所有類。因此,basePackage 包下的類不需要使用 @Component 注解或 XML 中配置 bean 定義,也會被添加到 IoC 容器中。

代碼塊2:doScan

@Override public Set<BeanDefinitionHolder> doScan(String... basePackages) {// 1.直接使用父類的方法掃描和注冊bean定義,// 之前在spring中已經(jīng)介紹過:Spring IoC源碼學習:context:component-scan 節(jié)點詳解 代碼塊5Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);if (beanDefinitions.isEmpty()) {LOGGER.warn(() -> "No MyBatis mapper was found in '" + Arrays.toString(basePackages)+ "' package. Please check your configuration.");} else {// 2.對掃描到的beanDefinitions進行處理,主要4件事:// 1)將bean的真正接口類添加到通用構(gòu)造函數(shù)參數(shù)中// 2)將beanClass直接設(shè)置為MapperFactoryBean.class,// 結(jié)合1,相當于要使用的構(gòu)造函數(shù)是MapperFactoryBean(java.lang.Class<T>)// 3)添加sqlSessionFactory屬性,sqlSessionFactoryBeanName和// sqlSessionFactory中,優(yōu)先使用sqlSessionFactoryBeanName// 4)添加sqlSessionTemplate屬性,同樣的,sqlSessionTemplateBeanName// 優(yōu)先于sqlSessionTemplate,processBeanDefinitions(beanDefinitions);}return beanDefinitions; }

小結(jié),解析 MapperScannerConfigurer 主要是做了幾件事:

1)新建掃描器 ClassPathMapperScanner;

2)使用 ClassPathMapperScanner 掃描注冊 basePackage 包下的所有 bean;

3)將 basePackage 包下的所有 bean 進行一些特殊處理:beanClass 設(shè)置為 MapperFactoryBean、bean 的真正接口類作為構(gòu)造函數(shù)參數(shù)傳入 MapperFactoryBean、為 MapperFactoryBean 添加 sqlSessionFactory 和 sqlSessionTemplate屬性。

2、解析 SqlSessionFactoryBean

對于 SqlSessionFactoryBean 來說,實現(xiàn)了2個接口,InitializingBean 和 FactoryBean,看過我之前 Spring 文章的同學應(yīng)該對這2個接口不會陌生,簡單來說:1)FactoryBean 可以自己定義創(chuàng)建實例對象的方法,只需要實現(xiàn)它的 getObject() 方法;InitializingBean 則是會在 bean 初始化階段被調(diào)用。

SqlSessionFactoryBean 重寫這兩個接口的部分方法代碼如下,核心代碼就一個方法—— “buildSqlSessionFactory()”。

@Override public SqlSessionFactory getObject() throws Exception {if (this.sqlSessionFactory == null) {// 如果之前沒有構(gòu)建,則這邊也會調(diào)用afterPropertiesSet進行構(gòu)建操作afterPropertiesSet();}return this.sqlSessionFactory; }@Override public void afterPropertiesSet() throws Exception {// 省略部分代碼// 構(gòu)建sqlSessionFactorythis.sqlSessionFactory = buildSqlSessionFactory(); }

buildSqlSessionFactory()

主要做了幾件事:1)對我們配置的參數(shù)進行相應(yīng)解析;2)使用配置的參數(shù)構(gòu)建一個 Configuration;3)使用 Configuration 新建一個 DefaultSqlSessionFactory。

這邊的核心內(nèi)容是對于 mapperLocations 的解析,如下代碼。

protected SqlSessionFactory buildSqlSessionFactory() throws Exception {// 省略部分代碼// 5.mapper處理(最重要)if (this.mapperLocations != null) {if (this.mapperLocations.length == 0) {LOGGER.warn(() -> "Property 'mapperLocations' was specified but matching resources are not found.");} else {for (Resource mapperLocation : this.mapperLocations) {if (mapperLocation == null) {continue;}try {// 5.1 新建XMLMapperBuilderXMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());// 5.2 解析mapper文件xmlMapperBuilder.parse();} catch (Exception e) {throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);} finally {ErrorContext.instance().reset();}LOGGER.debug(() -> "Parsed mapper file: '" + mapperLocation + "'");}}} else {LOGGER.debug(() -> "Property 'mapperLocations' was not specified.");}// 6.使用targetConfiguration構(gòu)建DefaultSqlSessionFactoryreturn this.sqlSessionFactoryBuilder.build(targetConfiguration); }

5.2 解析mapper文件,見代碼塊3。

代碼塊3:parse()

public void parse() {// 1.如果resource沒被加載過才進行加載if (!configuration.isResourceLoaded(resource)) {// 1.1 解析mapper文件configurationElement(parser.evalNode("/mapper"));// 1.2 將resource添加到已加載列表configuration.addLoadedResource(resource);// 1.3 綁定namespace的mapperbindMapperForNamespace();}parsePendingResultMaps();parsePendingCacheRefs();parsePendingStatements(); }

1.1 解析mapper文件,見代碼4。

1.3 綁定namespace的mapper,見代碼塊6。

代碼塊4:configurationElement

private void configurationElement(XNode context) {try {// 1.獲取namespace屬性String namespace = context.getStringAttribute("namespace");if (namespace == null || namespace.isEmpty()) {throw new BuilderException("Mapper's namespace cannot be empty");}// 2.設(shè)置currentNamespace屬性builderAssistant.setCurrentNamespace(namespace);// 3.解析parameterMap、resultMap、sql等節(jié)點cacheRefElement(context.evalNode("cache-ref"));cacheElement(context.evalNode("cache"));parameterMapElement(context.evalNodes("/mapper/parameterMap"));resultMapElements(context.evalNodes("/mapper/resultMap"));sqlElement(context.evalNodes("/mapper/sql"));// 4.解析增刪改查節(jié)點,封裝成StatementbuildStatementFromContext(context.evalNodes("select|insert|update|delete"));} catch (Exception e) {throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);} }private void buildStatementFromContext(List<XNode> list) {if (configuration.getDatabaseId() != null) {buildStatementFromContext(list, configuration.getDatabaseId());}// 解析增刪改查節(jié)點,封裝成StatementbuildStatementFromContext(list, null); }private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {for (XNode context : list) {// 1.構(gòu)建XMLStatementBuilderfinal XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);try {// 2.解析節(jié)點statementParser.parseStatementNode();} catch (IncompleteElementException e) {configuration.addIncompleteStatement(statementParser);}} }

這邊會一直執(zhí)行到 “statementParser.parseStatementNode();”,見代碼塊5。

這邊每個 XNode 都相當于如下的一個 SQL,下面封裝的每個 MappedStatement 可以理解就是每個 SQL。

<select id="queryByPrimaryKey" resultMap="BaseResultMap"parameterType="java.lang.Integer">select id, name, password, agefrom userwhere id = #{id,jdbcType=INTEGER} </select>

代碼塊5:parseStatementNode

public void parseStatementNode() {// 省略所有的屬性解析// 將解析出來的所有參數(shù)添加到 mappedStatements 緩存builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,resultSetTypeEnum, flushCache, useCache, resultOrdered,keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets); }// MapperBuilderAssistant.java public MappedStatement addMappedStatement(String id,SqlSource sqlSource,StatementType statementType,SqlCommandType sqlCommandType,Integer fetchSize,Integer timeout,String parameterMap,Class<?> parameterType,String resultMap,Class<?> resultType,ResultSetType resultSetType,boolean flushCache,boolean useCache,boolean resultOrdered,KeyGenerator keyGenerator,String keyProperty,String keyColumn,String databaseId,LanguageDriver lang,String resultSets) {if (unresolvedCacheRef) {throw new IncompleteElementException("Cache-ref not yet resolved");}// 1.將id填充上namespace,例如:queryByPrimaryKey變成// com.joonwhee.open.mapper.UserPOMapper.queryByPrimaryKeyid = applyCurrentNamespace(id, false);boolean isSelect = sqlCommandType == SqlCommandType.SELECT;// 2.使用參數(shù)構(gòu)建MappedStatement.BuilderMappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType).resource(resource).fetchSize(fetchSize).timeout(timeout).statementType(statementType).keyGenerator(keyGenerator).keyProperty(keyProperty).keyColumn(keyColumn).databaseId(databaseId).lang(lang).resultOrdered(resultOrdered).resultSets(resultSets).resultMaps(getStatementResultMaps(resultMap, resultType, id)).resultSetType(resultSetType).flushCacheRequired(valueOrDefault(flushCache, !isSelect)).useCache(valueOrDefault(useCache, isSelect)).cache(currentCache);ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);if (statementParameterMap != null) {statementBuilder.parameterMap(statementParameterMap);}// 3.使用MappedStatement.Builder構(gòu)建MappedStatementMappedStatement statement = statementBuilder.build();// 4.將MappedStatement 添加到緩存configuration.addMappedStatement(statement);return statement; }

該方法會將節(jié)點的屬性解析后封裝成 MappedStatement,放到 mappedStatements 緩存中,key 為 id,例如:com.joonwhee.open.mapper.UserPOMapper.queryByPrimaryKey,value 為 MappedStatement。

代碼塊6:bindMapperForNamespace

private void bindMapperForNamespace() {String namespace = builderAssistant.getCurrentNamespace();if (namespace != null) {Class<?> boundType = null;try {// 1.解析namespace對應(yīng)的綁定類型boundType = Resources.classForName(namespace);} catch (ClassNotFoundException e) {// ignore, bound type is not required}if (boundType != null && !configuration.hasMapper(boundType)) {// Spring may not know the real resource name so we set a flag// to prevent loading again this resource from the mapper interface// look at MapperAnnotationBuilder#loadXmlResource// 2.boundType不為空,并且configuration還沒有添加boundType,// 則將namespace添加到已加載列表,將boundType添加到knownMappers緩存configuration.addLoadedResource("namespace:" + namespace);configuration.addMapper(boundType);}} }public <T> void addMapper(Class<T> type) {mapperRegistry.addMapper(type); }public <T> void addMapper(Class<T> type) {if (type.isInterface()) {if (hasMapper(type)) {throw new BindingException("Type " + type + " is already known to the MapperRegistry.");}boolean loadCompleted = false;try {// 將type和以該type為參數(shù)構(gòu)建的MapperProxyFactory作為鍵值對,// 放到knownMappers緩存中去knownMappers.put(type, new MapperProxyFactory<>(type));// It's important that the type is added before the parser is run// otherwise the binding may automatically be attempted by the// mapper parser. If the type is already known, it won't try.MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);parser.parse();loadCompleted = true;} finally {if (!loadCompleted) {knownMappers.remove(type);}}} }

主要是將剛剛解析過的 mapper 文件的 namespace 放到 knownMappers 緩存中,key 為 namespace 對應(yīng)的 class,value 為 MapperProxyFactory。

小結(jié),解析 SqlSessionFactoryBean 主要做了幾件事:

1)解析處理所有屬性參數(shù)構(gòu)建 Configuration ,使用 Configuration 新建 DefaultSqlSessionFactory;

2)解析 mapperLocations 屬性的 mapper 文件,將 mapper 文件中的每個 SQL 封裝成 MappedStatement,放到 mappedStatements 緩存中,key 為 id,例如:com.joonwhee.open.mapper.UserPOMapper.queryByPrimaryKey,value 為 MappedStatement。

3)將解析過的 mapper 文件的 namespace 放到 knownMappers 緩存中,key 為 namespace 對應(yīng)的 class,value 為 MapperProxyFactory。

3、解析 DAO 文件

DAO 文件,也就是 basePackage 指定的包下的文件,也就是上文的 interface UserPOMapper 。

上文 doScan 中說過,basePackage 包下所有 bean 定義的 beanClass 會被設(shè)置成 MapperFactoryBean.class,而 MapperFactoryBean 也是 FactoryBean,因此直接看 MapperFactoryBean 的 getObject 方法。

@Override public T getObject() throws Exception {// 1.從父類中拿到sqlSessionTemplate,這邊的sqlSessionTemplate也是doScan中添加的屬性// 2.通過mapperInterface獲取mapperreturn getSqlSession().getMapper(this.mapperInterface); }// SqlSessionTemplate @Override public <T> T getMapper(Class<T> type) {return getConfiguration().getMapper(type, this); }// Configuration.java public <T> T getMapper(Class<T> type, SqlSession sqlSession) {return mapperRegistry.getMapper(type, sqlSession); }// MapperRegistry.java public <T> T getMapper(Class<T> type, SqlSession sqlSession) {// 1.從knownMappers緩存中獲取final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);if (mapperProxyFactory == null) {throw new BindingException("Type " + type + " is not known to the MapperRegistry.");}try {// 2.新建實例return mapperProxyFactory.newInstance(sqlSession);} catch (Exception e) {throw new BindingException("Error getting mapper instance. Cause: " + e, e);} }// MapperProxyFactory.java public T newInstance(SqlSession sqlSession) {// 1.構(gòu)造一個MapperProxyfinal MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);// 2.使用MapperProxy來構(gòu)建實例對象return newInstance(mapperProxy); }protected T newInstance(MapperProxy<T> mapperProxy) {// 使用JDK動態(tài)代理來代理要創(chuàng)建的實例對象,InvocationHandler為mapperProxy,// 因此當我們真正調(diào)用時,會走到mapperProxy的invoke方法return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); }

這邊代碼用到的 sqlSessionTemplate、mapperInterface 等都是之前添加的屬性。

小結(jié),解析 DAO 文件 主要做了幾件事:

1)通過 mapperInterface 從 knownMappers 緩存中獲取到 MapperProxyFactory 對象;

2)通過 JDK 動態(tài)代理創(chuàng)建 MapperProxyFactory 實例對象,InvocationHandler 為 MapperProxy。

4、DAO 接口被調(diào)用

當 DAO 中的接口被調(diào)用時,會走到 MapperProxy 的 invoke 方法。

@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {if (Object.class.equals(method.getDeclaringClass())) {return method.invoke(this, args);} else {// 1.創(chuàng)建MapperMethodInvoker// 2.將method -> MapperMethodInvoker放到methodCache緩存// 3.調(diào)用MapperMethodInvoker的invoke方法return cachedInvoker(method).invoke(proxy, method, args, sqlSession);}} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);} }// MapperProxy.java private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {try {// 1.放到methodCache緩存,key為method,value為MapperMethodInvokerreturn methodCache.computeIfAbsent(method, m -> {if (m.isDefault()) {// 2.方法為默認方法,Java8之后,接口允許有默認方法try {if (privateLookupInMethod == null) {return new DefaultMethodInvoker(getMethodHandleJava8(method));} else {return new DefaultMethodInvoker(getMethodHandleJava9(method));}} catch (IllegalAccessException | InstantiationException | InvocationTargetException| NoSuchMethodException e) {throw new RuntimeException(e);}} else {// 3.正常接口會走這邊,使用mapperInterface、method、configuration// 構(gòu)建一個MapperMethod,封裝成PlainMethodInvokerreturn new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));}});} catch (RuntimeException re) {Throwable cause = re.getCause();throw cause == null ? re : cause;} }

3.調(diào)用 MapperMethodInvoker 的 invoke 方法,見代碼塊7。

代碼塊7:invoke

@Override public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {return mapperMethod.execute(sqlSession, args); }// MapperMethod.java public Object execute(SqlSession sqlSession, Object[] args) {Object result;// 1.根據(jù)命令類型執(zhí)行來進行相應(yīng)操作switch (command.getType()) {case INSERT: {Object param = method.convertArgsToSqlCommandParam(args);result = rowCountResult(sqlSession.insert(command.getName(), param));break;}case UPDATE: {Object param = method.convertArgsToSqlCommandParam(args);result = rowCountResult(sqlSession.update(command.getName(), param));break;}case DELETE: {Object param = method.convertArgsToSqlCommandParam(args);result = rowCountResult(sqlSession.delete(command.getName(), param));break;}case SELECT:if (method.returnsVoid() && method.hasResultHandler()) {executeWithResultHandler(sqlSession, args);result = null;} else if (method.returnsMany()) {result = executeForMany(sqlSession, args);} else if (method.returnsMap()) {result = executeForMap(sqlSession, args);} 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());}if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {throw new BindingException("Mapper method '" + command.getName()+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");}return result; }

這邊就比較簡單,根據(jù)不同的操作類型執(zhí)行相應(yīng)的操作,最終將結(jié)果返回,見代碼塊8。

這邊的 command 是上文 “new MapperMethod(mapperInterface, method, sqlSession.getConfiguration())” 時創(chuàng)建的。

代碼塊8:增刪改查

// 1.insert @Override public int insert(String statement, Object parameter) {return update(statement, parameter); }// 2.update @Override public int update(String statement, Object parameter) {try {dirty = true;// 從mappedStatements緩存拿到對應(yīng)的MappedStatement對象,執(zhí)行更新操作MappedStatement ms = configuration.getMappedStatement(statement);return executor.update(ms, wrapCollection(parameter));} catch (Exception e) {throw ExceptionFactory.wrapException("Error updating database. Cause: " + e, e);} finally {ErrorContext.instance().reset();} }// 3.delete @Override public int delete(String statement, Object parameter) {return update(statement, parameter); }// 4.select,以executeForMany為例 private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {List<E> result;// 1.參數(shù)轉(zhuǎn)換成sql命令參數(shù)Object param = method.convertArgsToSqlCommandParam(args);if (method.hasRowBounds()) {RowBounds rowBounds = method.extractRowBounds(args);result = sqlSession.selectList(command.getName(), param, rowBounds);} else {// 2.執(zhí)行查詢操作result = sqlSession.selectList(command.getName(), param);}// 3.處理返回結(jié)果// issue #510 Collections & arrays supportif (!method.getReturnType().isAssignableFrom(result.getClass())) {if (method.getReturnType().isArray()) {return convertToArray(result);} else {return convertToDeclaredCollection(sqlSession.getConfiguration(), result);}}return result; }@Override public <E> List<E> selectList(String statement, Object parameter) {return this.selectList(statement, parameter, RowBounds.DEFAULT); }@Override public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {try {//從mappedStatements緩存中拿到對應(yīng)的MappedStatement對象,執(zhí)行查詢操作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();} }

可以看出,最終都是從 mappedStatements 緩存中拿到對應(yīng)的 MappedStatement 對象,執(zhí)行相應(yīng)的操作。

這邊的增刪改查不是直接調(diào)用 SqlSession 中的方法,而是調(diào)用 SqlSessionTemplate 中的方法,繼而通過 sqlSessionProxy 來調(diào)用 SqlSession 中的方法。SqlSessionTemplate 中的方法主要是通過 sqlSessionProxy 做了一層動態(tài)代理,基本沒差別。

總結(jié)

整個流程主要是以下幾個核心步驟:

1)掃描注冊 basePackage 包下的所有 bean,將 basePackage 包下的所有 bean 進行一些特殊處理:beanClass 設(shè)置為 MapperFactoryBean、bean 的真正接口類作為構(gòu)造函數(shù)參數(shù)傳入 MapperFactoryBean、為 MapperFactoryBean 添加 sqlSessionFactory 和 sqlSessionTemplate屬性。

2)解析 mapperLocations 屬性的 mapper 文件,將 mapper 文件中的每個 SQL 封裝成 MappedStatement,放到 mappedStatements 緩存中,key 為 id,例如:com.joonwhee.open.mapper.UserPOMapper.queryByPrimaryKey,value 為 MappedStatement。并且將解析過的 mapper 文件的 namespace 放到 knownMappers 緩存中,key 為 namespace 對應(yīng)的 class,value 為 MapperProxyFactory。

3)創(chuàng)建 DAO 的 bean 時,通過 mapperInterface 從 knownMappers 緩存中獲取到 MapperProxyFactory 對象,通過 JDK 動態(tài)代理創(chuàng)建 MapperProxyFactory 實例對象,InvocationHandler 為 MapperProxy。

4)DAO 中的接口被調(diào)用時,通過動態(tài)代理,調(diào)用 MapperProxy 的 invoke 方法,最終通過 mapperInterface 從 mappedStatements 緩存中拿到對應(yīng)的 MappedStatement,執(zhí)行相應(yīng)的操作。

推薦閱讀

程序員囧輝:字節(jié)、美團、快手核心部門面試總結(jié)(真題解析)?zhuanlan.zhihu.com程序員囧輝:如何準備好一場大廠面試?zhuanlan.zhihu.com程序員囧輝:跳槽,如何選擇一家公司?zhuanlan.zhihu.com程序員囧輝:如何寫一份讓 HR 眼前一亮的簡歷(附模板)?zhuanlan.zhihu.com程序員囧輝:4 年 Java 經(jīng)驗面試總結(jié)、心得體會?zhuanlan.zhihu.com程序員囧輝:面試阿里,HashMap 這一篇就夠了?zhuanlan.zhihu.com程序員囧輝:面試必問的線程池,你懂了嗎??zhuanlan.zhihu.com程序員囧輝:BATJTMD 面試必問的 MySQL,你懂了嗎??zhuanlan.zhihu.com程序員囧輝:面試題:mybatis 中的 DAO 接口和 XML 文件里的 SQL 是如何建立關(guān)系的??zhuanlan.zhihu.com

總結(jié)

以上是生活随笔為你收集整理的if mybatis tk 多个_面试题:mybatis 中的 DAO 接口和 XML 文件里的 SQL 是如何建立关系的?...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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

主站蜘蛛池模板: 中文字幕观看 | 欧美精品免费在线 | 成人在线视频观看 | 亚洲123区 | 国产一区二区在线播放 | 久久久久久久久久久久久久久久久 | av大片在线播放 | 羞辱极度绿帽vk | 人妻与黑人一区二区三区 | 国产精品第72页 | 体内射精一区二区 | 国产成人精品一区二三区四区五区 | 国产麻豆成人精品av | 国产123在线 | 国产欧美一区二区在线观看 | 亚洲黄色在线看 | 好吊色一区二区 | 超碰爱爱 | 一级a性色生活片久久毛片 爱爱高潮视频 | 一级黄色在线视频 | 亚洲91精品 | 综合亚洲色图 | 华人永久免费 | 华人永久免费视频 | 一级黄在线观看 | 成人你懂的 | 欧美日韩一区二区三区电影 | 少妇激情偷人三级 | 最新视频–x99av | 国产人妻人伦精品1国产盗摄 | 国产一二三四在线 | 久久成人a毛片免费观看网站 | 国产青青草视频 | 奇米第四色影视 | 精品黑人一区二区三区国语馆 | 都市激情 亚洲 | 男人天堂一区 | 先锋影音av资源在线 | 国内少妇毛片视频 | 女女同性女同一区二区三区九色 | 潘金莲一级淫片aaaaaa播放 | 成人精品一区二区三区 | 日韩欧美成人免费视频 | 欧美性网址 | 美女扒开粉嫩的尿囗给男生桶 | www亚洲一区 | 亚洲自拍网站 | 玩日本老头很兴奋xxxx | 最新中文字幕第一页 | 欧美第一精品 | 亚洲伊人久久综合 | 欧美日韩精品网站 | 亚洲精品高潮久久久久久久 | 亚洲国产精品无码专区 | 免费看国产曰批40分钟粉红裤头 | 婷婷激情在线 | 亚洲精品在线视频免费观看 | 黄色精品在线 | 青青草日韩 | 国产不卡精品 | 韩国妻子的朋友 | 男女猛烈无遮挡免费视频 | 欧美视频亚洲视频 | 亚洲免费影视 | 国产主播在线观看 | 亚洲第一在线 | 亚洲精品国产欧美 | 色伊人网 | 亚洲色图 美腿丝袜 | 草草影院网址 | 欧美乱大交xxxxx | 亚洲人人爱 | 欧美在线视频免费观看 | 日本一区二区在线免费观看 | 超碰香蕉| 国产精品久久久久久久久久久久久 | 美女扒开内裤让男人捅 | 日韩高清三区 | 狠狠干在线观看 | 天堂av免费观看 | 免费成人深夜 | 在线观看精品 | 无码人妻一区二区三区免费n鬼沢 | 日韩黄色片子 | 日韩在线黄色 | 中文字幕理伦片免费看 | 天天摸天天操 | 三级av网 | 99久久久无码国产精品不卡 | 理论片亚洲 | 欧美美女喷水 | 一级毛片黄色 | 美国一级片网站 | 日韩免费淫片 | 精品国产户外野外 | 欧美精品四区 | 国产夜色精品一区二区av | 亚洲中文字幕无码一区 | 成人黄色在线观看 |