Mybatis基本流程理解(详情点击)
引入
首先那一段最简单的mybatis调用的代码块来说明
{
SqlSession session = null;
String resource = "configuration.xml";
// 使用io流读取配置
InputStream inputStream;
inputStream = Resources.getResourceAsStream(resource);
//这里是解析配置文件
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 得到了一个会话,有了这个会话,你就可以对数据进行增,删,改,查的操作
session = sqlSessionFactory.openSession();
}
XMLConfigBuilder部分方法
private void parseConfiguration(XNode root) {
try {
// issue #117 read properties first
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
loadCustomLogImpl(settings);
typeAliasesElement(root.evalNode("typeAliases"));
//你的接口实现类似在这一步就解析了
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
private void pluginElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
String interceptor = child.getStringAttribute("interceptor");
Properties properties = child.getChildrenAsProperties();
Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor().newInstance();
interceptorInstance.setProperties(properties);
//添加拦截器链
configuration.addInterceptor(interceptorInstance);
}
}
}
四大对象(StatementHandler,ParameterHandler,ResultHandler,Executor)创建过程:
只拿Parameterhandler示例
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
//插件方法
parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
return parameterHandler;
}
public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
ResultHandler resultHandler, BoundSql boundSql) {
ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
//插件方法
resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
return resultSetHandler;
}
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
//插件方法
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
总结(顺序如下图):
四大对象每个创建的时候都有一个interceptorChain.pluginAll(parameterHandler),每个对象不是直接返回的,而是调用interceptorChain.pluginAll(),获取到所有的Interceptor,调用interceptor.plugin(target),返回target包装后的对象
插件机制:可以使用插件为目标对象创建一个代理对象,代理对象会拦截每一个对象的执行。
应用Interceptor插件
书写Interceptor实现类+注解@Intercepts
/**
* @作者 five-five
* @创建时间 2021/1/13
*/
//说明:我们使用插件拦截,都是拦截四大对象,其中@Intercepts注解是数组,所以可以写多个拦截的值
//type: 四大对象的那个(传入Class)
//method: 方法名
//args: 方法的参数
@Intercepts({
@Signature(type = Executor.class, method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
@Signature(type=StatementHandler.class,method="parameterize",args={Statement.class})
})
public class MyTestExecutePlugin implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
Object target = invocation.getTarget();//目标方法
System.out.println("目标对象:" + target);
//这个方法是mybatis提供的,得到元数据,使用getValue()可以拿到getter和setter的值
MetaObject metaObject = SystemMetaObject.forObject(target);
metaObject.getValue("boundSql.sql");
metaObject.getValue("configuration.environment");
Object proceed = invocation.proceed();//拦截器放行
return proceed;//返回的就是查询到的对象
}
@Override
public Object plugin(Object target) {
//target:核心对象
//StatementHandler ParameterHandler ResultSetHandler Executor
//将拦截器中定义的增强功能和某一个核心对象合并起来,称为最终的核心对象
Object wrap = Plugin.wrap(target, this);
System.out.println(wrap);
return wrap;
}
@Override
public void setProperties(Properties properties) {
// 属性值
System.out.println(properties);
}
}
mybatisconf.xml配置
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 开启懒加载 -->
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false" />
<setting name="jdbcTypeForNull" value="NULL" /><!-- 不设置这个参数的话有一些数据库不能识别null,如oracle数据库 -->
<setting name="logImpl" value="log4j" />
</settings>
<!-- <typeAliases>-->
<!-- <package name="com.edu.bean"/>-->
<!-- </typeAliases>-->
<!-- 配置分页插件 5之后的版本:com.github.pagehelper.PageInterceptor -->
<plugins>
<!--自定义的一个分页插件-->
<!-- <plugin interceptor="com.edu.mybatisIntercept.MyPagePlugin"></plugin>-->
<!--旨在测试-->
<plugin interceptor="com.edu.mybatisIntercept.MyTestExecutePlugin"></plugin>
<!--分页插件-->
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<property name="reasonable" value="true"/>
</plugin>
</plugins>
</configuration>
其中metadate
元数据是最重要的,下面来看看这个元数据在Mybatis中有什么样的方法
MetaObject对象
public class MetaObject {
public static MetaObject forObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
if (object == null) {
return SystemMetaObject.NULL_META_OBJECT;
} else {
return new MetaObject(object, objectFactory, objectWrapperFactory, reflectorFactory);
}
}
public ObjectFactory getObjectFactory() {
return objectFactory;
}
public ObjectWrapperFactory getObjectWrapperFactory() {
return objectWrapperFactory;
}
public ReflectorFactory getReflectorFactory() {
return reflectorFactory;
}
public Object getOriginalObject() {
return originalObject;
}
public String findProperty(String propName, boolean useCamelCaseMapping) {
return objectWrapper.findProperty(propName, useCamelCaseMapping);
}
public String[] getGetterNames() {
return objectWrapper.getGetterNames();
}
public String[] getSetterNames() {
return objectWrapper.getSetterNames();
}
public Class<?> getSetterType(String name) {
return objectWrapper.getSetterType(name);
}
public Class<?> getGetterType(String name) {
return objectWrapper.getGetterType(name);
}
public boolean hasSetter(String name) {
return objectWrapper.hasSetter(name);
}
public boolean hasGetter(String name) {
return objectWrapper.hasGetter(name);
}
public Object getValue(String name) {
PropertyTokenizer prop = new PropertyTokenizer(name);
if (prop.hasNext()) {
MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
return null;
} else {
return metaValue.getValue(prop.getChildren());
}
} else {
return objectWrapper.get(prop);
}
}
public void setValue(String name, Object value) {
PropertyTokenizer prop = new PropertyTokenizer(name);
if (prop.hasNext()) {
MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
if (value == null) {
// don't instantiate child path if value is null
return;
} else {
metaValue = objectWrapper.instantiatePropertyValue(name, prop, objectFactory);
}
}
metaValue.setValue(prop.getChildren(), value);
} else {
objectWrapper.set(prop, value);
}
}
public MetaObject metaObjectForProperty(String name) {
Object value = getValue(name);
return MetaObject.forObject(value, objectFactory, objectWrapperFactory, reflectorFactory);
}
public ObjectWrapper getObjectWrapper() {
return objectWrapper;
}
public boolean isCollection() {
return objectWrapper.isCollection();
}
public void add(Object element) {
objectWrapper.add(element);
}
public <E> void addAll(List<E> list) {
objectWrapper.addAll(list);
}
}
看源码可以知道,这个MetaObject有点类似于一个反射工具类,其实现也是利用的反射获取实例的 getter 和 setter 方法进行赋值。而我们用的最多的就是getValue()和setValue();下面我们来看看有哪些属性,我们是可以获得的。
目前我只成功了拿boundSql
属性
BoundSql
对象的属性:
private final String sql;//sql语句
private final List<ParameterMapping> parameterMappings;//参数映射
private final Object parameterObject;//参数
private final Map<String, Object> additionalParameters;
private final MetaObject metaParameters;//源参数