(1)读取MyBatis的配置文件。mybatis-config.xml为MyBatis的全局配置文件,用于配置数据库连接信息。加载配置方式(后加载的配置会覆盖之前的配置)。
(2)加载映射文件。映射文件即SQL映射文件,该文件中配置了操作数据库的SQL语句,需要在MyBatis配置文件mybatis-config.xml中加载。mybatis-config.xml 文件可以加载多个映射文件,每个文件对应数据库中的一张表。
(3)构造会话工厂。通过MyBatis的环境配置信息构建会话工厂SqlSessionFactory(单例或静态单例)。
(4)创建会话对象。由会话工厂创建SqlSession对象,该对象中包含了执行SQL语句的所有方法(工厂)。
(5)Executor执行器。MyBatis底层定义了一个Executor接口来操作数据库,它将根据SqlSession传递的参数动态地生成需要执行的SQL语句,同时负责查询缓存的维护(动态代理)。
(6)MappedStatement对象。在Executor接口的执行方法中有一个MappedStatement类型的参数,该参数是对映射信息的封装,用于存储要映射的SQL语句的id、参数等信息。
(7)输入参数映射。输入参数类型可以是Map、List等集合类型,也可以是基本数据类型和POJO类型。输入参数映射过程类似于JDBC对preparedStatement对象设置参数的过程。
(8)输出结果映射。输出结果类型可以是Map、List等集合类型,也可以是基本数据类型和POJO类型。输出结果映射过程类似于JDBC对结果集的解析过程。
MyBatis 允许你在映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
通过 MyBatis 提供的强大机制,使用插件是非常简单的,只需实现 Interceptor 接口,并指定想要拦截的方法签名即可。
在很多业务场景下我们需要对查询sql做一定的处理,但是又不能过度入侵原有业务。比如:分页操作,数据权限过滤操作,统一异常处理,SQL执行时间性能监控等等,这里我们就可以用到Mybatis的拦截器Interceptor。
通过mybatis插件,捕获所有sql执行异常,统一做异常处理(连接数据库操作不在范围内,connect被框架实现了)。
封装的sdk地址。框架在怎么扩展都好说。
import com.sky.common.exception.domain.SqlExceptionLog;
import com.sky.common.exception.exception.UniteSqlException;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.beans.factory.annotation.Value;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Date;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Mybatis拦截器. 拦截SQL执行时的异常,写入DB.
*/
@Slf4j
@Intercepts({
//插入和删除底层都是通过update实现的
@Signature(type = Executor.class, method = "update", args = {
MappedStatement.class, Object.class}),
@Signature(type = Executor.class, method = "query", args = {
MappedStatement.class, Object.class, RowBounds.class,
ResultHandler.class})})
public class SqlExceptionInterceptor implements Interceptor {
@Value("${dubbo.application.name}")
private String application;
/**
* 线程池.
*/
private static ExecutorService executor = Executors.newFixedThreadPool(5);
/**
* 拦截方法.
*/
public Object intercept(Invocation invocation) throws Throwable {
try {
return invocation.proceed();
} catch (Exception e) {
// 日志对象
final SqlExceptionLog record = new SqlExceptionLog();
try {
// 取得各种值
MappedStatement statement = (MappedStatement) invocation
.getArgs()[0];
Object parameter = invocation.getArgs()[1];
BoundSql boundSql = statement.getBoundSql(parameter);
// 防止死循环
if (statement.getId().toUpperCase()
.contains("sqlExceptionLogMapper".toUpperCase())) {
throw e;
}
record.setExceptionMessage(e.getCause().toString());
record.setExceptionStack(getExceptionStackTrace(e));
record.setCreateTime(new Date());
record.setSystemName(application);
record.setSqlId(statement.getId());
record.setSqlParameter(parameter.toString());
record.setSqlSource(boundSql.getSql());
record.setSqlType(statement.getSqlCommandType().toString());
executor.execute(new Runnable() {
@Override
public void run() {
}
});
log.warn("SQL异常处理", application, "【异常信息:{},异常点:{},时间:{}】", record.getExceptionMessage(), record.getSqlId(), record.getCreateTime());
} catch (Exception ex) {
log.error("SQL异常处理", application, ex, "【拦截异常信息处理失败,失败原因:{}】", ex.getCause().toString());
// ex.printStackTrace();
}
// 抛出异常
throw new UniteSqlException(record.toString());
}
}
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
/**
* 配置文件读取属性.
*/
public void setProperties(Properties properties) {
}
/**
* 获取异常的堆栈信息.
*
* @param e
* @return
*/
private String getExceptionStackTrace(Exception e) {
ByteArrayOutputStream buf = new ByteArrayOutputStream();
e.printStackTrace(new java.io.PrintWriter(buf, true));
String expMessage = buf.toString();
try {
buf.close();
} catch (IOException ex) {
}
return expMessage;
}
}
一个sdk封装并没有什么太大的价值。更多的是扩展思考吧。
mybatis中用的设计模式,可能不全,毕竟看的还不是很通透,随时补充吧。
用途:Mybatis中日志模块。
Mybatis 通过使用内置的日志工厂提供日志功能。内置日志工厂将会把日志工作委托给下面的实现之一:
MyBatis 内置日志工厂会基于运行时检测信息选择日志委托实现。它会(按上面罗列的顺序)使用第一个查找到的实现。当没有找到这些实现时,将会禁用日志功能。
你可以通过在 MyBatis 配置文件 mybatis-config.xml 里面添加一项 setting 来选择其它日志实现。
<configuration>
<settings>
...
<setting name="logImpl" value="LOG4J"/>
...
settings>
configuration>
日志打印,sql執行。Mybatis实现的核心,比如MapperProxy、ConnectionLogger,用的jdk的动态代理;还有executor.loader包使用了cglib或者javassist达到延迟加载(如果查询只有马上遍历集合,会马上触发加载)的效果。
mybatis中数据源,例如SqlSessionFactory、ObjectFactory、MapperProxyFactory。
mybatis中的缓存模块,例如Cache包中的cache.decorators子包中等各个装饰者的实现。
IO中输入流和输出流的设计就是完美的体现。
Mybatis的初始化,例如SqlSessionFactoryBuilder、XMLConfigBuilder、XMLMapperBuilder、XMLStatementBuilder、CacheBuilder。
创建SqlSession。
Executor查询操作流程,例如BaseExecutor和SimpleExecutor,还有BaseTypeHandler和所有的子类例如IntegerTypeHandler。
例如ErrorContext和LogFactory。加载配置文件SqlSessionFactory,基本是单例或者静态单例。
例如SqlNode和各个子类ChooseSqlNode等。
mybatis的源码很适合小白阅读,内容不是很复杂,而且用法很经典,包括一些处理逻辑,设计模式的运用。恰到好处。渐渐真香。慢慢学习。