java Mybatis Plus 启动过程

Mybatis Plus启动过程分析

MybatisPlus很多方法都是直接继承Mybatis的方法进行修改

  • MybatisConfiguration→MybatisMapperRegistry→AutoSqlInjector..injectSqlRunner→MybatisPlusAutoConfiguration..sqlSessionFactory→MybatisSqlSessionFactoryBean..buildSqlSessionFactory→XMLMapperBuilder..parse()→XMLMapperBuilder..configurationElement()→XMLMapperBuilder..buildStatementFromContext()→XMLStatementBuilder..parseStatementNode→MapperBuilderAssistant..addMappedStatement()→configuration.addMappedStatement()
  • MybatisConfiguration实例化,MybatisMapperRegistry实例化注入SqlRunner,MybatisPlusAutoConfiguration实例化并调用sqlSessionFactory()
  • MybatisPlusAutoConfiguration.sqlSessionFactory()
    • MybatisSqlSessionFactoryBean..buildSqlSessionFactory加载自定义MybatisXmlConfigBuilder/MybatisConfiguration并且调用xmlMapperBuilder.parse(){configurationElement();//解析xml},xmlMapperBuilder.parse()调用configurationElement()再调用buildStatementFromContext()解析xml中的sql成Statement,解析成功调用builderAssistant.addMappedStatement(),builderAssistant.addMappedStatement()再调用configuration.addMappedStatement(statement)添加到配置中
    • 返回xmlMapperBuilder.parse(){bindMapperForNamespace();//绑定命名空间等},执行bindMapperForNamespace(){configuration.addMapper(boundType)}调用configuration.addMapper(boundType)调用 mapperRegistry.addMapper(type),mapperRegistry.addMapper(type)中调用MybatisMapperRegistry..addMapper(),MybatisMapperRegistry.addMapper()中调用MybatisMapperAnnotationBuilder..parse()解析无xml注解,注入扩展全局crud,MybatisMapperAnnotationBuilder.parse()调用loadXmlResource()如果已经加载xmlResource不进行再次加载
    • MybatisMapperAnnotationBuilder..parse()中GlobalConfigUtils.getSqlInjector(configuration).inspectInject(assistant, type)注入动态CRUD
  • MybatisSqlSessionFactoryBean..buildSqlSessionFactory执行完初始化逐层返回
  • MybatisMapperRegistry替换原生MapperRegistry调用addMapper()解析Mapper对应的xml文件

MybatisPlus自动配置

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
public class MybatisPlusAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();
factory.setDataSource(dataSource);
factory.setVfs(SpringBootVFS.class);
if (StringUtils.hasText(this.properties.getConfigLocation())) {
factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
}

MybatisConfiguration configuration = this.properties.getConfiguration();
if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) {
configuration = new MybatisConfiguration();
}

if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) {
Iterator i$ = this.configurationCustomizers.iterator();

while(i$.hasNext()) {
ConfigurationCustomizer customizer = (ConfigurationCustomizer)i$.next();
customizer.customize(configuration);
}
}

configuration.setDefaultScriptingLanguage(MybatisXMLLanguageDriver.class);
factory.setConfiguration(configuration);
if (this.properties.getConfigurationProperties() != null) {
factory.setConfigurationProperties(this.properties.getConfigurationProperties());
}

if (!ObjectUtils.isEmpty(this.interceptors)) {
factory.setPlugins(this.interceptors);
}

if (this.databaseIdProvider != null) {
factory.setDatabaseIdProvider(this.databaseIdProvider);
}

if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
}

if (StringUtils.hasLength(this.properties.getTypeEnumsPackage())) {
factory.setTypeEnumsPackage(this.properties.getTypeEnumsPackage());
}

if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
}

if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
factory.setMapperLocations(this.properties.resolveMapperLocations());
}

if (!ObjectUtils.isEmpty(this.properties.getGlobalConfig())) {
factory.setGlobalConfig(this.properties.getGlobalConfig().convertGlobalConfiguration());
}

return factory.getObject(); // ***MybatisSqlSessionFactoryBean***
}
}

拷贝类 org.mybatis.spring.SqlSessionFactoryBean 修改方法 buildSqlSessionFactory()

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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
/**
* <p>
* 拷贝类 org.mybatis.spring.SqlSessionFactoryBean 修改方法 buildSqlSessionFactory()
* 加载自定义 MybatisXmlConfigBuilder
* </p>
*
* @author hubin
* @Date 2017-01-04
*/
package com.baomidou.mybatisplus.spring;
public class MybatisSqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
/**
* {@inheritDoc}
*/
@Override
public SqlSessionFactory getObject() throws Exception {
if (this.sqlSessionFactory == null) {
afterPropertiesSet();
}

return this.sqlSessionFactory;
}
/**
* {@inheritDoc}
*/
@Override
public void afterPropertiesSet() throws Exception {
notNull(dataSource, "Property 'dataSource' is required");
notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
"Property 'configuration' and 'configLocation' can not specified with together");

this.sqlSessionFactory = buildSqlSessionFactory();
}
/**
* Build a {@code SqlSessionFactory} instance.
* <p>
* The default implementation uses the standard MyBatis {@code XMLConfigBuilder} API to build a
* {@code SqlSessionFactory} instance based on an Reader.
* Since 1.3.0, it can be specified a {@link Configuration} instance directly(without config file).
*
* @return SqlSessionFactory
* @throws IOException if loading the config file failed
*/
protected SqlSessionFactory buildSqlSessionFactory() throws Exception {

Configuration configuration;

// TODO 加载自定义 MybatisXmlConfigBuilder
MybatisXMLConfigBuilder xmlConfigBuilder = null;
if (this.configuration != null) {
configuration = this.configuration;
if (configuration.getVariables() == null) {
configuration.setVariables(this.configurationProperties);
} else if (this.configurationProperties != null) {
configuration.getVariables().putAll(this.configurationProperties);
}
} else if (this.configLocation != null) {
xmlConfigBuilder = new MybatisXMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
configuration = xmlConfigBuilder.getConfiguration();
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
}
// TODO 使用自定义配置
configuration = new MybatisConfiguration();
if (this.configurationProperties != null) {
configuration.setVariables(this.configurationProperties);
}
}

if (this.objectFactory != null) {
configuration.setObjectFactory(this.objectFactory);
}

if (this.objectWrapperFactory != null) {
configuration.setObjectWrapperFactory(this.objectWrapperFactory);
}

if (this.vfs != null) {
configuration.setVfsImpl(this.vfs);
}

if (hasLength(this.typeAliasesPackage)) {
// TODO 支持自定义通配符
String[] typeAliasPackageArray;
if (typeAliasesPackage.contains("*") && !typeAliasesPackage.contains(",")
&& !typeAliasesPackage.contains(";")) {
typeAliasPackageArray = PackageHelper.convertTypeAliasesPackage(typeAliasesPackage);
} else {
typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
}
if (typeAliasPackageArray == null) {
throw new MybatisPlusException("not find typeAliasesPackage:" + typeAliasesPackage);
}
for (String packageToScan : typeAliasPackageArray) {
configuration.getTypeAliasRegistry().registerAliases(packageToScan,
typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Scanned package: '" + packageToScan + "' for aliases");
}
}
}

// TODO 自定义枚举类扫描处理
if (hasLength(this.typeEnumsPackage)) {
Set<Class> classes = null;
if (typeEnumsPackage.contains("*") && !typeEnumsPackage.contains(",")
&& !typeEnumsPackage.contains(";")) {
classes = PackageHelper.scanTypePackage(typeEnumsPackage);
} else {
String[] typeEnumsPackageArray = tokenizeToStringArray(this.typeEnumsPackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
if (typeEnumsPackageArray == null) {
throw new MybatisPlusException("not find typeEnumsPackage:" + typeEnumsPackage);
}
classes = new HashSet<Class>();
for (String typePackage : typeEnumsPackageArray) {
classes.addAll(PackageHelper.scanTypePackage(typePackage));
}
}
// 取得类型转换注册器
TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
for (Class cls : classes) {
if (cls.isEnum()) {
if (IEnum.class.isAssignableFrom(cls)) {
typeHandlerRegistry.register(cls.getName(), com.baomidou.mybatisplus.handlers.EnumTypeHandler.class.getCanonicalName());
} else {
// 使用原生 EnumOrdinalTypeHandler
typeHandlerRegistry.register(cls.getName(), org.apache.ibatis.type.EnumOrdinalTypeHandler.class.getCanonicalName());
}
}
}
}

if (!isEmpty(this.typeAliases)) {
for (Class<?> typeAlias : this.typeAliases) {
configuration.getTypeAliasRegistry().registerAlias(typeAlias);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Registered type alias: '" + typeAlias + "'");
}
}
}

if (!isEmpty(this.plugins)) {
for (Interceptor plugin : this.plugins) {
configuration.addInterceptor(plugin);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Registered plugin: '" + plugin + "'");
}
}
}

if (hasLength(this.typeHandlersPackage)) {
String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
for (String packageToScan : typeHandlersPackageArray) {
configuration.getTypeHandlerRegistry().register(packageToScan);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Scanned package: '" + packageToScan + "' for type handlers");
}
}
}

if (!isEmpty(this.typeHandlers)) {
for (TypeHandler<?> typeHandler : this.typeHandlers) {
configuration.getTypeHandlerRegistry().register(typeHandler);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Registered type handler: '" + typeHandler + "'");
}
}
}

if (this.databaseIdProvider != null) {//fix #64 set databaseId before parse mapper xmls
try {
configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
} catch (SQLException e) {
throw new NestedIOException("Failed getting a databaseId", e);
}
}

if (this.cache != null) {
configuration.addCache(this.cache);
}

if (xmlConfigBuilder != null) {
try {
xmlConfigBuilder.parse();

if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Parsed configuration file: '" + this.configLocation + "'");
}
} catch (Exception ex) {
throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
} finally {
ErrorContext.instance().reset();
}
}

if (this.transactionFactory == null) {
this.transactionFactory = new SpringManagedTransactionFactory();
}

configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource));
// 设置元数据相关
GlobalConfigUtils.setMetaData(dataSource, globalConfig);
SqlSessionFactory sqlSessionFactory = this.sqlSessionFactoryBuilder.build(configuration);
// TODO SqlRunner
SqlRunner.FACTORY = sqlSessionFactory;
// TODO 缓存 sqlSessionFactory
globalConfig.setSqlSessionFactory(sqlSessionFactory);
// TODO 设置全局参数属性
globalConfig.signGlobalConfig(sqlSessionFactory);
if (!isEmpty(this.mapperLocations)) {
if (globalConfig.isRefresh()) {
//TODO 设置自动刷新配置 减少配置
new MybatisMapperRefresh(this.mapperLocations, sqlSessionFactory, 2,
2, true);
}
for (Resource mapperLocation : this.mapperLocations) { // **********************循环所有mapper xxx/xx.xml
if (mapperLocation == null) {
continue;
}

try {
// TODO 这里也换了噢噢噢噢
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
configuration, mapperLocation.toString(), configuration.getSqlFragments());
xmlMapperBuilder.parse(); //************************解析XML***************************
} catch (Exception e) {
throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
} finally {
ErrorContext.instance().reset();
}

if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Parsed mapper file: '" + mapperLocation + "'");
}
}
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found");
}
}
return sqlSessionFactory;
}
}

XMLMapperBuilder

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
package org.apache.ibatis.builder.xml;
public class XMLMapperBuilder extends BaseBuilder {
public void parse() {
if (!configuration.isResourceLoaded(resource)) {
configurationElement(parser.evalNode("/mapper")); // ************************
configuration.addLoadedResource(resource); // 配置文件中增加加载Resource路径,resource=xxx/xxx/dao/XXXDao.xml
bindMapperForNamespace(); // vip.infotech.xxx.xxx.dao.XXXDao,添加Mapper到配置中
}

parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}
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"));
cacheElement(context.evalNode("cache"));
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
resultMapElements(context.evalNodes("/mapper/resultMap"));
sqlElement(context.evalNodes("/mapper/sql"));
buildStatementFromContext(context.evalNodes("select|insert|update|delete")); // *********所有自定义XML解析成Statement*********
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
}
}
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
/**
* 循环把自定义xml配置文件中的sql获取出来
*/
for (XNode context : list) {
final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
try {
statementParser.parseStatementNode(); // 转换成statement ************
} catch (IncompleteElementException e) { // 不完整
configuration.addIncompleteStatement(statementParser);
}
}
}
private void bindMapperForNamespace() {
String namespace = builderAssistant.getCurrentNamespace();
if (namespace != null) {
Class<?> boundType = null;
try {
boundType = Resources.classForName(namespace); // vip.infotech.xxx.xxx.dao.XXXDao
} catch (ClassNotFoundException e) {
//ignore, bound type is not required
}
if (boundType != null) {
if (!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
configuration.addLoadedResource("namespace:" + namespace);
configuration.addMapper(boundType);
}
}
}
}
}

Configuration配置信息

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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
public class Configuration {

protected Environment environment;
/* 是否启用行内嵌套语句**/
protected boolean safeRowBoundsEnabled;
protected boolean safeResultHandlerEnabled = true;
protected boolean mapUnderscoreToCamelCase;
protected boolean aggressiveLazyLoading;
protected boolean multipleResultSetsEnabled = true;
protected boolean useGeneratedKeys;
protected boolean useColumnLabel = true;
/*配置全局性的cache开关,默认为true**/
protected boolean cacheEnabled = true;
protected boolean callSettersOnNulls;
protected boolean useActualParamName = true;
protected boolean returnInstanceForEmptyRow;

protected String logPrefix;
protected Class <? extends Log> logImpl;
protected Class <? extends VFS> vfsImpl;
protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
protected JdbcType jdbcTypeForNull = JdbcType.OTHER;
protected Set<String> lazyLoadTriggerMethods = new HashSet<String>(Arrays.asList(new String[] { "equals", "clone", "hashCode", "toString" }));
/* 设置驱动等待数据响应超时数**/
protected Integer defaultStatementTimeout;
protected Integer defaultFetchSize;
protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
/* 指定 MyBatis 如何自动映射 数据基表的列 NONE:不隐射 PARTIAL:部分 FULL:全部**/
protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL;
protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior = AutoMappingUnknownColumnBehavior.NONE;

protected Properties variables = new Properties();
protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
protected ObjectFactory objectFactory = new DefaultObjectFactory();
protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();

protected boolean lazyLoadingEnabled = false;
protected ProxyFactory proxyFactory = new JavassistProxyFactory(); // #224 Using internal Javassist instead of OGNL
/* 数据库类型id**/
protected String databaseId;
/**
* Configuration factory class.
* Used to create Configuration for loading deserialized unread properties.
*
* @see <a href='https://code.google.com/p/mybatis/issues/detail?id=300'>Issue 300 (google code)</a>
*/
protected Class<?> configurationFactory;

protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
protected final InterceptorChain interceptorChain = new InterceptorChain();
protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();

protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");
protected final Map<String, Cache> caches = new StrictMap<Cache>("Caches collection");
protected final Map<String, ResultMap> resultMaps = new StrictMap<ResultMap>("Result Maps collection");
protected final Map<String, ParameterMap> parameterMaps = new StrictMap<ParameterMap>("Parameter Maps collection");
protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<KeyGenerator>("Key Generators collection");

protected final Set<String> loadedResources = new HashSet<String>();
protected final Map<String, XNode> sqlFragments = new StrictMap<XNode>("XML fragments parsed from previous mappers");

protected final Collection<XMLStatementBuilder> incompleteStatements = new LinkedList<XMLStatementBuilder>();
protected final Collection<CacheRefResolver> incompleteCacheRefs = new LinkedList<CacheRefResolver>();
protected final Collection<ResultMapResolver> incompleteResultMaps = new LinkedList<ResultMapResolver>();
protected final Collection<MethodResolver> incompleteMethods = new LinkedList<MethodResolver>();

/*
* A map holds cache-ref relationship. The key is the namespace that
* references a cache bound to another namespace and the value is the
* namespace which the actual cache is bound to.
*/
protected final Map<String, String> cacheRefMap = new HashMap<String, String>();

public Configuration(Environment environment) {
this();
this.environment = environment;
}

public Configuration() {
typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);

typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);

typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
typeAliasRegistry.registerAlias("LRU", LruCache.class);
typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
typeAliasRegistry.registerAlias("WEAK", WeakCache.class);

typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);

typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);

typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);

typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);

languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
languageRegistry.register(RawLanguageDriver.class);
}

public <T> void addMapper(Class<T> type) {
mapperRegistry.addMapper(type);
}
}

MybatisMapperRegistry Mapper注册

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
package com.baomidou.mybatisplus;
public class MybatisMapperRegistry extends MapperRegistry {
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
private final Configuration config;

public MybatisMapperRegistry(Configuration config) {
super(config);
this.config = config;
// TODO注入SqlRunner
GlobalConfigUtils.getSqlInjector(config).injectSqlRunner(config); // .injectSqlRunner(config) 先注入
}

@Override
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
if (hasMapper(type)) {
// TODO 如果之前注入 直接返回
return;
// throw new BindingException("Type " + type +
// " is already known to the MybatisPlusMapperRegistry.");
}
boolean loadCompleted = false;
try {
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.
// TODO 自定义无 XML 注入
MybatisMapperAnnotationBuilder parser = new MybatisMapperAnnotationBuilder(config, type); // ******
parser.parse(); // **** 转换
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
}

MybatisMapperAnnotationBuilder 没有XML配置文件注入基础CRUD方法

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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
/**
* <p>
* *********继承 MapperAnnotationBuilder 没有XML配置文件注入基础CRUD方法**********
* </p>
*
* @author Caratacus
* @Date 2017-01-04
*/
package com.baomidou.mybatisplus;
public class MybatisMapperAnnotationBuilder extends MapperAnnotationBuilder {
private final Set<Class<? extends Annotation>> sqlAnnotationTypes = new HashSet<>();
private final Set<Class<? extends Annotation>> sqlProviderAnnotationTypes = new HashSet<>();

private final Configuration configuration;
private final MapperBuilderAssistant assistant;
private final Class<?> type;

public MybatisMapperAnnotationBuilder(Configuration configuration, Class<?> type) {
// 执行父类
super(configuration, type);

String resource = type.getName().replace('.', '/') + ".java (best guess)";
this.assistant = new MapperBuilderAssistant(configuration, resource);
this.configuration = configuration;
this.type = type;

sqlAnnotationTypes.add(Select.class);
sqlAnnotationTypes.add(Insert.class);
sqlAnnotationTypes.add(Update.class);
sqlAnnotationTypes.add(Delete.class);

sqlProviderAnnotationTypes.add(SelectProvider.class);
sqlProviderAnnotationTypes.add(InsertProvider.class);
sqlProviderAnnotationTypes.add(UpdateProvider.class);
sqlProviderAnnotationTypes.add(DeleteProvider.class);
}
@Override
public void parse() {
String resource = type.toString();
if (!configuration.isResourceLoaded(resource)) {
loadXmlResource(); // ***加载xml资源,自定义的XML方法加载成statement放入configuration***
configuration.addLoadedResource(resource);
assistant.setCurrentNamespace(type.getName()); // 设置当前命名空间vip.infotech.xxx.xxx.dao.XXXDao
parseCache(); // 解析缓存
parseCacheRef(); // 解析缓存关联
Method[] methods = type.getMethods(); // 获取vip.infotech.xxx.xxx.dao.XXXDao所有方法,自定义方法与BaseMapper方法
// TODO 注入 CURD 动态 SQL (应该在注解之前注入)
if (BaseMapper.class.isAssignableFrom(type)) {
GlobalConfigUtils.getSqlInjector(configuration).inspectInject(assistant, type);
}
for (Method method : methods) {
try {
// issue #237
if (!method.isBridge()) {
parseStatement(method);
}
} catch (IncompleteElementException e) {
configuration.addIncompleteMethod(new MethodResolver(this, method));
}
}
}
parsePendingMethods();
}
private void parsePendingMethods() {
Collection<MethodResolver> incompleteMethods = configuration.getIncompleteMethods();
synchronized (incompleteMethods) {
Iterator<MethodResolver> iter = incompleteMethods.iterator();
while (iter.hasNext()) {
try {
iter.next().resolve();
iter.remove();
} catch (IncompleteElementException e) {
// This method is still missing a resource
}
}
}
}
private void loadXmlResource() {
// Spring may not know the real resource name so we check a flag
// to prevent loading again a resource twice
// this flag is set at XMLMapperBuilder#bindMapperForNamespace
// 已经加载不进行二次加载
if (!configuration.isResourceLoaded("namespace:" + type.getName())) {
String xmlResource = type.getName().replace('.', '/') + ".xml";
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource);
} catch (IOException e) {
// ignore, resource is not required
}
if (inputStream != null) {
XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName());
xmlParser.parse();
}
}
}
// 解析Statement
void parseStatement(Method method) {
//获取参数类型,当只有一个入参的时候获取到的类型为入参的类型,多个入参时获取到的是ParamMap.class
Class<?> parameterTypeClass = getParameterType(method);
//根据方法上标注的注解Lang中的值获取语言驱动
LanguageDriver languageDriver = getLanguageDriver(method);
//从注解中获取sqlSource
//获取SqlSource从Insert|Select|Update|Delete注解或者InsertProvider|SelectProvider|UpdateProvider|DeleteProvider注解
SqlSource sqlSource = getSqlSourceFromAnnotations(method, parameterTypeClass, languageDriver);
//如果sqlSource是空的,并没有报错,说明是允许没有注解的,在代码编写的时候需要注意不能遗漏
if (sqlSource != null) {
Options options = method.getAnnotation(Options.class);
final String mappedStatementId = type.getName() + "." + method.getName();
Integer fetchSize = null;
Integer timeout = null;
StatementType statementType = StatementType.PREPARED;
ResultSetType resultSetType = ResultSetType.FORWARD_ONLY;
SqlCommandType sqlCommandType = getSqlCommandType(method);
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
boolean flushCache = !isSelect;
boolean useCache = isSelect;

KeyGenerator keyGenerator;
String keyProperty = "id";
String keyColumn = null;
//如果是更新或者插入
if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {
// first check for SelectKey annotation - that overrides everything else
SelectKey selectKey = method.getAnnotation(SelectKey.class);
if (selectKey != null) {
keyGenerator = handleSelectKeyAnnotation(selectKey, mappedStatementId, getParameterType(method), languageDriver);
keyProperty = selectKey.keyProperty();
} else if (options == null) {
keyGenerator = configuration.isUseGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
} else {
keyGenerator = options.useGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
keyProperty = options.keyProperty();
keyColumn = options.keyColumn();
}
} else {
keyGenerator = NoKeyGenerator.INSTANCE;
}

if (options != null) {
if (FlushCachePolicy.TRUE.equals(options.flushCache())) {
flushCache = true;
} else if (FlushCachePolicy.FALSE.equals(options.flushCache())) {
flushCache = false;
}
useCache = options.useCache();
fetchSize = options.fetchSize() > -1 || options.fetchSize() == Integer.MIN_VALUE ? options.fetchSize() : null; //issue #348
timeout = options.timeout() > -1 ? options.timeout() : null;
statementType = options.statementType();
resultSetType = options.resultSetType();
}

String resultMapId = null;
ResultMap resultMapAnnotation = method.getAnnotation(ResultMap.class);
if (resultMapAnnotation != null) {
String[] resultMaps = resultMapAnnotation.value();
StringBuilder sb = new StringBuilder();
for (String resultMap : resultMaps) {
if (sb.length() > 0) {
sb.append(",");
}
sb.append(resultMap);
}
resultMapId = sb.toString();
} else if (isSelect) {
resultMapId = parseResultMap(method);
}
//全局都是使用的一个assistant,将新解析出来的Statement加入到Map中
assistant.addMappedStatement(
mappedStatementId,
sqlSource,
statementType,
sqlCommandType,
fetchSize,
timeout,
// ParameterMapID
null,
parameterTypeClass,
resultMapId,
getReturnType(method),
resultSetType,
flushCache,
useCache,
// TODO gcode issue #577
false,
keyGenerator,
keyProperty,
keyColumn,
// DatabaseID
null,
languageDriver,
// ResultSets
options != null ? nullOrEmpty(options.resultSets()) : null);
}
}
}

Mybatis全局缓存工具类

1
2
3
4
5
6
7
8
9
10
11
12
public class GlobalConfigUtils {
public static ISqlInjector getSqlInjector(Configuration configuration) {
// fix #140
GlobalConfiguration globalConfiguration = getGlobalConfig(configuration);
ISqlInjector sqlInjector = globalConfiguration.getSqlInjector();
if (sqlInjector == null) {
sqlInjector = new AutoSqlInjector();
globalConfiguration.setSqlInjector(sqlInjector);
}
return sqlInjector;
}
}

自动接口注入器

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
/**
* <p>
* SQL 自动注入器接口
* </p>
*
* @author hubin
* @Date 2016-07-24
*/
public interface ISqlInjector {

/**
* 根据mapperClass注入SQL
*
* @param builderAssistant
* @param mapperClass
*/
void inject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass);

/**
* 检查SQL是否注入(已经注入过不再注入)
*
* @param builderAssistant
* @param mapperClass
*/
void inspectInject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass);

/**
* 注入SqlRunner相关
*
* @param configuration
* @see com.baomidou.mybatisplus.mapper.SqlRunner
*/
void injectSqlRunner(Configuration configuration);

}

自动注入器

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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169

public class AutoSqlInjector implements ISqlInjector {
private static final Log logger = LogFactory.getLog(AutoSqlInjector.class);

protected Configuration configuration;
protected LanguageDriver languageDriver;
protected MapperBuilderAssistant builderAssistant;
/**
* <p>
* CRUD 注入后给予标识 注入过后不再注入
* </p>
*
* @param builderAssistant
* @param mapperClass
*/
@Override
public void inspectInject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) {
String className = mapperClass.toString();
Set<String> mapperRegistryCache = GlobalConfigUtils.getMapperRegistryCache(builderAssistant.getConfiguration());
if (!mapperRegistryCache.contains(className)) {
inject(builderAssistant, mapperClass); // *******
mapperRegistryCache.add(className);
}
}
/**
* 注入单点 crudSql
*/
@Override
public void inject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) {
this.configuration = builderAssistant.getConfiguration();
this.builderAssistant = builderAssistant;
this.languageDriver = configuration.getDefaultScriptingLanguageInstance();
/**
* 驼峰设置 PLUS 配置 > 原始配置
*/
GlobalConfiguration globalCache = this.getGlobalConfig();
if (!globalCache.isDbColumnUnderline()) {
globalCache.setDbColumnUnderline(configuration.isMapUnderscoreToCamelCase());
}
Class<?> modelClass = extractModelClass(mapperClass);
if (null != modelClass) {
/**
* 初始化 SQL 解析
*/
if (globalCache.isSqlParserCache()) {
PluginUtils.initSqlParserInfoCache(mapperClass);
}
TableInfo table = TableInfoHelper.initTableInfo(builderAssistant, modelClass);
injectSql(builderAssistant, mapperClass, modelClass, table);
}
}
/**
* <p>
* 注入SQL
* </p>
*
* @param builderAssistant
* @param mapperClass
* @param modelClass
* @param table
*/
protected void injectSql(MapperBuilderAssistant builderAssistant, Class<?> mapperClass, Class<?> modelClass, TableInfo table) {
/**
* #148 表信息包含主键,注入主键相关方法
*/
if (StringUtils.isNotEmpty(table.getKeyProperty())) {
/** 删除 */
this.injectDeleteByIdSql(false, mapperClass, modelClass, table);
this.injectDeleteByIdSql(true, mapperClass, modelClass, table);
/** 修改 */
this.injectUpdateByIdSql(true, mapperClass, modelClass, table);
this.injectUpdateByIdSql(false, mapperClass, modelClass, table);
/** 查询 */
this.injectSelectByIdSql(false, mapperClass, modelClass, table);
this.injectSelectByIdSql(true, mapperClass, modelClass, table);
} else {
// 表不包含主键时 给予警告
logger.warn(String.format("%s ,Not found @TableId annotation, Cannot use Mybatis-Plus 'xxById' Method.",
modelClass.toString()));
}
/**
* 正常注入无需主键方法
*/
/** 插入 */
this.injectInsertOneSql(true, mapperClass, modelClass, table);
this.injectInsertOneSql(false, mapperClass, modelClass, table);
/** 删除 */
this.injectDeleteSql(mapperClass, modelClass, table);
this.injectDeleteByMapSql(mapperClass, table);
/** 修改 */
this.injectUpdateSql(mapperClass, modelClass, table);
/** 查询 */
this.injectSelectByMapSql(mapperClass, modelClass, table);
this.injectSelectOneSql(mapperClass, modelClass, table);
this.injectSelectCountSql(mapperClass, modelClass, table);
this.injectSelectListSql(SqlMethod.SELECT_LIST, mapperClass, modelClass, table);
this.injectSelectListSql(SqlMethod.SELECT_PAGE, mapperClass, modelClass, table);
this.injectSelectMapsSql(SqlMethod.SELECT_MAPS, mapperClass, modelClass, table);
this.injectSelectMapsSql(SqlMethod.SELECT_MAPS_PAGE, mapperClass, modelClass, table);
this.injectSelectObjsSql(SqlMethod.SELECT_OBJS, mapperClass, modelClass, table);
/** 自定义方法 */
this.inject(configuration, builderAssistant, mapperClass, modelClass, table);
}
/**
* <p>
* 注入删除 SQL 语句
* </p>
*
* @param mapperClass
* @param modelClass
* @param table
*/
protected void injectDeleteByIdSql(boolean batch, Class<?> mapperClass, Class<?> modelClass, TableInfo table) {
SqlMethod sqlMethod = SqlMethod.DELETE_BY_ID;
SqlSource sqlSource;
// 因为后面要通过get方法获取类型,所以这里要获取key的属性值
String idStr = table.getKeyProperty();
if (batch) {
sqlMethod = SqlMethod.DELETE_BATCH_BY_IDS;
StringBuilder ids = new StringBuilder();
ids.append("\n<foreach item=\"item\" index=\"index\" collection=\"coll\" separator=\",\">");
ids.append("#{item}");
ids.append("\n</foreach>");
idStr = ids.toString();
}
String sql = String.format(sqlMethod.getSql(), table.getTableName(), table.getKeyColumn(), idStr);
sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass); // <script>DELETE FROM xxx WHERE id=#{id}</script>
this.addDeleteMappedStatement(mapperClass, sqlMethod.getMethod(), sqlSource);
}
/**
* 删除
*/
public MappedStatement addDeleteMappedStatement(Class<?> mapperClass, String id, SqlSource sqlSource) {
return this.addMappedStatement(mapperClass, id, sqlSource, SqlCommandType.DELETE, null, null, Integer.class,
new NoKeyGenerator(), null, null);
}
public MappedStatement addMappedStatement(Class<?> mapperClass, String id, SqlSource sqlSource,
SqlCommandType sqlCommandType, Class<?> parameterClass, String resultMap, Class<?> resultType,
KeyGenerator keyGenerator, String keyProperty, String keyColumn) {
String statementName = mapperClass.getName() + "." + id;
if (hasMappedStatement(statementName)) {
System.err.println("{" + statementName
+ "} Has been loaded by XML or SqlProvider, ignoring the injection of the SQL.");
return null;
}
/** 缓存逻辑处理 */
boolean isSelect = false;
if (sqlCommandType == SqlCommandType.SELECT) {
isSelect = true;
}
return builderAssistant.addMappedStatement(id, sqlSource, StatementType.PREPARED, sqlCommandType, null, null, null,
parameterClass, resultMap, resultType, null, !isSelect, isSelect, false, keyGenerator, keyProperty, keyColumn,
configuration.getDatabaseId(), languageDriver, null);
}

// --------------------------------------------------------SqlRunner------------------------------------------------------------

@Override
public void injectSqlRunner(Configuration configuration) {
this.configuration = configuration;
this.languageDriver = configuration.getDefaultScriptingLanguageInstance();
initSelectList();
initSelectObjs();
initInsert();
initUpdate();
initDelete();
initCount();
}
}

TableInfoHelper 实体反射辅助类

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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
package com.baomidou.mybatisplus.toolkit;
public class TableInfoHelper {
/**
* <p>
* 实体类反射获取表信息【初始化】
* <p>
*
* @param clazz 反射实体类
* @return
*/
public synchronized static TableInfo initTableInfo(MapperBuilderAssistant builderAssistant, Class<?> clazz) {
TableInfo tableInfo = tableInfoCache.get(clazz.getName());
if (StringUtils.checkValNotNull(tableInfo)) {
if (StringUtils.checkValNotNull(builderAssistant)) {
tableInfo.setConfigMark(builderAssistant.getConfiguration());
}
return tableInfo;
}
tableInfo = new TableInfo();
GlobalConfiguration globalConfig;
if (null != builderAssistant) {
tableInfo.setCurrentNamespace(builderAssistant.getCurrentNamespace());
tableInfo.setConfigMark(builderAssistant.getConfiguration());
globalConfig = GlobalConfigUtils.getGlobalConfig(builderAssistant.getConfiguration());
} else {
// 兼容测试场景
globalConfig = GlobalConfigUtils.DEFAULT;
}
/* 表名 */
TableName table = clazz.getAnnotation(TableName.class);
String tableName = clazz.getSimpleName();
if (table != null && StringUtils.isNotEmpty(table.value())) {
tableName = table.value();
} else {
// 开启字段下划线申明
if (globalConfig.isDbColumnUnderline()) {
tableName = StringUtils.camelToUnderline(tableName);
}
// 大写命名判断
if (globalConfig.isCapitalMode()) {
tableName = tableName.toUpperCase();
} else {
// 首字母小写
tableName = StringUtils.firstToLowerCase(tableName);
}
}
tableInfo.setTableName(tableName);

// 开启了自定义 KEY 生成器
if (null != globalConfig.getKeyGenerator()) {
tableInfo.setKeySequence(clazz.getAnnotation(KeySequence.class));
}

/* 表结果集映射 */
if (table != null && StringUtils.isNotEmpty(table.resultMap())) {
tableInfo.setResultMap(table.resultMap());
}
List<TableFieldInfo> fieldList = new ArrayList<>();
List<Field> list = getAllFields(clazz);
// 标记是否读取到主键
boolean isReadPK = false;
boolean existTableId = existTableId(list);
for (Field field : list) {
/*
* 主键ID 初始化
*/
if (!isReadPK) {
if (existTableId) {
isReadPK = initTableId(globalConfig, tableInfo, field, clazz);
} else {
isReadPK = initFieldId(globalConfig, tableInfo, field, clazz);
}
if (isReadPK) {
continue;
}

}
/*
* 字段初始化
*/
if (initTableField(globalConfig, tableInfo, fieldList, field, clazz)) {
continue;
}

/*
* 字段, 使用 camelToUnderline 转换驼峰写法为下划线分割法, 如果已指定 TableField , 便不会执行这里
*/
fieldList.add(new TableFieldInfo(globalConfig, tableInfo, field));
}

/* 字段列表 */
tableInfo.setFieldList(globalConfig, fieldList);
/*
* 未发现主键注解,提示警告信息
*/
if (StringUtils.isEmpty(tableInfo.getKeyColumn())) {
logger.warn(String.format("Warn: Could not find @TableId in Class: %s.", clazz.getName()));
}
/*
* 注入
*/
tableInfoCache.put(clazz.getName(), tableInfo);
return tableInfo;
}
}

MapperBuilderAssistant

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
package org.apache.ibatis.builder;
public class MapperBuilderAssistant extends BaseBuilder {
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");
}

id = applyCurrentNamespace(id, false);
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;

MappedStatement.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);
}

MappedStatement statement = statementBuilder.build(); // 构建MappedStatement
configuration.addMappedStatement(statement); // *******添加MappedStatement id=vip.infotech.xxxx.dao.XXDao.getXXByXX 到configuration配置中*********
return statement;
}
}

MapperProxyFactory 代理工厂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();

public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}

XMLStatementBuilder

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
public class XMLStatementBuilder extends BaseBuilder {
public XMLStatementBuilder(Configuration configuration, MapperBuilderAssistant builderAssistant, XNode context, String databaseId) {
super(configuration);
this.builderAssistant = builderAssistant;
this.context = context;
this.requiredDatabaseId = databaseId;
}
public void parseStatementNode() {
String id = context.getStringAttribute("id"); // xml中id:getXXXById
String databaseId = context.getStringAttribute("databaseId");

if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
return;
}

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);

Class<?> resultTypeClass = resolveClass(resultType);
String resultSetType = context.getStringAttribute("resultSetType");
StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);

String nodeName = context.getNode().getNodeName();
SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
boolean useCache = context.getBooleanAttribute("useCache", isSelect);
boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);

// Include Fragments before parsing
XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
includeParser.applyIncludes(context.getNode());

// Parse selectKey after includes and remove them.
processSelectKeyNodes(id, parameterTypeClass, langDriver);

// Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
String resultSets = context.getStringAttribute("resultSets");
String keyProperty = context.getStringAttribute("keyProperty");
String keyColumn = context.getStringAttribute("keyColumn");
KeyGenerator keyGenerator;
String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true); // vip.infotech.dao.XXXDao.getXXByXX!selectKey
if (configuration.hasKeyGenerator(keyStatementId)) {
keyGenerator = configuration.getKeyGenerator(keyStatementId);
} else {
keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
}

builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets); // ****构建并且添加MappedStatement,注入sqlSource中的SQL语句以及其他参数****
}
}

MybatisConfiguration

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
public class MybatisConfiguration extends Configuration {
/**
* <p>
* MybatisPlus 加载 SQL 顺序:
* </p>
* 1、加载XML中的SQL<br>
* 2、加载sqlProvider中的SQL<br>
* 3、xmlSql 与 sqlProvider不能包含相同的SQL<br>
* <br>
* 调整后的SQL优先级:xmlSql > sqlProvider > curdSql <br>
*/
@Override
public void addMappedStatement(MappedStatement ms) {
logger.debug("addMappedStatement: " + ms.getId());
if (GlobalConfigUtils.isRefresh(ms.getConfiguration())) {
/*
* 支持是否自动刷新 XML 变更内容,开发环境使用【 注:生产环境勿用!】
*/
this.mappedStatements.remove(ms.getId());
} else {
if (this.mappedStatements.containsKey(ms.getId())) {
/*
* 说明已加载了xml中的节点; 忽略mapper中的SqlProvider数据
*/
logger.error("mapper[" + ms.getId() + "] is ignored, because it's exists, maybe from xml file");
return;
}
}
super.addMappedStatement(ms);
}
}

MappedStatement

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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
package org.apache.ibatis.mapping;
public final class MappedStatement {
private String resource;
private Configuration configuration;
private String id;
private Integer fetchSize;
private Integer timeout;
private StatementType statementType;
private ResultSetType resultSetType;
private SqlSource sqlSource;
private Cache cache;
private ParameterMap parameterMap;
private List<ResultMap> resultMaps;
private boolean flushCacheRequired;
private boolean useCache;
private boolean resultOrdered;
private SqlCommandType sqlCommandType;
private KeyGenerator keyGenerator;
private String[] keyProperties;
private String[] keyColumns;
private boolean hasNestedResultMaps;
private String databaseId;
private Log statementLog;
private LanguageDriver lang;
private String[] resultSets;

MappedStatement() {
// constructor disabled
}

public static class Builder {
private MappedStatement mappedStatement = new MappedStatement();

public Builder(Configuration configuration, String id, SqlSource sqlSource, SqlCommandType sqlCommandType) {
mappedStatement.configuration = configuration;
mappedStatement.id = id;
mappedStatement.sqlSource = sqlSource;
mappedStatement.statementType = StatementType.PREPARED;
mappedStatement.parameterMap = new ParameterMap.Builder(configuration, "defaultParameterMap", null, new ArrayList<ParameterMapping>()).build();
mappedStatement.resultMaps = new ArrayList<ResultMap>();
mappedStatement.sqlCommandType = sqlCommandType;
mappedStatement.keyGenerator = configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType) ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
String logId = id;
if (configuration.getLogPrefix() != null) {
logId = configuration.getLogPrefix() + id;
}
mappedStatement.statementLog = LogFactory.getLog(logId);
mappedStatement.lang = configuration.getDefaultScriptingLanguageInstance();
}

public Builder resource(String resource) {
mappedStatement.resource = resource;
return this;
}

public String id() {
return mappedStatement.id;
}

public Builder parameterMap(ParameterMap parameterMap) {
mappedStatement.parameterMap = parameterMap;
return this;
}

public Builder resultMaps(List<ResultMap> resultMaps) {
mappedStatement.resultMaps = resultMaps;
for (ResultMap resultMap : resultMaps) {
mappedStatement.hasNestedResultMaps = mappedStatement.hasNestedResultMaps || resultMap.hasNestedResultMaps();
}
return this;
}

public Builder fetchSize(Integer fetchSize) {
mappedStatement.fetchSize = fetchSize;
return this;
}

public Builder timeout(Integer timeout) {
mappedStatement.timeout = timeout;
return this;
}

public Builder statementType(StatementType statementType) {
mappedStatement.statementType = statementType;
return this;
}

public Builder resultSetType(ResultSetType resultSetType) {
mappedStatement.resultSetType = resultSetType;
return this;
}

public Builder cache(Cache cache) {
mappedStatement.cache = cache;
return this;
}

public Builder flushCacheRequired(boolean flushCacheRequired) {
mappedStatement.flushCacheRequired = flushCacheRequired;
return this;
}

public Builder useCache(boolean useCache) {
mappedStatement.useCache = useCache;
return this;
}

public Builder resultOrdered(boolean resultOrdered) {
mappedStatement.resultOrdered = resultOrdered;
return this;
}

public Builder keyGenerator(KeyGenerator keyGenerator) {
mappedStatement.keyGenerator = keyGenerator;
return this;
}

public Builder keyProperty(String keyProperty) {
mappedStatement.keyProperties = delimitedStringToArray(keyProperty);
return this;
}

public Builder keyColumn(String keyColumn) {
mappedStatement.keyColumns = delimitedStringToArray(keyColumn);
return this;
}

public Builder databaseId(String databaseId) {
mappedStatement.databaseId = databaseId;
return this;
}

public Builder lang(LanguageDriver driver) {
mappedStatement.lang = driver;
return this;
}

public Builder resultSets(String resultSet) {
mappedStatement.resultSets = delimitedStringToArray(resultSet);
return this;
}

/** @deprecated Use {@link #resultSets} */
@Deprecated
public Builder resulSets(String resultSet) {
mappedStatement.resultSets = delimitedStringToArray(resultSet);
return this;
}

public MappedStatement build() {
assert mappedStatement.configuration != null;
assert mappedStatement.id != null;
assert mappedStatement.sqlSource != null;
assert mappedStatement.lang != null;
mappedStatement.resultMaps = Collections.unmodifiableList(mappedStatement.resultMaps);
return mappedStatement;
}
}

public KeyGenerator getKeyGenerator() {
return keyGenerator;
}

public SqlCommandType getSqlCommandType() {
return sqlCommandType;
}

public String getResource() {
return resource;
}

public Configuration getConfiguration() {
return configuration;
}

public String getId() {
return id;
}

public boolean hasNestedResultMaps() {
return hasNestedResultMaps;
}

public Integer getFetchSize() {
return fetchSize;
}

public Integer getTimeout() {
return timeout;
}

public StatementType getStatementType() {
return statementType;
}

public ResultSetType getResultSetType() {
return resultSetType;
}

public SqlSource getSqlSource() {
return sqlSource;
}

public ParameterMap getParameterMap() {
return parameterMap;
}

public List<ResultMap> getResultMaps() {
return resultMaps;
}

public Cache getCache() {
return cache;
}

public boolean isFlushCacheRequired() {
return flushCacheRequired;
}

public boolean isUseCache() {
return useCache;
}

public boolean isResultOrdered() {
return resultOrdered;
}

public String getDatabaseId() {
return databaseId;
}

public String[] getKeyProperties() {
return keyProperties;
}

public String[] getKeyColumns() {
return keyColumns;
}

public Log getStatementLog() {
return statementLog;
}

public LanguageDriver getLang() {
return lang;
}

public String[] getResultSets() {
return resultSets;
}

/** @deprecated Use {@link #getResultSets()} */
@Deprecated
public String[] getResulSets() {
return resultSets;
}

public BoundSql getBoundSql(Object parameterObject) {
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings == null || parameterMappings.isEmpty()) {
boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
}

// check for nested result maps in parameter mappings (issue #30)
for (ParameterMapping pm : boundSql.getParameterMappings()) {
String rmId = pm.getResultMapId();
if (rmId != null) {
ResultMap rm = configuration.getResultMap(rmId);
if (rm != null) {
hasNestedResultMaps |= rm.hasNestedResultMaps();
}
}
}

return boundSql;
}

private static String[] delimitedStringToArray(String in) {
if (in == null || in.trim().length() == 0) {
return null;
} else {
return in.split(",");
}
}

}

调用方法MappedStatement中的SQL过程

  • 使用了动态代理,生成mapper接口的代理类实现

MapperRegistry本质上用于注册Mapper接口和获取MapperProxy类,MapperProxy并通过MapperProxyFactory进行实例化,然而在MapperProxy的invoke()方法中会调用Mapper接口与之对应的MapperMethod类中的execute()方法。MapperRegistry依赖于Mapper类路径,SqlSession作为入参,结合MapperProxy,MapperProxyFactory,ResolverUtil,MapperAnnotationBuilder等组件才完成了一次注册Mapper到SQL

  • CglibAopProxy,spring使用的动态代理进入实现
  • 调用过程ServiceImpl..selectPage()→org.apache.ibatis.binding.MapperProxy→MapperProxy..invoke()→MapperMethod..execute→execute..executeForMany→SqlSessionTemplate..selectList→SqlSessionTemplate..SqlSessionInterceptor..invoke()→Method..invoke()→DelegatingMethodAccessorImpl..invoke()→DefaultSqlSession..selectList()→BaseExecutor..query()→BaseExecutor..queryFromDatabase()→SimpleExecutor..doQuery()→Plugin..invoke()→Method..invoke()→DelegatingMethodAccessorImpl.invoke()→RoutingStatementHandler.query()→PreparedStatementHandler.query()PreparedStatementLogger..execute()→DruidPooledPreparedStatement.execute()→DefaultResultSetHandler..handleResultSets()

MapperProxy代理类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MapperProxy<T> implements InvocationHandler, Serializable {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
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);
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args); //执行方法
}
}
1
2
3
4
5
6
7
8
public class ServiceImpl<M extends BaseMapper<T>, T> implements IService<T> {
@Override
public Page<T> selectPage(Page<T> page, Wrapper<T> wrapper) {
wrapper = (Wrapper<T>) SqlHelper.fillWrapper(page, wrapper);
page.setRecords(baseMapper.selectPage(page, wrapper));
return page;
}
}
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
73
public class MapperMethod {
private final SqlCommand command;
private final MethodSignature method;

public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
this.command = new SqlCommand(config, mapperInterface, method);
this.method = new MethodSignature(config, mapperInterface, method);
}
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
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);
}
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;
}
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) { // 查询列表调用
List<E> result;
Object param = method.convertArgsToSqlCommandParam(args); // 参数转换
if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args); // 分页参数封装
result = sqlSession.<E>selectList(command.getName(), param, rowBounds);
} else {
result = sqlSession.<E>selectList(command.getName(), param);
}
// issue #510 Collections & arrays support
if (!method.getReturnType().isAssignableFrom(result.getClass())) {
if (method.getReturnType().isArray()) {
return convertToArray(result);
} else {
return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
}
}
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
39
40
41
42
43
package org.mybatis.spring;
public class SqlSessionTemplate implements SqlSession, DisposableBean {
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
return this.sqlSessionProxy.selectList(statement, parameter, rowBounds);
}
private class SqlSessionInterceptor implements InvocationHandler {
private SqlSessionInterceptor() {
}

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
SqlSession sqlSession = SqlSessionUtils.getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);

Object unwrapped;
try {
Object result = method.invoke(sqlSession, args);
if (!SqlSessionUtils.isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
sqlSession.commit(true);
}

unwrapped = result;
} catch (Throwable var11) {
unwrapped = ExceptionUtil.unwrapThrowable(var11);
if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
sqlSession = null;
Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException)unwrapped);
if (translated != null) {
unwrapped = translated;
}
}

throw (Throwable)unwrapped;
} finally {
if (sqlSession != null) {
SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}

}

return unwrapped;
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
public class DefaultSqlSession implements SqlSession {
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
MappedStatement ms = configuration.getMappedStatement(statement);
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
}
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
public abstract class BaseExecutor implements Executor {
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameter);
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
@SuppressWarnings("unchecked")
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing a 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 #601
deferredLoads.clear();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
return list;
}
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, 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;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package org.apache.ibatis.executor;
public class SimpleExecutor extends BaseExecutor {
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Plugin implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
Set<Method> methods = signatureMap.get(method.getDeclaringClass());
if (methods != null && methods.contains(method)) {
return interceptor.intercept(new Invocation(target, method, args));
}
return method.invoke(target, args); //method:org.apache.ibatis.executor.statement.StatementHandler,target:RoutingStatementHandler
} catch (Exception e) {
throw ExceptionUtil.unwrapThrowable(e);
}
}
}
1
2
3
4
5
6
7
package org.apache.ibatis.executor.statement;
public class RoutingStatementHandler implements StatementHandler {
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
return delegate.<E>query(statement, resultHandler);
}
}
1
2
3
4
5
6
7
8
9
package org.apache.ibatis.executor.statement;
public class PreparedStatementHandler extends BaseStatementHandler {
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.<E> handleResultSets(ps);
}
}
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
package org.apache.ibatis.logging.jdbc;
public final class PreparedStatementLogger extends BaseJdbcLogger implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, params);
}
if (EXECUTE_METHODS.contains(method.getName())) {
if (isDebugEnabled()) {
debug("Parameters: " + getParameterValueString(), true);
}
clearColumnInfo();
if ("executeQuery".equals(method.getName())) {
ResultSet rs = (ResultSet) method.invoke(statement, params);
return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack);
} else {
return method.invoke(statement, params);
}
} else if (SET_METHODS.contains(method.getName())) {
if ("setNull".equals(method.getName())) {
setColumn(params[0], null);
} else {
setColumn(params[0], params[1]);
}
return method.invoke(statement, params);
} else if ("getResultSet".equals(method.getName())) {
ResultSet rs = (ResultSet) method.invoke(statement, params);
return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack);
} else if ("getUpdateCount".equals(method.getName())) {
int updateCount = (Integer) method.invoke(statement, params);
if (updateCount != -1) {
debug(" Updates: " + updateCount, false);
}
return updateCount;
} else {
return method.invoke(statement, params);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.alibaba.druid.pool;
public class DruidPooledPreparedStatement extends DruidPooledStatement implements PreparedStatement {
public boolean execute() throws SQLException {
this.checkOpen();
this.incrementExecuteCount();
this.transactionRecord(this.sql);
this.conn.beforeExecute();

boolean var1;
try {
var1 = this.stmt.execute();
} catch (Throwable var5) {
this.errorCheck(var5);
throw this.checkException(var5);
} finally {
this.conn.afterExecute();
}

return var1;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
package com.alibaba.druid.proxy.jdbc;
public class PreparedStatementProxyImpl extends StatementProxyImpl implements PreparedStatementProxy {
public boolean execute() throws SQLException {
this.updateCount = null;
this.lastExecuteSql = this.sql;
this.lastExecuteType = StatementExecuteType.Execute;
this.lastExecuteStartNano = -1L;
this.lastExecuteTimeNano = -1L;
this.firstResultSet = this.createChain().preparedStatement_execute(this);
return this.firstResultSet;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.alibaba.druid.proxy.jdbc;
public class StatementProxyImpl extends WrapperProxyImpl implements StatementProxy {
public FilterChainImpl createChain() {
FilterChainImpl chain = this.filterChain;
if (chain == null) {
chain = new FilterChainImpl(this.getConnectionProxy().getDirectDataSource());
} else {
this.filterChain = null;
}

return chain;
}
public ConnectionProxy getConnectionProxy() {
return this.connection;
}
}
1
2
3
4
5
public class ConnectionProxyImpl extends WrapperProxyImpl implements ConnectionProxy {
public DataSourceProxy getDirectDataSource() {
return this.dataSource;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class DruidPooledConnection extends PoolableWrapper implements PooledConnection, Connection {
final void afterExecute() { // 处理结果
DruidConnectionHolder holder = this.holder;
if (holder != null) {
DruidAbstractDataSource dataSource = holder.dataSource;
if (dataSource.removeAbandoned) {
this.running = false;
holder.lastActiveTimeMillis = System.currentTimeMillis();
}

dataSource.onFatalError = false;
}
}
}
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
public class DefaultResultSetHandler implements ResultSetHandler { // 结果处理
@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

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

int resultSetCount = 0;
ResultSetWrapper rsw = getFirstResultSet(stmt);

List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
validateResultMapsCount(rsw, resultMapCount);
while (rsw != null && resultMapCount > resultSetCount) {
ResultMap resultMap = resultMaps.get(resultSetCount);
handleResultSet(rsw, resultMap, multipleResults, null);
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}

String[] resultSets = mappedStatement.getResultSets();
if (resultSets != null) {
while (rsw != null && resultSetCount < resultSets.length) {
ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
if (parentMapping != null) {
String nestedResultMapId = parentMapping.getNestedResultMapId();
ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
handleResultSet(rsw, resultMap, null, parentMapping);
}
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
}

return collapseSingleResultList(multipleResults);
}
}

使用

mybatis-plus updateById无法更新空字符串或者null

最主要还是Integer类型会出现Null导致无法更新,所以最好设计的时候需要更新成空值的字段用char代替,然后个别必须使用Integer的接口单独使用完全更新模式处理。

方法零:

前端不传递值时(空值),服务端一般处理为不修改

方法一:

1
2
#字段策略 0:"忽略判断,所有字段都更新和插入",1:"非 NULL 判断,只更新和插入非NULL值"),2:"非空判断,只更新和插入非NULL值且非空字符串"
field-strategy: 2

field-strategy设置成0

方法二:

@TableField(strategy = FieldStrategy.IGNORED) 在需要更新插入的字段上加注解,比如Integer类型的字段由于""会被框架转换成null,然后配置文件配置成field-strategy: 1,这样会导致Integer字段无法更新成空
注意:如果设置成0会导致界面没传的字段被更新成null
对于只传输一部分数据的接口只能后台先查询下旧数据再把新数据覆盖到对象里在进行更新然后配置文件配置成field-strategy: 0,如果页面传入的值有Integer类型""被转换成了null到底是要更新还是不更新,可能部分字段没传也是null,如果更新了还是会导致数据丢失,如果null不更新会导致Integer类型无法设置成空。估计只能放弃Integer类型,使用String类型,或者更新接口拆分出来再做一个只更新部分字段的接口,或者更新操作前端所有参数都传
使用Map没这种问题。

组合使用:

  • 比如把creator这种创建者设置成@TableField(strategy = FieldStrategy.NOT_EMPTY),更新的时候前端可以不传,然后后端设置成空值进行屏蔽。默认策略设置成用field-strategy: 0策略,这样需要特殊处理的地方单独加TableField进行设置。保证前端更新的时候传输完整的对象。
  • 或者默认设置成field-strategy: 1策略。对应整数类型设置成@TableField(strategy = FieldStrategy.IGNORED)保证表单中的整数类型都要传输值。对应只想传部分值的接口单独加接口单独写update
  • 直接使用策略2,不让清空。根据情况使用updateAllColumnById全部字段更新: 3.0已经移除,3.0使用UpdateWrapper
  • 直接使用策略1,Integer类型与datetime的无法清空,不做处理
  • 单独加接口,更新指定字段
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
    updateWrapper.eq("name","1");
    User user = new User();
    user.setAge(35);
    Integer rows = userMapper.update(user, updateWrapper);

    UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
    updateWrapper.eq("name","1").set("age", 35);
    Integer rows = userMapper.update(null, updateWrapper);

    LambdaUpdateWrapper<User> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
    lambdaUpdateWrapper.eq(User::getRealName, "1").set(User::getAge, 34);
    Integer rows = userMapper.update(null, lambdaUpdateWrapper);

通过注解+拦截器统一实现数据过滤

DataFilterAspect 数据过滤切面,对加注解的方法统一注入过滤SQL片段,还可配合addFilterIfNeed方法
public @interface DataFilter
@DataFilter(subDept = true, user = false)

伪删除、逻辑删除

  1. 配置文件:
1
2
3
4
5
6
7
mybatis-plus:
global-config:
#数据库相关配置
db-config:
#逻辑删除配置
logic-delete-value: -1
logic-not-delete-value: 0
  1. 实体类添加逻辑删除注解系统就会自动使用逻辑删除:
1
2
@TableLogic
private Integer delFlag;

主键策略配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public enum IdType {
/**
* 数据库ID自增
*/
AUTO(0),
NONE(1),
/**
* 用户输入
*/
INPUT(2),
ID_WORKER(3),
UUID(4),
ID_WORKER_STR(5);

private final int key;

private IdType(int key) {
this.key = key;
}

public int getKey() {
return this.key;
}
}
1
2
@TableId(type = IdType.INPUT)
private long userId;

命名规范

同样是保存需要进行特殊处理的时候,使用自定义方法,都应该写在service层,你不要重写plus的方法。方法名统一成save+业务saveUser或者固定saveItem以此类推、updateItem、removeItem、getItem(当个)、listItem(listItems 复数,用不用复数要统一)。

参考