Mybatis的使用与源码分析
1.Mybatis整合到SpringBoot项目
1.1 引入依赖
如果使用最基础级mybatis,使用以下依赖:
<!-- mybatis依赖-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.version}</version>
</dependency>
如果使用通用mapper,使用以下依赖:
<!-- 通用mapper依赖 -->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>${version}</version>
</dependency>
如果使用mybatis-plus,使用以下依赖:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${version}</version>
</dependency>
如果使用mybatis-plus插件生成代码,使用以下依赖:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>${version}</version>
</dependency>
<!-- 以下依赖与选择的代码模板引擎有关,不同引擎选择不同模板-->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>${version}</version>
</dependency>
1.2 增加Config
@Configuration
public class MybatisConfig {
@Value("${mybatis.mapper-locations}")
private String mapperLocations;
@Bean
public SqlSessionFactoryBean configSqlSessionFactoryBean(DataSource dataSource) throws IOException {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
// 时候输出mybatis相关日志
configuration.setLogImpl(StdOutImpl.class);
// 开启驼峰命名
configuration.setMapUnderscoreToCamelCase(true);
sqlSessionFactoryBean.setConfiguration(configuration);
sqlSessionFactoryBean.setDataSource(dataSource);
// 设置pageHelper分页插件
PageHelper pageHelper = new PageHelper();
Properties properties = new Properties();
// 指定数据库方言
properties.setProperty("dialect", dialect);
pageHelper.setProperties(properties);
sqlSessionFactoryBean.setPlugins(pageHelper);
// 设置mapper.xml路径
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
sqlSessionFactoryBean.setMapperLocations(resolver.getResources(mapperLocations));// 设置mapper文件扫描路径
return sqlSessionFactoryBean;
}
}
2.Mybatis的核心流程
XML处理相关的类
一、XMLConfigBuilder用来解析mybatis的核心配置文件文件xml中的
二、XMLMapperBuilder用来解析mybatis中Mapper的xml文件,获取到所有节点为<select>|<insert>|<update>|<delete>的节点,然后通过XMLStatementBuilder类来解析这些节点,最后通过MapperBuilderAssistant类来转成MappedStatment对象,添加到Configuration中。
2.1 SqlSessionFactoryBuilder
负责创建SqlSessionFactory。主要解析出MybatisConfig.xml中数据数据库连接信息和Mapper.xml文件,封装成MappedStatement对象。
org.apache.ibatis.session.SqlSessionFactoryBuilder#build
org.apache.ibatis.builder.xml.XMLConfigBuilder#parse
org.apache.ibatis.builder.xml.XMLConfigBuilder#parseConfiguration
2.2 SqlSessionFactory
负责创建SqlSession,并指定SqlSession的执行器,环境,事务管理器,事务提交机制。
org.apache.ibatis.session.defaults.DefaultSqlSessionFactory#openSession()
org.apache.ibatis.session.defaults.DefaultSqlSessionFactory#openSessionFromDataSource
2.3 SqlSession
1.负责调用getMapper方法生成代理对象,并执行对应的代理对象调用方法的具体sql语句
org.apache.ibatis.binding.MapperRegistry#getMapper
org.apache.ibatis.binding.MapperProxyFactory#newInstance
2.4 ParameterHandler
负责将用户传递的参数转换成JDBC Statement需要的参数
org.apache.ibatis.scripting.defaults.DefaultParameterHandler#setParameters
2.3 StatementHandler
负责对JDBC Statement的操作,如Statement执行查询、更新
org.apache.ibatis.executor.statement.PreparedStatementHandler#query
2.6 ResultSetHandler
负责将JDBC返回的ResultSet结果集转换成对象或List类型的集合
org.apache.ibatis.executor.resultset.DefaultResultSetHandler#handleRowValuesForSimpleResultMap
2.7 Executor
Mybatis执行器,负责Sql语句的执行和查询缓存的维护
org.apache.ibatis.executor.CachingExecutor#query
3.Mybatis的插件使用
3.1 自定义插件完成sql语句的输出和进行排序
<!-- 需要加入mybatisConfig.xml中的配置-->
<plugins>
<plugin interceptor="com.two.dream.interceptor.ZdyPlugin"></plugin>
</plugins>
如果是springboot项目直接: sqlSessionFactoryBean.setPlugins(new ZdyPlugin());
@Intercepts({@Signature(
type = Executor.class, //拦截的接口
method = "query", // 拦截的方法
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class} // 拦截方法的入参的参数类型
)})
public class ZdyPlugin implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
System.out.println("=========Executor===========");
// 获取拦截方法的参数
Object[] args = invocation.getArgs();
MappedStatement mappedStatement = (MappedStatement) args[0];
Configuration configuration = mappedStatement.getConfiguration();
// 输出原sql语句
SqlSource sqlSource = mappedStatement.getSqlSource();
BoundSql boundSql = sqlSource.getBoundSql(args[1]);
System.out.println(boundSql.getSql());
System.out.println("==========================");
// 修改原sql语句
SqlSource newSqlSource = new StaticSqlSource(configuration, boundSql.getSql().concat(" order by address desc"), boundSql.getParameterMappings());
MappedStatement newMappedStatement = this.build(mappedStatement, newSqlSource);
// 修改原参数对象
args[0] = newMappedStatement;
// 输出修改后的sql
System.out.println(newSqlSource.getBoundSql(args[1]).getSql());
return invocation.proceed();
}
private MappedStatement build(MappedStatement ms, SqlSource newSqlSource){
// 拷贝原对象的属性到新对象
MappedStatement.Builder builder = new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), newSqlSource, ms.getSqlCommandType());
builder.resource(ms.getResource());
builder.fetchSize(ms.getFetchSize());
builder.statementType(ms.getStatementType());
builder.timeout(ms.getTimeout());
builder.parameterMap(ms.getParameterMap());
builder.resultSetType(ms.getResultSetType());
builder.resultMaps(ms.getResultMaps());
builder.cache(ms.getCache());
builder.flushCacheRequired(ms.isFlushCacheRequired());
builder.useCache(ms.isUseCache());
return builder.build();
}
}