Mybatis 第一讲
Mapper的生成
org.mybatis.spring.annotation.MapperScan
在我们的开发过程中,与数据库交互我们第一步大多数都是从这个注解开始,这个注解表示需要扫描一些类加载到Spring 的bean 容器里进行管理。
java
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
/**
* {@inheritDoc}
*
* @deprecated Since 2.0.2, this method not used never.
*/
@Override
@Deprecated
public void setResourceLoader(ResourceLoader resourceLoader) {
// NOP
}
/**
* {@inheritDoc}
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AnnotationAttributes mapperScanAttrs = AnnotationAttributes
.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
if (mapperScanAttrs != null) {
registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry,
generateBaseBeanName(importingClassMetadata, 0));
}
}
// 注册扫描配置类
void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs,
BeanDefinitionRegistry registry, String beanName) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
}
}
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
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
在做依赖的时候需要注入Mapper ,需要一个SqlSessionFactory ,但是一开始的Bean容器是不存在SqlSessionFactory的,这个就需要去构建他,SqlSessionFactory 实现了InitializingBean 接口会自动触发 afterPropertiesSet(), 在里面他会开始向Bean 容器正式注册Mapper,并且在mybatis 的Configuration 也注册Bean 和 Method
java
try {
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());
xmlMapperBuilder.parse();
} catch (Exception e) {
throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
} finally {
ErrorContext.instance().reset();
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
调用到后面会调用到一个addMapper()
java
mybatisMapperRegistry.addMapper(type);
knownMappers.put(type, new MybatisMapperProxyFactory<>(type));
1
2
3
2
3
会存放进已知mapper ,这个MybatisMapperProxyFactory
java
public class MybatisMapperProxyFactory<T> {
@Getter
private final Class<T> mapperInterface;
@Getter
private final Map<Method, MybatisMapperProxy.MapperMethodInvoker> methodCache = new ConcurrentHashMap<>();
public MybatisMapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
@SuppressWarnings("unchecked")
protected T newInstance(MybatisMapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MybatisMapperProxy<T> mapperProxy = new MybatisMapperProxy<>(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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
他其实是JDK动态代理 注入到knownMappers, 以后获取Mapper 都是从这个knownMappers 里面获取
java
@Override
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
// TODO 这里换成 MybatisMapperProxyFactory 而不是 MapperProxyFactory
final MybatisMapperProxyFactory<T> mapperProxyFactory = (MybatisMapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MybatisPlusMapperRegistry.");
}
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
方法的调用
以list()
方法为例,在调用Mapper 的方法,我们知道因为JDK动态代理其实是在调用MapperProxy 的invoke()
方法。
java
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
// 是否是Object 的原生方法,是的话就掉原生的方法
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else {
// 不是那就去缓存先找一下方法,然后在调用
return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
try {
// 去找一下试试,没找到就添加进缓存
return CollectionUtils.computeIfAbsent(methodCache, method, m -> {
if (m.isDefault()) {
// 默认方法的调用
try {
if (privateLookupInMethod == null) {
return new DefaultMethodInvoker(getMethodHandleJava8(method));
} else {
return new DefaultMethodInvoker(getMethodHandleJava9(method));
}
} catch (IllegalAccessException | InstantiationException | InvocationTargetException
| NoSuchMethodException e) {
throw new RuntimeException(e);
}
} else {
// 主要看这里
return new PlainMethodInvoker(new MybatisMapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}
});
} catch (RuntimeException re) {
Throwable cause = re.getCause();
throw cause == null ? re : cause;
}
}
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
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
通过返回的PlainMethodInvoker
的invoke()
方法触发缓存Method 的调用。MybatisMapperMethod
这个类主要是对方法的元数据的补充,和实际执行方法的封装execute(SqlSession sqlSession, Object[] args)
java
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
2
3
4
5
6
7
8
9
10
11
我们知道Mybtais是有缓存的,就在这个query 的方法处理里面
java
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameterObject);
// 拿到缓存的Key
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
Cache cache = ms.getCache();
Optional<IPage> pageOptional = ParameterUtils.findPage(parameterObject);
// 缓存处理 没有就不进
if (cache != null) {
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, boundSql);
Object result = tcm.getObject(cache, key);
if (result == null) {
if (pageOptional.isPresent()) {
IPage page = pageOptional.get();
CacheKey countCacheKey = null;
if (page.isSearchCount()) {
// 这里的执行sql为原select语句,标准一点的是需要将此转换为count语句当做缓存key的,留做当优化把.
countCacheKey = getCountCacheKey(ms, boundSql, parameterObject, RowBounds.DEFAULT);
// 复用count结果缓存,减少count查询.
Number count = (Number) tcm.getObject(cache, countCacheKey);
if (count != null) {
page.hitCount(true);
page.setTotal(count.longValue());
}
}
// 切勿将这提取至上方,如果先查的话,需要提前将boundSql拷贝一份
result = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
List<E> records = (List<E>) result;
page.setRecords(records);
tcm.putObject(cache, key, records);
if (countCacheKey != null && !page.isHitCount()) {
tcm.putObject(cache, countCacheKey, page.getTotal());
}
return new PageList(records, page.getTotal());
} else {
result = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
// issue #578 and #116
tcm.putObject(cache, key, result);
return (List<E>) result;
}
} else {
if (pageOptional.isPresent()) {
IPage page = pageOptional.get();
if (page.isSearchCount()) {
CacheKey cacheKey = getCountCacheKey(ms, boundSql, parameterObject, RowBounds.DEFAULT);
Number count = (Number) tcm.getObject(cache, cacheKey);
if (count != null) {
page.hitCount(true);
return new PageList((List) result, count.longValue());
} else {
// 某些特殊情况,比如先不查count,缓存了list数据或者count缓存数据被淘汰(这几率比较小),就再查一次算了。
result = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
List<E> records = (List<E>) result;
tcm.putObject(cache, cacheKey, page.getTotal());
return records;
}
}
return new PageList((List) result, 0L);
} else {
return (List<E>) result;
}
}
}
}
// 没有缓存走这里
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
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
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
之后交给BaseExecutor#queryFromDatabase
去执行查询。
java
@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(), false);
// 执行SQL
return stmt == null ? Collections.emptyList() : handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
参数的处理
java
public class DynamicSqlSource implements SqlSource {
@Override
public BoundSql getBoundSql(Object parameterObject) {
DynamicContext context = new DynamicContext(configuration, parameterObject);
rootSqlNode.apply(context);
SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
context.getBindings().forEach(boundSql::setAdditionalParameter);
return boundSql;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
java
public class SqlSourceBuilder extends BaseBuilder {
public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {
ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);
GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
String sql;
if (configuration.isShrinkWhitespacesInSql()) {
sql = parser.parse(removeExtraWhitespaces(originalSql));
} else {
sql = parser.parse(originalSql);
}
return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14