Mybatis global configuration file <mappers> tag analysis
write in front
In this article we analyze the process of parsing the global configuration file, in which "5.1.10: mappers" analyzes the parsing<mappers/>
The process of parsing the corresponding mapper xml when the tag is parsed will be parsed into an XMLMapperBuilder object, but the parsing process has not been analyzed in detail. Let’s take a look at this article. As a supplement, the work done is actuallyLoad mapper xml mapping configuration file
.
If you want to learn systematically, you can refer to this article, important! ! ! .
1: Entrance
The source code is as follows:
org . apache . ibatis . builder . xml . XMLConfigBuilder #parseConfiguration
private void parseConfiguration ( XNode root ) {
try {
. . . snip . . .
// <202107161610>
mapperElement ( root . evalNode ( "mappers" ) ) ;
} catch ( Exception e ) {
throw new BuilderException ( "Error parsing SQL Mapper Configuration. Cause: " + e , e ) ;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
<202107161610>
The source code is as follows:
org . apache . ibatis . builder . xml . XMLConfigBuilder #mapperElement
private void mapperElement ( XNode parent ) throws Exception {
if ( parent != null ) {
for ( XNode child : parent . getChildren ( ) ) {
// There are packages, such as configuration :
/*
<mappers>
<package name="yudaosourcecode.mybatis.mapper"/>
</mappers>
*/
if ( "package" . equals ( child . getName ( ) ) ) {
// Get the value of name. Namely package name
String mapperPackage = child . getStringAttribute ( "name" ) ;
// <202108101735>
configuration . addMappers ( mapperPackage ) ;
} else {
// Get resource, such as <mapper resource="mybatis/mapper/TestTypehandlerMapper.xml"/ >
String resource = child .getStringAttribute ( "resource" ) ;
// Get url
String url = child . getStringAttribute ( "url" ) ;
// Get class
String mapperClass = child . getStringAttribute ( "class" ) ;
// Prioritize resource processing, which is class path in this case resource
if ( resource != null && url == null && mapperClass == null ) {
ErrorContext . instance () . resource ( resource ) ;
// get resource
InputStream inputStream = Resources . getResourceAsStream ( resource ) ;
// create XMLMapperBuilder
// <202107161617>
XMLMapperBuilder mapperParser = new XMLMapperBuilder ( inputStream , configuration , resource , configuration . getSqlFragments ( ) ) ;
/ / Execute parsing. After parsing, you will know which interface class corresponds to mapper xml is
// <202107161724>
mapperParser . parse ( ) ;
} else if ( resource == null && url != null && mapperClass == null ) { // handle url case
ErrorContext . instance ( ) . resource ( url ) ;
// get resource
InputStream inputStream according to url = Resources . getUrlAsStream ( url ) ;
// create XMLMapperBuilder
XMLMapperBuilder mapperParser = new XMLMapperBuilder ( inputStream , configuration , url , configuration . getSqlFragments ( ) ) ;
// perform parsing
mapperParser . parse ( ) ;
} else if ( resource == null && url == null && mapperClass != null ) { / / Directly specified class // Get the class
Class of the mapped mapper interface
< ? > mapperInterface= Resources . classForName ( mapperClass ) ;
// add mapping
configuration . addMapper ( mapperInterface ) ;
} else {
throw new BuilderException ( "A mapper element may only specify a url, resource or class, but not more than one." ) ;
}
}
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
<202107161617>
The source code of the constructor function is as follows:
public XMLMapperBuilder ( InputStream inputStream , Configuration configuration , String resource , Map < String , XNode > sqlFragments ) {
// XPathParser: is a tool for parsing xml based on xpath, in the parsing package of mybatis, it belongs to a function of parsing module
// configuration .getVariables(): Information configured through the <properties/> tag in the global configuration file
// <202107161626>
this ( new XPathParser ( inputStream , true , configuration . getVariables ( ) , new XMLMapperEntityResolver ( ) ) ,
configuration , resource , sqlFragments ) ;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
<202107161626>
The source code is as follows:
/*
parser: a tool class for parsing xml based on xpath
configuration: The configuration class obtained by parsing the global config configuration file
resource: location of mapper xml
sqlFragments: a collection of sql statement segments that store the configuration of the <sql> tag
*/
private XMLMapperBuilder ( XPathParser parser , Configuration configuration , String resource , Map < String , XNode > sqlFragments ) {
super ( configuration ) ;
// Create MapperBuilderAssistant object
this . builderAssistant = new MapperBuilderAssistant ( configuration , resource ) ;
this . parser = parser;
this . sqlFragments = sqlFragments ;
this . resource = resource ;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
<202107161724>
The place is to perform parsing, for specific reference2: XMLMapperBuilder#parse
.<202108101735>
is configured in the global configuration file through<package>
Directly configure the package path to be scanned, which may be configured as follows:
< mappers >
< package name = " yudaosourcecode.mybatis.annotation " />
</ mappers >
- 1
- 2
- 3
This configuration will not only parse to mapper xml, but also parse to mapper interface configured using annotationsMapper xml is not needed at this point
, this article will analyze the former, and you can refer to this article for the latter .
2: XMLMapperBuilder#parse
The source code is as follows:
org . apache . ibatis . builder . xml . XMLMapperBuilder #parse
public void parse ( ) {
// <202107161747>
if ( ! configuration . isResourceLoaded ( resource ) ) {
// <202107161755>
// parse the <mapper/> node
configurationElement ( parser . evalNode ( "/mapper" ) ) ;
// mark the resource as loaded
/*
org.apache.ibatis.session.Configuration#addLoadedResource
public void addLoadedResource(String resource) {
// protected final Set<String> loadedResources = new HashSet<String>();
loadedResources.add(resource);
}
*/
configuration . addLoadedResource ( resource ) ;
// The jdk dynamic proxy implementation class that binds the namespace to the mapper interface
// <202107301547>
bindMapperForNamespace ( ) ;
}
// Note that the following pending means that there is a parsing in the previous parsing To the half-failed node, it may be because some dependent nodes have not been resolved to cause
// so repeat the analysis here again
// parsing the pending resultMap node
parsePendingResultMaps ( ) ;
// parsing the pending cache-ref node
parsePendingChacheRefs ( ) ;
// parsing Pending statement node
parsePendingStatements ( ) ;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
<202107161747>
The point is to judge whether the mapp xml resource has been loaded. The source code is as follows:
org . apache . ibatis . session . Configuration #isResourceLoaded
public boolean isResourceLoaded ( String resource ) {
// protected final Set<String> loadedResources = new HashSet<String>();
// Store the location corresponding to the loaded resources through the set collection info
return loadedResources . contains ( resource ) ;
}
- 1
- 2
- 3
- 4
- 5
- 6
<202107161755>
The place is to parse the mapper node, for specific reference2.1: mapper node analysis
,<202107301547>
The place is to bind the corresponding namespace of mapper xml to the specific jdk dynamic proxy of the mapper interface. The source code is as follows:
org . apache . ibatis . builder . xml . XMLMapperBuilder #bindMapperForNamespace
private void bindMapperForNamespace ( ) {
// Get the namespace, which is actually the interface name of the mapper, which is normally configured like this
Stringnamespace = builderAssistant . getCurrentNamespace ( ) ;
if ( namespace ! = null ) {
Class < ? > boundType = null ;
try {
// Get the bound class type by reflection
boundType = Resources . classForName ( namespace ) ;
} catch ( ClassNotFoundException e ) {
}
if ( boundType != null ) {
// if no current mapper information exists
// protected MapperRegistry mapperRegistry = new MapperRegistry(this);
if ( ! configuration . hasMapper ( boundType ) ) {
// prevent repeated loading
// protected final Set<String> loadedResources = new HashSet<String>();
configuration.addLoadedResource ( //"namespace:" + namespace ) ;
// Set the type of the map
// protected MapperRegistry mapperRegistry = new MapperRegistry(this);
// The boundType here is the fully qualified name of the mapper interface, and based on this, the jdk dynamic proxy implementation class will be generated later
/ / 2021-09-09 17:47:57
configuration.addMapper ( boundType ) ; } } } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
2021-09-09 17:47:57
The place is to generate the jdk dynamic proxy implementation class of mapper, and then add it to the mapping register. The source code is as follows:
org . apache . ibatis . session . Configuration #addMapper
public < T > void addMapper ( Class < T > type ) {
mapperRegistry . addMapper ( type ) ;
}
org . apache . ibatis . binding . MapperRegistry #addMapper
public < T > void addMapper ( Class < T > type ) {
// Processed only if it is an interface
if ( type . isInterface ( ) ) {
if ( hasMapper ( type ) ) {
throw new BindingException ( "Type " + type + " is already known to the MapperRegistry." ) ;
}
boolean loadCompleted = false ;
try {
// Create a dynamic proxy factory class for the interface and add mapping information
// private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();
// 2021-09-09 17:47:57
knownMappers . put ( type , new MapperProxyFactory < T > ( type ) ) ;
. . . snip . . .
} finally {
if ( ! loadCompleted ) {
knownMappers . remove ( type ) ;
}
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
2021-09-09 17:47:57
MapperProxyFactory is the factory class used to generate mapper dynamic proxy, for specific reference3: MapperProxyFactory
.
2.1: mapper node analysis
The source code is as follows:
org . apache . ibatis . builder . xml . XMLMapperBuilder #parse
public void parse ( ) {
if ( ! configuration . isResourceLoaded ( resource ) ) {
// <202107171110>
configurationElement ( parser . evalNode ( "/mapper" ) ) ;
. . . snip . . .
}
. . .snip . . .
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
<202107171110>
The source code is as follows:
private void configurationElement ( XNode context ) {
try {
// get namespace
// <mapper namespace="yudaosourcecode.mybatis.mapper.TestResultmapAndParametermapMapper"><mapper/>
String namespace = context . getStringAttribute ( "namespace" ) ;
// mandatory A namespace is required, if it is not set then an exception
if ( namespace . equals ( "" ) ) {
throw new BuilderException ( "Mapper's namespace cannot be empty" ) ;
}
// Set the current namespace value to
builderAssistant . setCurrentNamespace ( namespace ) ;
// <202107171125>
cacheRefElement ( context . evalNode ( "cache-ref" ) ) ;
// resolve <cache/> tags
// <202107181024>
cacheElement ( context . evalNode ( "cache" ) ) ;
// Parse the parameter map of
parameterMap parameterMapElement ( context . evalNodes ( "/mapper/parameterMap" )) ;
// <202107181124>
// Parse the parameter configuration of the returned result resultMap
resultMapElements ( context . evalNodes ( "/mapper/resultMap" ) ) ;
// <202107301052>
// Parse the sql tag under mapper
sqlElement ( context . evalNodes ( "/mapper/sql" ) ) ;
// <202107301409>
// Parse add insert delete delete change update check select tag
buildStatementFromContext ( context . evalNodes ( "select|insert|update|delete" ) ) ;
} catch (Exception e ) {
throw new BuilderException ( "Error parsing Mapper XML. Cause: " + e , e ) ;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
<202107181024>
at parsing<cache>
label, specific reference2.1.2: Parsing cache tags
.<202107181124>
The place is to parse the resultMap, for specific reference2.1.4: Parse the resultMap node
.<202107301052>
The place is to parse the sql tag, specific reference2.1.5: Parse sql tags
.<202107301409>
The place is to parse additions, deletes, modify and check tags, for specific reference2.1.6: Parse CRUD tags
.
<202107171125>
is parsing<cache-ref>
The source code is as follows:
org . apache . ibatis . builder . xml . XMLMapperBuilder #cacheRefElement
private void cacheRefElement ( XNode context ) {
if ( context != null ) {
// builderAssistant.getCurrentNamespace(): the namespace of the current mapper xml
// context.getStringAttribute("namespace "), <cache-ref namepsace="xxx.yyy">
// The namepsace configured in the node
// Add the reference relationship of this cache to the configured map
// protected final Map<String, String> cacheRefMap = new HashMap< String, String>();
configuration . addCacheRef ( builderAssistant . getCurrentNamespace ( ) , context . getStringAttribute ( "namespace" ) ) ;
// Use the parameter builderAssistant, and the namespace of the current mapper xml to create
// CacheRefResolver object
CacheRefResolver cacheRefResolver = new CacheRefResolver ( builderAssistant , context . getStringAttribute ( "namespace" ) ) ;
try {
// resolve cache-ref
// <202107180955>
cacheRefResolver . resolveCacheRef ( );
} catch ( IncompleteElementException e ) {
configuration . addIncompleteCacheRef ( cacheRefResolver ) ;
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
<202107180955>
reference2.1.1: Parse cache-ref
.
2.1.1: Parse cache-ref
The source code is as follows:
org . apache . ibatis . builder . CacheRefResolver #resolveCacheRef
public Cache resolveCacheRef ( ) {
return assistant . useCacheRef ( cacheRefNamespace ) ;
}
org . apache . ibatis . builder . MapperBuilderAssistant #useCacheRef
public Cache useCacheRef ( String namespace ) {
// If the namespace of the reference cache is null, throw an exception and tell you that namepsace is required
// Exception information of the attribute
// as written <cache-ref> but no namespace attribute is specified, the exception here will be triggered
if ( namespace == null ) {
throw new BuilderException ( "cache-ref element requires a namespace attribute." ) ;
}
try {
unresolvedCacheRef = true ;
// Get the cache from the configuration through the namespace, Cache objects are all Cache in namespace units.
cache = configuration . getCache ( namespace ) ; //
If the referenced cache is not obtained according to the namespace, an exception will be thrown
if ( cache == null ) {
throw new IncompleteElementException ( "No cache for namespace '" + namespace + "' could be found." ) ;
}
currentCache = cache ;
unresolvedCacheRef = false ;
return cache;
} catch ( IllegalArgumentException e ) {
throw new IncompleteElementException ( "No cache for namespace '" + namespace + "' could be found." , e ) ;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
2.1.2: Parsing cache tags
The source code is as follows:
org . apache . ibatis . builder . xml . XMLMapperBuilder #cacheElement
private void cacheElement ( XNode context ) throws Exception {
if ( context != null ) {
// Get the cache type used by the secondary cache, the default is PERPETUAL, which is org.apache .ibatis.cache.impl.PerpetualCache (default provided by the system)
// You can also customize
String by implementing the interface org.apache.ibatis.cache.Cache type = context . getStringAttribute ( "type" , "PERPETUAL") ;
// resolve class
Class < ? extends Cache > typeClass = typeAliasRegistry . resolveAlias ( type ) ;
// Get cache elimination algorithm, default LRU, or FIFO, WEAK, SOFT
String eviction = context . getStringAttribute ( "eviction" , "LRU" ) ;
// Get the class
Class corresponding to the elimination algorithm < ? extends Cache > evictionClass = typeAliasRegistry . resolveAlias ( eviction) ;
// Get the flush interval of the cache, the default is not flushing
Long flushInterval = context . getLongAttribute ( "flushInterval" ) ;
// Get the size of the cache
Integer size = context . getIntAttribute ( "size" ) ;
// Get the cache read-only , read-only always returns the same instance, but not safe, non-read-only returns a different instance
// but safe, so the default value is false, which is not read-only (note: read-only here is not really read-only, but Said to always be an object, only read-only)
boolean readWrite = ! context . getBooleanAttribute ( "readOnly" , false ) ;
// Get all child node information, name->value
Properties props = context . getChildrenAsProperties ( ) ;
// <202107181039>
// Add new cache
builderAssistant . useNewCache ( typeClass , evictionClass , flushInterval , size , readWrite , props ) ;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
<202107181039>
The place is to add a new cache, specific reference2.1.3: Add namespace cache
.
2.1.3: Add namespace cache
The source code is as follows:
org . apache . ibatis . builder . MapperBuilderAssistant #useNewCache
public Cache useNewCache ( Class < ? extends Cache > typeClass ,
Class < ? extends Cache > evictionClass ,
Long flushInterval ,
Integer size ,
boolean readWrite ,
Properties props ) {
// Get the cache type class, if provided, use provided, otherwise use PerpetualCache
typeClass= valueOrDefault ( typeClass , PerpetualCache . class ) ;
// The class of the elimination algorithm, if provided, use the provided one, otherwise use LruCache . class
evictionClass = valueOrDefault ( evictionClass , LruCache . class ) ; // Construct the cache object
Cache through the cache constructor
cache = new CacheBuilder ( currentNamespace ) . implementation ( typeClass ) . addDecorator ( evictionClass ) . clearInterval (
flushInterval )
. size ( size )
. readWrite ( readWrite )
. properties ( props )
. build ( ) ;
// Add to the change collection of the configuration class
/*
org.apache.ibatis.session.Configuration#addCache
public void addCache(Cache cache) {
caches.put(cache.getId(), cache);
}
*/
configuration . addCache ( cache ) ;
// assign to the current cache
currentCache = cache ;
// return the cache
return cache ;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
2.1.4: Parse the resultMap node
The source code is as follows;
org . apache . ibatis . builder . xml . XMLMapperBuilder #resultMapElements
private void resultMapElements ( List < XNode > list ) throws Exception {
// loop through all <resultMap/> nodes
for ( XNode resultMapNode : list ) {
try {
// < 202107181410>
resultMapElement ( resultMapNode ) ;
} catch (IncompleteElementException e ) {
// ignore, it will be retried
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
<202107181410>
The source code is as follows:
org . apache . ibatis . builder . xml . XMLMapperBuilder # resultMapElement ( org . apache . ibatis . parsing . XNode )
private ResultMap resultMapElement ( XNode resultMapNode ) throws Exception {
return resultMapElement ( resultMapNode , Collections . < ResultMapping > emptyList ( )) ;
}
private ResultMap resultMapElement ( XNode resultMapNode , List < ResultMapping > additionalResultMappings ) throws Exception {
ErrorContext . instance ( ) . activity ( "processing " + resultMapNode . getValueBasedIdentifier ( ) ) ;
// get the value of the id attribute
String id = resultMapNode . getStringAttribute ( " id" ,
resultMapNode.getValueBasedIdentifier ( ) ) ; // get the value of the type attribute String type = resultMapNode . getStringAttribute ( "type" ,
resultMapNode . getStringAttribute ( "ofType" , resultMapNode
. getStringAttribute ( " resultType" ,
resultMapNode . getStringAttribute ( "javaType" ) ) ) ) ; // Get the value of extend, that is, the inherited resultMap String extend =
resultMapNode . getStringAttribute ( "extends" ) ;
// Get the value of autoMapping, that is, whether the result column that is not configured in resultMap is automatically mapped
Boolean autoMapping = resultMapNode . getBooleanAttribute ( "autoMapping" ) ;
// Create the type corresponding to the type configured in resultMap class class type object
Class < ? > typeClass = resolveClass ( type ) ;
Discriminator discriminator = null ;
List < ResultMapping > resultMappings = new ArrayList < ResultMapping > ( ) ;
resultMappings . addAll ( additionalResultMappings ) ;
List < XNode > resultChildren = resultMapNode . getChildren ( ) ;
// Get the child nodes of resultMap and loop through the child nodes
for ( XNode resultChild : resultChildren ) {
// <202107191613 >
// if <constructor> tag
if ( "constructor" . equals ( resultChild .getName ( ) ) ) {
processConstructorElement ( resultChild , typeClass , resultMappings ) ;
} else if ( "discriminator" . equals ( resultChild . getName ( ) ) ) {
// Parse the <discriminator> tag, discriminator, to depend on a column to dynamically assign values to properties
// <202107211750>
discriminator = processDiscriminatorElement ( resultChild , typeClass , resultMappings ) ;
} else { // Here, the description is <id>, <result>, <collection>, <association> and other tags
ArrayList < ResultFlag > flags = new ArrayList < ResultFlag > ( ) ;
// If it is <id> tag, Then add ResultFlag.ID enumeration to flags
if ( "id" . equals ( resultChild . getName ( ) ) ) {
flags . add ( ResultFlag . ID ) ;
}
// <202107211806>
resultMappings.add ( buildResultMappingFromContext ( resultChild , typeClass , flags ) ) ; } } // The constructor is as follows: / *
public ResultMapResolver(MapperBuilderAssistant assistant, String id, Class<?> type, String extend, Discriminator discriminator, List<ResultMapping> resultMappings, Boolean autoMapping) {
// map helper class
this.assistant = assistant;
this.id = id;
// entity type
this.type = type;
// Inherited resultMap information
this.extend = extend;
// discriminator
this.discriminator = discriminator;
// All internal node information
this.resultMappings = resultMappings;
// Whether to automatically map unconfigured properties
this.autoMapping = autoMapping;
}
*/
ResultMapResolver resultMapResolver = new ResultMapResolver ( builderAssistant , id , typeClass , extend , discriminator , resultMappings , autoMapping ) ;
try {
// <202107211821>
return resultMapResolver . resolve ( ) ;
} catch ( IncompleteElementException e ) {
configuration . addIncompleteResultMap (resultMapResolver ) ;
throw e ;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
<202107191613>
Here is the constructor sub-tag that parses the resultMap. The source code is as follows:
org . apache . ibatis . builder . xml . XMLMapperBuilder #processConstructorElement
private void processConstructorElement ( XNode resultChild , Class < ? > resultType , List < ResultMapping > resultMappings ) throws Exception {
// Get all child nodes, the following possible configurations:
/*
<resultMap id="myResultMap" type="yudaosourcecode.mybatis.model.TestResultmapConstructor">
<constructor>
<idArg javaType="integer" jdbcType="INTEGER" column="id"/>
<arg javaType="string" jdbcType="VARCHAR" column="myname"/>
<arg javaType="integer" jdbcType="INTEGER" column="myage"/>
</constructor>
</resultMap>
*/
List < XNode > argChildren = resultChild . getChildren ( ) ;
for ( XNode argChild : argChildren ) {
// ResultFlag is an enumeration class, as follows:
/*
org.apache.ibatis.mapping.ResultFlag
public enum ResultFlag {
ID, CONSTRUCTOR
}
*/
ArrayList < ResultFlag > flags = new ArrayList < ResultFlag > ( ) ;
// first add ResultFlag.CONSTRUCTOR
flags . add ( ResultFlag . CONSTRUCTOR ) ;
// if idArg, then add ResultFlag.ID
if ( "idArg" . equals ( argChild . getName ( ) ) ) {
flags . add ( ResultFlag . ID );
}
// <202107191636>
// Construct the current child node into a ReusltMapping object and add it to the resultMappings collection
resultMappings . add ( buildResultMappingFromContext ( argChild , resultType , flags ) ) ;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
<202107191636>
wherebuildResultMappingFromContext
Used to build ResultMapping objects, specific reference2.2: Build ResultMapping
.<202107211750>
The discriminator, the source code is as follows:
org . apache . ibatis . builder . xml . XMLMapperBuilder #processDiscriminatorElement
private Discriminator processDiscriminatorElement ( XNode context , Class < ? > resultType , List < ResultMapping > resultMappings ) throws Exception {
// Get various attributes
String column = context . getStringAttribute ( "column " ) ;
String javaType = context . getStringAttribute ( "javaType" ) ;
String jdbcType = context . getStringAttribute ( "jdbcType " ) ;
String typeHandler = context . getStringAttribute ( "typeHandler" ) ;
Class < ? > javaTypeClass = resolveClass ( javaType ) ;
@SuppressWarnings ( "unchecked" )
Class< ? extends TypeHandler < ? > > typeHandlerClass = ( Class < ? extends TypeHandler < ? > > ) resolveClass ( typeHandler ) ;
JdbcType jdbcTypeEnum = resolveJdbcType ( jdbcType ) ;
Map < String , String > discriminatorMap = new HashMap < String , String >( ) ;
for ( XNode caseChild : context . getChildren ( ) ) {
// Get the value of the corresponding column of case processing
String value = caseChild . getStringAttribute ( "value" ) ;
// Get the resultMap used
String resultMap = caseChild . getStringAttribute ( "resultMap" , processNestedResultMappings ( caseChild , resultMappings ) ) ;
discriminatorMap . put ( value , resultMap ) ;
}
// construct Discriminator
return builderAssistant . buildDiscriminator ( resultType , column , javaTypeClass , jdbcTypeEnum , typeHandlerClass , discriminatorMap ) ;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
<202107211806>
The buildResultMappingFromContext is to build a ResultMapping object, refer to2.2: Build ResultMapping
.<202107211821>
specific reference2.3: Parse the resultMap tag as a ResultMap object
.
2.1.5: Parse sql tags
The source code is as follows:
org . apache . ibatis . builder . xml . XMLMapperBuilder #sqlElement
// The list parameter is all sql node information, because multiple mappers can be configured in a namespace, so it is a collection
private void sqlElement ( List < XNode > list ) throws Exception {
// The datbaseid here is generally obtained from the connection information of the database in mybatis
// In addition, the databaseId attribute can also be configured in the sql node
if ( configuration . getDatabaseId ( ) != null ) {
sqlElement ( list , configuration.getDatabaseId ( ) ) ; } // <202107301107 > sqlElement ( list , null ) ; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
<202107301107>
The source code is as follows:
org . apache . ibatis . builder . xml . XMLMapperBuilder # sqlElement ( java . util . List < org . apache . ibatis . parsing . XNode > , java . lang . String )
private void sqlElement ( List < XNode > list , String requiredDatabaseId ) throws Exception {
// Loop through all nodes and process them in sequence
for ( XNode context : list ) {
// Get the value of the databaseId attribute of the node configuration
String databaseId = context . getStringAttribute ( "databaseId" ) ;
// Get the value of the node id attribute
String id = context . getStringAttribute ( "id" ) ;
// Add namespace prefix to generate complete id information, because the id of different namespaces can be repeated, so you need to add namespace prefix
id = builderAssistant . applyCurrentNamespace ( id , false ) ;
// <202107301123>
if ( databaseIdMatchesCurrent ( id , databaseId , requiredDatabaseId ) ) sqlFragments . put ( id , context ) ;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
<202107301123>
wheredatabaseIdMatchesCurrent
The source code is as follows:
org . apache . ibatis . builder . xml . XMLMapperBuilder #databaseIdMatchesCurrent
// The overall logic of the method is complex, you can know the logic
private boolean databaseIdMatchesCurrent ( String id , String databaseId , String requiredDatabaseId ) {
// If the global databseId has value
if ( requiredDatabaseId != null ) {
// If it is not equal to the databseId property configuration, return false
if ( ! requiredDatabaseId . equals( databaseId ) ) {
return false ;
}
} else {
// if there is no global databaseId, but the databaseId property is set, return false
if ( databaseId != null ) {
return false ;
}
// if the sql is already included
if ( this . sqlFragments . containsKey ( id ) ) {
// get the contained sql
XNode context = this . sqlFragments . get ( id );
// Get the configured databaseId attribute from the sql node information already contained, and return false
if configured if ( context . getStringAttribute ( "databaseId" ) != null ) {
return false ;
}
}
}
// Finally return true directly
return true ;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
<202107301123>
wheresqlFragments
Defined as follows:
// key: sql node id with namespace, value: node object corresponding to sql node
private Map < String , XNode > sqlFragments ;
- 1
- 2
2.1.6: Parse CRUD tags
The source code is as follows:
org . apache . ibatis . builder . xml . XMLMapperBuilder # buildStatementFromContext ( java . util . List < org . apache . ibatis . parsing . XNode > )
private void buildStatementFromContext ( List < XNode > list ) {
// databaseId if requested
if ( configuration.getDatabaseId _ _( ) != null ) {
buildStatementFromContext ( list , configuration . getDatabaseId ( ) ) ;
}
// <202107301506>
// build expression
buildStatementFromContext ( list , null ) ;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
<202107301506>
The place is to build the statement, the source code is as follows:
org . apache . ibatis . builder . xml . XMLMapperBuilder # buildStatementFromContext ( java . util . List < org . apache . ibatis . parsing . XNode > , java . lang . String )
private void buildStatementFromContext ( List < XNode > list , String requiredDatabaseId) {
// Traverse the <insert><delete><update><select> nodes in turn and perform parsing
for ( XNode context : list ) {
// <202107301526>
final XMLStatementBuilder statementParser = new XMLStatementBuilder ( configuration , builderAssistant , context , requiredDatabaseId ) ;
try {
statementParser . parseStatementNode ( ) ;
} catch ( IncompleteElementException e ) {
// If the parsing fails, add it to the incomplete statement collection, and try to parse it again later
// protected final Collection<XMLStatementBuilder> incompleteStatements = new LinkedList<XMLStatementBuilder>();
configuration . addIncompleteStatement ( statementParser ) ;
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
<202107301526>
The place is to create XMLStatementBuilder and perform specific parsing, refer to this article for details .
2.2: Build ResultMapping
The source code is as follows:
org . apache . ibatis . builder . xml . XMLMapperBuilder #buildResultMappingFromContext
private ResultMapping buildResultMappingFromContext ( XNode context , Class < ? > resultType , ArrayList < ResultFlag > flags ) throws Exception {
// Get various attributes
String property = context . getStringAttribute ( "property" ) ;
String column = context . getStringAttribute ( "column" ) ;
String javaType = context . getStringAttribute ( "javaType" ) ;
String jdbcType = context . getStringAttribute ( "jdbcType " ) ;
String nestedSelect = context . getStringAttribute ( "select" ) ;
String nestedResultMap = context .getStringAttribute ( "resultMap" ,
processNestedResultMappings ( context , Collections . < ResultMapping > emptyList ( ) ) ) ;
String notNullColumn = context . getStringAttribute ( "notNullColumn" ) ;
String columnPrefix = context . getStringAttribute ( "columnPrefix" ) ;
String typeHandler = context .getStringAttribute ( "typeHandler" ) ;
String resulSet = context . getStringAttribute ( "resultSet" ) ;
String foreignColumn = context . getStringAttribute ( "foreignColumn" ) ;
boolean lazy = "lazy" . equals ( context . getStringAttribute ( "fetchType" , configuration . isLazyLoadingEnabled ( ) ? "lazy" : "eager" ) ) ;
// resolve javaType to corresponding class
Class < ? > javaTypeClass = resolveClass ( javaType ) ;
// resolve typeHandler to corresponding class @SuppressWarnings
( " unchecked" )
Class < ? extends TypeHandler < ? > > typeHandlerClass = ( Class < ? extends TypeHandler < ? > > ) resolveClass ( typeHandler) ;
,// resolve jdbcType to the corresponding org.apache.itabis.type.JdbcType enumeration class
JdbcType jdbcTypeEnum = resolveJdbcType ( jdbcType ) ;
// <202107191647>
return builderAssistant . buildResultMapping ( resultType , property , column , javaTypeClass , jdbcTypeEnum , nestedSelect , nestedResultMap , notNullColumn , columnPrefix , typeHandlerClass , flags , resulSetforeignColumn ,lazy ) ;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
<202107191647>
The first step is to construct ResultMapping through the mapping construction helper class. The source code is as follows:
org . apache . ibatis . builder . MapperBuilderAssistant #buildResultMapping
public ResultMapping buildResultMapping (
Class < ? > resultType ,
String property ,
String column ,
Class < ? > javaType ,
JdbcType jdbcType ,
String nestedSelect ,
String nestedResultMap ,
String notNullColumn ,
StringcolumnPrefix ,
Class < ? extends TypeHandler < ? > > typeHandler ,
List < ResultFlag > flags ,
String resultSet ,
String foreignColumn ,
boolean lazy ) {
// resolve java type class
// <202107191656>
Class < ? > javaTypeClass = resolveResultJavaType ( resultType , property , javaType ) ;
// Resolve a type handler class (database <->pojo property handler)
// <202107191704> TypeHandler
< ?> typeHandlerInstance = resolveTypeHandler ( javaTypeClass , typeHandler ) ; // resolve the column property in the <constructor> tag // <202107211419> List < ResultMapping > composites = parseCompositeColumnName ( column ) ; // If column is configured, assign the value of column to null if ( composites . size ( ) > 0 ) column = null
;
// configuration: configuration object
// property: the property property value of the
current label // column: the column property value of the
current label // javaTypeClass: the class corresponding to the javaType of the current label
// Create the result mapping builder object
ResultMapping . Builder builder = new ResultMapping . Builder ( configuration , property , column , javaTypeClass ) ;
// Set various properties
builder . jdbcType ( jdbcType ) ;
// Set the embedded query id, that is, the id of other statements, and finally generate a named The id of the space
// Parse the select attribute, that is, the statement of the other or its own namespace that is referenced
// Such as <collection ... select="yudaosourcecode.mybatis.mapper.TestResultmapCollectionManyMapper.getById1" .../>
// I think since it is a reference to other statements, then write the full path regardless of whether it is consistent with the current namespace It will be very clear, the code is as follows:
/*
org.apache.ibatis.builder.MapperBuilderAssistant#applyCurrentNamespace
public String applyCurrentNamespace(String base, boolean isReference) {
if (base == null) return null;
if (isReference) {
// is it qualified with any namespace yet?
if (base.contains(".")) return base;
} else {
// is it qualified with this namespace yet?
if (base.startsWith(currentNamespace + ".")) return base;
if (base.contains(".")) throw new BuilderException("Dots are not allowed in element names, please remove it from " + base);
}
return currentNamespace + "." + base;
}
*/
builder . nestedQueryId ( applyCurrentNamespace ( nestedSelect , true ) ) ;
// set the embedded resultMap id (with namespace)
// eg: <collection .. resultMap="xxx.yyy.zzz.someOtherResultMapId" ... />
builder . nestedResultMapId ( applyCurrentNamespace ( nestedResultMap , true ) ) ;
builder . resultSet ( resultSet ) ;
builder . typeHandler ( typeHandlerInstance ) ;
builder . flags ( flags == null ? new ArrayList < ResultFlag > ( ) : flags ) ;
builder . composites ( composites ) ;
builder . notNullColumns ( parseMultipleColumnNames ( notNullColumn ) ) ;
builder . columnPrefix ( columnPrefix ) ;
builder . foreignColumn (foreignColumn ) ;
builder . lazy ( lazy ) ;
// construct a ResultMapping object and return
return builder . build ( ) ;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
<202107191656>
The corresponding java type of service analysis, the source code is as follows:
org . apache . ibatis . builder . MapperBuilderAssistant #resolveResultJavaType
private Class < ? > resolveResultJavaType ( Class < ? > resultType , String property , Class < ? > javaType ) {
// If the incoming javaType and property are not null
if ( javaType == null && property != null ) {
try {
// Through the reflection package tool class, get the class type of the property
MetaClass metaResultType = MetaClass . forClass ( resultType ) ;
javaType = metaResultType . getSetterType ( property ) ;
} catch ( Exception e ) {
//ignore, following null check statement will deal with the situation
}
}
// If this javaType is still null, assign the value to Object.class
if ( javaType == null ) {
javaType = Object.class ; } return javaType ;
} _
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
<202107191704>
The source code is as follows:
org . apache . ibatis . builder . BaseBuilder #resolveTypeHandler
protected TypeHandler < ? > resolveTypeHandler ( Class < ? > javaType , Class < ? extends TypeHandler < ? > > typeHandlerType ) {
// is null, indicating that the user has not set type handler for this mapping Handler , return null directly
if ( typeHandlerType == null ) return null ;
// Get the type handler from the type handler register
TypeHandler < ? > handler = typeHandlerRegistry . getMappingTypeHandler ( typeHandlerType ) ;
if ( handler == null ) {
// If there is no type handler registry, instantiate a
handler = typeHandlerRegistry . getInstance ( javaType , typeHandlerType ) ;
}
return handler ;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
<202107211419>
at parsingThe column attribute in <collection> in <resultMap>
, the source code is as follows:
org . apache . ibatis . builder . MapperBuilderAssistant #parseCompositeColumnName
private List < ResultMapping > parseCompositeColumnName ( String columnName ) {
List < ResultMapping > composites = new ArrayList < ResultMapping > ( ) ;
// Associated query configures multiple mappings, possible values "{oneid =id,outerManyName=manyname}"
// if contains "=", and contains ","
if ( columnName != null && ( columnName . indexOf ( '=' ) > - 1 || columnName . indexOf ( ',' ) > - 1 ) ) {
// Respectively parse the attribute mapping relationship configured at the place
StringTokenizer parser = new StringTokenizer ( columnName , "{}= , " , false ) ;
while ( parser . hasMoreTokens ( ) ) {
String property = parser. nextToken ( ) ;
String column = parser . nextToken ( ) ;
ResultMapping . Builder complexBuilder = new ResultMapping . Builder ( configuration , property , column , configuration . getTypeHandlerRegistry ( ) . getUnknownTypeHandler ( ) ) ;
// Create a result map object and add it to The collection to return
composites.add (complexBuilder . build ( ) ) ;
}
}
return composites ;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
2.3: Parse the resultMap tag as a ResultMap object
The source code is as follows:
org . apache . ibatis . builder . ResultMapResolver #resolve
public ResultMap resolve ( ) {
return assistant . addResultMap ( this . id , this . type , this . extend , this . discriminator , this . resultMappings , this . autoMapping ) ;
}
org . apache . ibatis . builder . MapperBuilderAssistant #addResultMap
public ResultMap addResultMap (
String id ,
Class < ? > type ,
String extend ,
Discriminator discriminator ,
List < ResultMapping > resultMappings ,
Boolean autoMapping ) {
// Add mapper xml namespace prefix
id = applyCurrentNamespace ( id, false ) ;
// Add the namespace prefix of mapper xml to
extend extend = applyCurrentNamespace ( extend , true ) ;
// ResultMap constructor
ResultMap . Builder resultMapBuilder = new ResultMap . Builder ( configuration , id , type , resultMappings , autoMapping ) ;
/ / if there is an extend
if ( extend != null ) {
// check if there is a resultMap of the extend, if not, an exception
if ( ! configuration . hasResultMap ( extend ) ) {
throw new IncompleteElementException ( "Could not find a parent resultmap with id '" + extend + "'" ) ;
}
// Get the ResultMap corresponding to the extend from the configuration
ResultMap resultMap = configuration . getResultMap ( extend ) ;
// Get all ResultMappings
List in the inherited ResultMap < ResultMapping > extendedResultMappings = new ArrayList < ResultMapping > ( resultMap . getResultMappings ( ) ) ;
// Delete the inherited
extendedResultMappings . removeAll ( resultMappings ) ;
// Determine if the current ResultMap declares <construcotr>
boolean declaresConstructor = false ;
for ( ResultMapping resultMapping : resultMappings ) {
// declared, set the flags to true
if ( resultMapping . getFlags ( ) .contains ( ResultFlag . CONSTRUCTOR ) ) {
declaresConstructor = true ;
break ;
}
}
// contains <constructor> declarations, cyclically inherited, delete declarations inside
if ( declaresConstructor ) {
Iterator < ResultMapping > extendedResultMappingsIter = extendedResultMappings . iterator ( ) ;
while ( extendedResultMappingsIter.hasNext ( ) ) { if ( _ _
extendedResultMappingsIter . next ( ) . getFlags ( ) . contains ( ResultFlag . CONSTRUCTOR ) ) {
extendedResultMappingsIter . remove ( ) ;
}
}
}
// Add inherited ResultMapping information to current
resultMappings . addAll ( extendedResultMappings ) ;
}
resultMapBuilder . discriminator ( discriminator ) ;
// Construct ResultMap
ResultMap resultMap = resultMapBuilder . build ( ) ;
// Add the ResultMap object corresponding to the <resultMap/> tag to the configuration
configuration . addResultMap ( resultMap ) ;
// Return the parsed ResultMap object
return resultMap ;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
3: MapperProxyFactory
This is a factory class used to generate the mapper interface jdk dynamic proxy. The source code is as follows:
org . apache . ibatis . binding . MapperProxyFactory
public class MapperProxyFactory < T > {
// mapper interface
private final Class < T > mapperInterface ;
private final Map < Method , MapperMethod > methodCache = new ConcurrentHashMap < Method , MapperMethod > ( ) ;
public MapperProxyFactory ( Class < T > mapperInterface ) {
this . mapperInterface = mapperInterface ;
}
// Get the proxy's mapper interface
public Class < T > getMapperInterface ( ) {
return mapperInterface ;
}
public Map < Method , MapperMethod > getMethodCache ( ) {
return methodCache ;
}
// Create mapper interface proxy class
@SuppressWarnings ( "unchecked" )
protected T newInstance ( MapperProxy < T > mapperProxy ) {
return ( T ) Proxy . newProxyInstance ( mapperInterface . getClassLoader ( ) , new Class [ ] { mapperInterface } , mapperProxy ) ;
}
public T newInstance ( SqlSession sqlSession ) {
// Generate the java.lang.reflect.InvocationHandler class MapperProxy used to execute the method in the jdk dynamic proxy
//, this class is the core class, and the invoke method here will be executed when performing database operations
// 2021-09-09 17:58:56
final MapperProxy < T > mapperProxy = new MapperProxy < T > ( sqlSession , mapperInterface , methodCache ) ;
return newInstance ( mapperProxy ) ;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
2021-09-09 17:58:56
is to createorg.apache.ibatis.bingding.MapperProxy
, which is used to generate the mapper interface dynamic proxy classjava.lang.reflect.InvocationHandler
Implementation class. Let's focus on theinvoke
The implementation of the method, the specific reference3.1: MaperProxy#invoke
.
3.1: MaperProxy#invoke
When we execute the following code:
String resource = "mybatis/mybatis-config-1.xml" ;
InputStream inputStream = Resources . getResourceAsStream ( resource ) ;
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder ( ) . build ( inputStream ) ;
this . sqlSessionFactory = sqlSessionFactory ;
this . sqlSession = this . sqlSessionFactory.openSession _ _( ) ;
this . testExtendAndAutoMappingMapper =
this . sqlSession . getMapper ( TestExtendAndAutoMappingMapper . class ) ;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
When obtaining the implementation class of the mapper interface, what is returned is the generated dynamic proxy class based on jdk. Through the previous analysis, we know that its InvocationHandler usesMaperProxy
, so when the mapper method is called, it will first executeorg.apache.ibatis.binding.MapperProxy#invoke
, the source code is as follows:
org . apache . ibatis . binding . MapperProxy #invoke
public Object invoke ( Object proxy , Method method , Object [ ] args ) throws Throwable {
// toString, equals, hashCode and other methods defined by Object classes
if ( Object . class . equals ( method . getDeclaringClass ( ) ) ) {
try {
// The method of the Object class is called, directly call and return the
return method . invoke ( this , args ) ;
} catch ( Throwable t ) {
throw ExceptionUtil . unwrapThrowable ( t ) ;
}
}
// Create MapperMethod, which encapsulates the interface Information, method information, and configuration object information of the global configuration file
// The source code is as follows:
/*
private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) {
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
*/
final MapperMethod mapperMethod = cachedMapperMethod ( method ) ;
// Execute through MapperMethod object
// 2021-09-10 15:12:30
return mapperMethod . execute ( sqlSession , args ) ;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
2021-09-10 15:12:30
throughorg.apache.ibatis.bingding.MaperMethod
Object execution, specific reference3.2::MapperMethod#execute
.
3.2::MapperMethod#execute
The source code is as follows:
org . apache . ibatis . binding . MapperMethod #execute
public Object execute ( SqlSession sqlSession , Object [ ] args ) {
Object result ;
// According to the corresponding statement type, call the API execution of the session object org.apache.ibatis.session.SqlSession
// Specific database operations
// 2021-09-10 15:17:17
if ( SqlCommandType . INSERT == command . getType ( ) ) {
Object param= method . convertArgsToSqlCommandParam ( args ) ;
result = rowCountResult ( sqlSession . insert ( command . getName ( ) , param ) ) ;
} else if ( SqlCommandType . UPDATE == command . getType ( ) ) {
Object param = method . convertArgsToSqlCommandParam (args ) ;
result = rowCountResult ( sqlSession . update ( command . getName ( ) , param ) ) ;
} else if ( SqlCommandType . DELETE == command . getType ( ) ) {
Object param = method . convertArgsToSqlCommandParam ( args ) ;
result = rowCountResult( sqlSession . delete ( command . getName ( ) , param ) ) ;
} else if ( SqlCommandType . SELECT == command . getType ( ) ) {
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 {
Object param =method . convertArgsToSqlCommandParam ( args ) ;
result = sqlSession . selectOne ( command . getName ( ) , param ) ;
}
} else if ( SqlCommandType . FLUSH == command . getType ( ) ) {
result = sqlSession . flushStatements ( ) ;
} else {
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 ;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
2021-09-10 15:17:17
is to call the session object according to the type of user commandorg.apache.ibatis.session.SqlSession
The different methods of the method will call the subclass of the executor inside the methodorg.apache.ibatis.executor.CachingExecutor
To perform subsequent operations, for this calling process, you can refer to this article .
write on the back
Looking at the source code is really boring! ! ! However, only those who can endure loneliness can achieve something again! !