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负责创建代理对象

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
// 接口
private final Class<T> mapperInterface;
// 缓存,key为mapperInterface接口中某方法所对应的Method对象,value是对应的代理
private final Map<Method, MapperMethodInvoker> methodCache = new ConcurrentHashMap();

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

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

lookupConstructor = lookup;
}
1
2
3
4
5
6
7
8
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);
}

MethodProxy

实现了InvocationHandler接口,是接口的代理类,主要看invoke()方法

1
2
3
4
5
6
7
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
return Object.class.equals(method.getDeclaringClass()) ? method.invoke(this, args) : this.cachedInvoker(method).invoke(proxy, method, args, this.sqlSession);
} catch (Throwable var5) {
throw ExceptionUtil.unwrapThrowable(var5);
}
}

invoke方法会调用cachedInvoker()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private MapperProxy.MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
try {
MapperProxy.MapperMethodInvoker invoker = (MapperProxy.MapperMethodInvoker)this.methodCache.get(method);
return invoker != null ? invoker : (MapperProxy.MapperMethodInvoker)this.methodCache.computeIfAbsent(method, (m) -> {
// 默认方法(Java8之后出的默认方法)
if (m.isDefault()) {
try {
return privateLookupInMethod == null ? new MapperProxy.DefaultMethodInvoker(this.getMethodHandleJava8(method)) : new MapperProxy.DefaultMethodInvoker(this.getMethodHandleJava9(method));
} catch (InstantiationException | InvocationTargetException | NoSuchMethodException | IllegalAccessException var4) {
throw new RuntimeException(var4);
}
}
// 普通的接口方法
else {
return new MapperProxy.PlainMethodInvoker(new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration()));
}
});
} catch (RuntimeException var4) {
Throwable cause = var4.getCause();
throw (Throwable)(cause == null ? var4 : cause);
}
}

在这里只看执行普通方法的情况,所以需要看一下MapperProxy中的静态内部类PlainMethodInvoker

1
2
3
4
5
6
7
8
9
10
11
12
//PlainMethodInvoker只是对于MethodHandle进行了一下包装,真正使用的还是MethodHandle对象
private static class PlainMethodInvoker implements MapperProxy.MapperMethodInvoker {
private final MapperMethod mapperMethod;

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

public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
return this.mapperMethod.execute(sqlSession, 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;
}
}