欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

MyBatis技术初探

程序员文章站 2022-06-08 17:34:06
...

初始化

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);
}
}
模板流程,如果符合拦截条件,执行拦截器,否则,通过反射执行目标方法.

相关标签: 程序人生