0%

mybatis之Sql绑定

Sql绑定

在mybatis中定义一个接口,然后在mapper.xml文件中编写一个sql语句,在执行该接口中方法的时候就会执行该sql语句,这是怎么做到的呢?

1
2
3
public interface UserMapper{
public User getUser(int i);// 在mapper.xml中写一个<select>标签,id为getUser
}

MapperRegistry

MapperRegistry类是Mapper接口及其对应的代理对象工厂的注册中心。

1
2
3
4
// 全局配置
private final Configuration config;
// 记录Mapper接口和代理工厂之间的关系
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap();

在mybatis初始化时读取配置文件以及Mapper接口的注解信息,调用MapperRegistry.addMapper()方法填充knownMappers。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
if (this.hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
this.knownMappers.put(type, new MapperProxyFactory(type));
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(this.config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
this.knownMappers.remove(type);
}
}
}
}

在需要执行某个SQL时,会先调用MapperRegistry.getMapper()方法获取实现Mapper接口的代理对象

1
2
3
4
5
6
7
8
9
10
11
12
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
} else {
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception var5) {
throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
}
}
}

MapperProxyFactory

MapperProxyFactory负责为mapper接口创建代理对象MapperProxy

1
2
3
4
5
// 接口
private final Class<T> mapperInterface;
// 缓存,key为mapperInterface接口中某方法所对应的Method对象,value是对应的代理
private final Map<Method, MapperMethodInvoker> methodCache = new ConcurrentHashMap();

1
2
3
4
5
6
7
8
9
protected T newInstance(MapperProxy<T> mapperProxy) {
return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}

public T newInstance(SqlSession sqlSession) {
MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
return this.newInstance(mapperProxy);
}

MapperProxy

实现了InvocationHandler接口,是接口的代理类,主要看invoke()方法,如果是接口,会调用cachedInvoker()方法生成代理类MapperMethodInvoker去执行

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
public class MapperProxy<T> implements InvocationHandler, Serializable {

private static final long serialVersionUID = -4724728412955527868L;
private static final int ALLOWED_MODES = MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
| MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC;
private static final Constructor<Lookup> lookupConstructor;
private static final Method privateLookupInMethod;
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
private final Map<Method, MapperMethodInvoker> methodCache;

public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethodInvoker> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}

static {
Method privateLookupIn;
try {
//java8的MethodHandles中没有privateLookupIn方法 privateLookupInMethod会是null
privateLookupIn = MethodHandles.class.getMethod("privateLookupIn", Class.class, MethodHandles.Lookup.class);
} catch (NoSuchMethodException e) {
privateLookupIn = null;
}
privateLookupInMethod = privateLookupIn;

Constructor<Lookup> lookup = null;
if (privateLookupInMethod == null) {
// JDK 1.8
try {
lookup = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);
lookup.setAccessible(true);
} catch (NoSuchMethodException e) {
throw new IllegalStateException(
"There is neither 'privateLookupIn(Class, Lookup)' nor 'Lookup(Class, int)' method in java.lang.invoke.MethodHandles.",
e);
} catch (Exception e) {
lookup = null;
}
}
lookupConstructor = lookup;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
// 是代理对象,直接执行invoke方法
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else { // 如果是接口不是代理对象的话,就会生成MapperMethodInvoker对象
return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}

private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
try {
// A workaround for https://bugs.openjdk.java.net/browse/JDK-8161372
// It should be removed once the fix is backported to Java 8 or
// MyBatis drops Java 8 support. See gh-1929
MapperMethodInvoker invoker = methodCache.get(method);
if (invoker != null) {
return invoker;
}

return methodCache.computeIfAbsent(method, m -> {
// 默认方法(Java8之后出的默认方法)
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 MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}
});
} catch (RuntimeException re) {
Throwable cause = re.getCause();
throw cause == null ? re : cause;
}
}

private MethodHandle getMethodHandleJava9(Method method)
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
final Class<?> declaringClass = method.getDeclaringClass();
return ((Lookup) privateLookupInMethod.invoke(null, declaringClass, MethodHandles.lookup())).findSpecial(
declaringClass, method.getName(), MethodType.methodType(method.getReturnType(), method.getParameterTypes()),
declaringClass);
}

private MethodHandle getMethodHandleJava8(Method method)
throws IllegalAccessException, InstantiationException, InvocationTargetException {
final Class<?> declaringClass = method.getDeclaringClass();
return lookupConstructor.newInstance(declaringClass, ALLOWED_MODES).unreflectSpecial(method, declaringClass);
}

interface MapperMethodInvoker {
Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable;
}
}
MapperMethodInvoker

MapperMethodInvoker有两个实现类,PlainMethodInvoker和DefaultMethodInvoker

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
// 普通的接口方法会执行
//PlainMethodInvoker只是对于MapperMethod进行了一下包装,真正使用的还是MapperMethod对象
private static class PlainMethodInvoker implements MapperMethodInvoker {
private final MapperMethod mapperMethod;

public PlainMethodInvoker(MapperMethod mapperMethod) {
super();
this.mapperMethod = mapperMethod;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
return mapperMethod.execute(sqlSession, args);
}
}

// java8中的默认方法会执行
private static class DefaultMethodInvoker implements MapperMethodInvoker {
private final MethodHandle methodHandle;

public DefaultMethodInvoker(MethodHandle methodHandle) {
super();
this.methodHandle = methodHandle;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
return methodHandle.bindTo(proxy).invokeWithArguments(args);
}
}
MapperMethod

MapperMethod中封装了Mapper接口对应的方法的信息,以及对应SQL语句的信息

1
2
3
4
// 记录了SQL语句的名称和类型
private final MapperMethod.SqlCommand command;
// 记录了Mapper接口中对应方法的相关信息
private final MapperMethod.MethodSignature method;

SqlCommandType记录了SQL语句的类型

1
2
3
4
5
6
7
8
9
10
11
public enum SqlCommandType {
UNKNOWN,
INSERT,
UPDATE,
DELETE,
SELECT,
FLUSH;

private SqlCommandType() {
}
}

根据PlainMethodInvoker中invoke方法发现,调用的是MapperMethodexecute方法

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
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
Object param;
// 根据SQL类型调用不同的sqlsession的方法
switch(this.command.getType()) {
case INSERT:
// 使用ParamNameResolver处理参数列表,将实参与指定参数名对应起来
param = this.method.convertArgsToSqlCommandParam(args);
// rowCountResult会根据method字段记录的方法的返回值类型对结果进行转换
result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
break;
case UPDATE:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
break;
case DELETE:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
break;
case SELECT:
// 处理返回值为void且ResultSet通过ResultHandler处理的方法
if (this.method.returnsVoid() && this.method.hasResultHandler()) {
this.executeWithResultHandler(sqlSession, args);
result = null;
}
// 处理返回值为集合的方法
else if (this.method.returnsMany()) {
result = this.executeForMany(sqlSession, args);
}
// 处理返回值为map的方法
else if (this.method.returnsMap()) {
result = this.executeForMap(sqlSession, args);
}
// 处理返回值为Cursor的方法
else if (this.method.returnsCursor()) {
result = this.executeForCursor(sqlSession, args);
}
// 处理返回值为单一对象的方法
else {
param = this.method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(this.command.getName(), param);
if (this.method.returnsOptional() && (result == null || !this.method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + this.command.getName());
}

if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {
throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");
} else {
return result;
}
}

欢迎关注我的其它发布渠道