by shihang.mai
1. mybatis的简单实用
我们使用mybatis的时候,首先定义接口,可以选择在接口中直接用注解的方式写上sql
@Mapper
public interface StudentMapper {
@Select("select id, name,age from student")
List<Student> queryStudents();
}
或者用xml方式写sql
<mapper namespace="com.shihangmai.test.mapper.StudentMapper">
<resultMap id="studentMap" type="com.shihangmai.test.Student">
<result column="id" property="id" />
<result column="name" property="name" />
<result column="age" property="age" />
</resultMap>
<select id="queryStudents" resultMap="studentMap" >
select id, name,age from student
</select>
</mapper>
使用的时候,直接用接口.方法就可以了
//使用接口
public class StudentImpl {
@Autowired
StudentMapper studentMapper;
public void test(){
studentMapper.queryStudents()
}
}
那当然配置mybatis肯定需要咯,这里用springboot,配置文件中配置sql xml路径
mybatis:
configuration:
cache-enabled: false
map-underscore-to-camel-case: true
use-generated-keys: true
default-executor-type: reuse
default-statement-timeout: 60
mapper-locations: classpath:sqlmapper/*Mapper.xml
启动类上写上扫面接口的路径
@MapperScan("com.shihangmai.test.mapper")
@SpringBootApplication
public class Application {
public static void main(String[] args) {
ApplicationContext a =SpringApplication.run(CacheApplication.class, args);
}
}
2. 原理分析
那为什么只用接口就可以执行sql返回数据呢?
我们传统用mybatis,都先通过
- Resources.getResourceAsStream(sql xml文件)获取SqlSessionFactory
- 通过SqlSessionFactory.openSession()获取SqlSession
- 再通过SqlSession.getMapper(类.class)获取接口对象,再点方法就可以用了;
所以关键点在于SqlSession.getMapper是如何获取到接口对象的,接下来分析源码,sqlSession使用的是默认实现类DefaultSqlSession
- DefaultSqlSession. getMapper(Class<T> type)调用Configuration.getMapper(Class<T> type, SqlSession sqlSession)
//DefaultSqlSession类
@Override
public <T> T getMapper(Class<T> type) {
return configuration.<T>getMapper(type, this);
}
//Configuration类
protected MapperRegistry mapperRegistry = new MapperRegistry(this);
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
- 接着调用MapperRegistry.getMapper(Class<T> type, SqlSession sqlSession),其中MapperRegistry中包含Configuratio。这里会通过Class获取MapperProxyFactory.获取MapperProxyFactory后调用其newInstance(SqlSession sqlSession)
//MapperRegistry类
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
- MapperProxyFactory. newInstance(SqlSession sqlSession)中构建MapperProxy,再调用一个重载方法newInstance(MapperProxy<T> mapperProxy)
//MapperProxyFactory
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;
}
public Class<T> getMapperInterface() {
return mapperInterface;
}
public Map<Method, MapperMethod> getMethodCache() {
return methodCache;
}
@SuppressWarnings("unchecked")
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);
}
}
至此看出返回了一个接口的代理对象,并且InvocationHandler是mapperProxy
- 我们来看看MapperProxy的invoke,这个方法会在代理对象调用方法时调起,先放到从cachedMapperMethod中获取,没有就缓存,并调起MapperMethod.execute方法
public class MapperProxy<T> implements InvocationHandler, Serializable {
private static final long serialVersionUID = -6424540398559729838L;
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache;
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
try {
return method.invoke(this, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) {
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
}
3. 总结
流程图- getMapper方法实质上是获取一个JDK动态代理对象,这个代理类会继承MapperProxy类,实现被代理的接口,如StudentMapper,并且里面持有一个MapperProxy类型的触发管理类
- 拿到代理类后,调用方法时,调起MapperProxy的invoke,invoke里面
- cachedMapperMethod,缓存method对应的信息,包含configuration,inf
- MapperMethod执行sql并返回结果, MapperMethod包含sql、sql需要的参数、方法签名
- springboot已经帮我们做了读取xml解析为配置等等.我们直接@Autowired时,放入的是代理对象,调用方法时执行2
网友评论