MyBatis技术初探

初始化

String resource = “org/mybatis/example/mybatis-config.xml”;
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
创建SqlSessionFactory 过程中,mybatis生成了一个重要的对象Configuration,该对象存储了框架运行的所有配置信息.下面摘取几个重要点列举一下:

protected Environment environment;//数据源和事务工厂持有者,mybatis业务环境
protected MapperRegistry mapperRegistry = new MapperRegistry(this);//Mapper接口注册表
protected final InterceptorChain interceptorChain = new InterceptorChain();//拦截器链,插件注册表
protected final Map<String, ResultMap> resultMaps = new StrictMap(“Result Maps collection”);//bean属性和数据库属性映射
protected final Map<String, MappedStatement> mappedStatements = new StrictMap(“Mapped Statements collection”);//xml每条sql配置,对应一个mappedstatement,sql语句注册表

了解了初始化配置信息后,再说一下mybatis的使用:
1.直接使用字符串key执行sql,方式如下,
SqlSession session = sqlSessionFactory.openSession();
session.selectOne(“org.mybatis.example.BlogMapper.selectBlog”, 101);

session创建核心:
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
final Executor executor = configuration.newExecutor(tx, execType, autoCommit);
return new DefaultSqlSession(configuration, executor);

从以上代码可以看出,为每个session开启一个单独事务,并且初始化一个执行器.

selectOne核心步骤:
MappedStatement ms = configuration.getMappedStatement(statement);
List result = executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
可以看出,从初始化配置的sql语句注册表中,按字符串key取出ms对象,然后使用执行器,进行执行;

2.通过获取Mapper执行,使用方式如下
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlog(101);
先说一下getMapper发生了什么:
public T getMapper(Class type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
可以看出,是从初始化时,mapper注册表取出mapper,那么再进一步看一下:
public T getMapper(Class type, SqlSession sqlSession) {
final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) 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);
}
}
从knownMappers这个hashmap中按接口类型提取到一个MapperProxyFactory对象,然后进一步创建一个代理:
public T newInstance(SqlSession sqlSession) {
final MapperProxy mapperProxy = new MapperProxy(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
protected T newInstance(MapperProxy mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
以上两步调用可以看出创建了一个指定Mapper接口的代理,所有对接口的调用都委托给MapperProxy执行;那么Blog blog = mapper.selectBlog(101);执行时,我们把该接口的调用委托给MapperProxy执行,看一下:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
}
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;
}
第一次执行时,新创建一个MapperMethod并缓存,来看一下具体的执行:
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
if (SqlCommandType.INSERT == command.getType()) {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
} else if (SqlCommandType.UPDATE == command.getType()) {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
} else if (SqlCommandType.DELETE == command.getType()) {…代码较长,不再列举.

接下来说一下与spring的整合当中,Mapper是如何注入到spring容器中的:
再spring的配置文件中配置扫描类org.mybatis.spring.mapper.MapperScannerConfigurer,看一下这个类是如何工作的:
public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware ;//类的签名,可以看出该类实现了BeanDefinitionRegistryPostProcessor,该接口赋予了增改beandefinition的能力.看一下如何更改bean定义的,
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
if (this.processPropertyPlaceHolders) {
processPropertyPlaceHolders();
}

ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
scanner.setAddToConfig(this.addToConfig);
scanner.setAnnotationClass(this.annotationClass);
scanner.setMarkerInterface(this.markerInterface);
scanner.setSqlSessionFactory(this.sqlSessionFactory);
scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
scanner.setResourceLoader(this.applicationContext);
scanner.setBeanNameGenerator(this.nameGenerator);
scanner.registerFilters();
scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));

}
配置了一个ClassPathMapperScanner类,以及必要的信息,看一下扫描过程:
Set beanDefinitions = super.doScan(basePackages);//调用spring框架注解扫描
definition.setBeanClass(MapperFactoryBean.class);//对扫描结果的beanclass赋值为MapperFactoryBean,这是一个实现了FactoryBean的工厂类,spring会调用getObject()方法返回对象实体,
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
可以看到MapperFactoryBean从sqlSession获取一个MapperProxy对象返回给spring容器.

接下来说一下mybatis的插件实现:
mybatis提供了四个插件切入点:
executor = (Executor) interceptorChain.pluginAll(executor);
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
看一下具体的pluginAll,
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}

再使用中我们需要自定义一个插件类,实现Interceptor 接口,然后再plugin方法中,使用Plugin.wrap(target,this)进行包装,看一下wrap是如何工作的:
public static Object wrap(Object target, Interceptor interceptor) {
Map<Class<?>, Set> signatureMap = getSignatureMap(interceptor);
Class<?> type = target.getClass();
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
if (interfaces.length > 0) {
return Proxy.newProxyInstance(
type.getClassLoader(),
interfaces,
new Plugin(target, interceptor, signatureMap));
}
return target;
}
可以看出返回了一个代理对象,通过wrap包装的四种类型所有调用,都会委托给Plugin执行;
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
Set methods = signatureMap.get(method.getDeclaringClass());
if (methods != null && methods.contains(method)) {
return interceptor.intercept(new Invocation(target, method, args));
}
return method.invoke(target, args);
} catch (Exception e) {
throw ExceptionUtil.unwrapThrowable(e);
}
}
模板流程,如果符合拦截条件,执行拦截器,否则,通过反射执行目标方法.

猜你喜欢

转载自blog.csdn.net/strive____/article/details/90768683