一、整体介绍
前面一篇文章MyBatis3使用 讲解了MyBatis的基本使用,这篇介绍MyBatis的核心组件,让我们从整体上了解MyBatis的组成。
MyBatis核心类/接口如下:
Configuration(主配置类)
MappedStatement:描述Xml中Sql配置
SqlSession:用户使用最多的就是这个接口,用于执行Sql
Executor:Sql执行器,上面SqlSession就是调用Executor来完成Sql执行的 几个Handler:
StatementHandler:封装了JDBC Statement对象的操作
ParameterHandler:当Statement为PreparedStatement或CallableStatement时用于设置参数
ResultHandler:用于将JDBC的ResultSet转换为Java对象
TypeHandler:用于处理JDBC类型和Java类型的转换
二、Configuration
Configuration:描述主配置信息,对应前面讲的主配置文件,另外它还是其它组件的容器,像Mapper、TypeHandler等都会注册到这里。
在主配置文件中settings节点配置的一些属性在这里都有对应。
常用配置如下:
cacheEnabled:是否开启2级缓存,2级缓存基于Mapper。
lazyLoadingEnabled:延迟加载开关,主要是延迟加载一些关联对象,建议设为true。
multiResultSetEnabled:是否允许一语句返回多结果集,建议关闭。
defaultStatementTimeout:Statement的超时时间,建议开启为3-10秒。
defaultFetchSize:从数据库获取最大行数,建议设置1000以内,后台系统除外。
三、MappedStatement
描述一条Sql语句,如前面一篇文章举例的:
代码语言:javascript复制<select id="listAllUser" resultType="com.liujh.entity.UserEntity" >
select
<include refid="userFields"/>
from user
</select>
select标签常用属性如下:
id:唯一标识
parameterType:参数的Java类型
resultType:描述结果和Java类的映射关系
timeout:超时时间
以下为例子
代码语言:javascript复制 <resultMap id="BaseResultMap" type="Post">
<id column="id" property="id" jdbcType="INTEGER"/>
<result column="title" property="title" jdbcType="VARCHAR"/>
<result column="author" property="author" jdbcType="VARCHAR"/>
<result column="create_time" property="createTime" jdbcType="BIGINT"/>
</resultMap>
<sql id="Table_Name">
post1
</sql>
<select id="list" resultMap="BaseResultMap" parameterType="Post">
select
id,title,author,create_time
from
<include refid="Table_Name"/>
<where>
<if test="title !=null and title!=''">
and title like CONCAT(#{title},'%')
</if>
</where>
<if test="offset!=null and limit!=null and limit>0">
limit #{offset},#{limit}
</if>
表结构如下:
代码语言:javascript复制CREATE TABLE `post1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(255) DEFAULT NULL,
`author` varchar(255) DEFAULT NULL,
`create_time` bigint(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
四、SqlSession
这个是门面类,面向用户Api大部分接口是通过这个接口暴露出去的,我们看一段代码:
代码语言:javascript复制 // 获取配置文件输入流
InputStream inputStream = Resources.getResourceAsStream("META-INF/spring/mybatis-config.xml");
// 通过SqlSessionFactoryBuilder的build()方法创建SqlSessionFactory实例
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//构造数据源
BoneCPDataSource dataSource= new BoneCPDataSource();
dataSource.setDriverClass(driverClass);
dataSource.setJdbcUrl(jdbcUrl);
dataSource.setUsername(userNmae);
dataSource.setPassword(password);
String id = "SqlSessionFactoryBean";
TransactionFactory transactionFactory = new SpringManagedTransactionFactory();
Environment newEnv = new Environment(id, transactionFactory, dataSource);
sqlSessionFactory.getConfiguration().setEnvironment(newEnv);
// 调用openSession()方法创建SqlSession实例
SqlSession sqlSession = sqlSessionFactory.openSession();
// 获取UserMapper代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 执行Mapper方法,获取执行结果
List<UserEntity> userList = userMapper.listAllUser();
可以看到SqlSession由SqlSessionFactory打开,执行SQL先由SqlSession获取一个Mapper,参数为Mapper接口类,然后直接执行其方法就可以了,Mapper接口定义如下:
代码语言:javascript复制@Repository
public interface UserMapper {
List<UserEntity> listAllUser();
}
五、Executor
真正执行Sql的组件,我们用的最多的是SimpleExecutor,还有加入缓存的CachingExecutor,以下为使用代码:
代码语言:javascript复制 public void testExecutor() throws IOException, SQLException {
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
Configuration configuration = sqlSession.getConfiguration();
MappedStatement listAllUserStmt = configuration.getMappedStatement(
"com.edward.mapper.UserMapper.listAllUser");
Executor executor = configuration.newExecutor(
new JdbcTransaction(sqlSession.getConnection()),
ExecutorType.SIMPLE
);
List<UserEntity> userList = executor.query(listAllUserStmt,
null,
RowBounds.DEFAULT,
Executor.NO_RESULT_HANDLER);
}
可以看到Executor由Configuration创建,query即查询方法需要一个MappedStatement参数(由Configuration.getMappedStatement获取)。
其中query方法定义在父类BaseExecutor中
代码语言:javascript复制@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameter);
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
更新对应的update方法,这里不详述。
六、几个Handler
这几个不在主流程上,代码不是很复杂,可以根据自己情况查阅,挑一个ResultSetHandler讲下。
ResultSetHandler用于将JDBC返回的ResultSet转化成Java对象,看下定义
代码语言:javascript复制public interface ResultSetHandler {
<E> List<E> handleResultSets(Statement stmt) throws SQLException;
void handleOutputParameters(CallableStatement cs) throws SQLException;
}
其中handleOutputParameters用于处理存储过程输出参数,一般互联网公司不建议用存储过程,所以就跳过这个,看下handleResultSets。
代码语言:javascript复制public List<Object> handleResultSets(Statement stmt) throws SQLException {
final List<Object> multipleResults = new ArrayList<Object>();
int resultSetCount = 0;
ResultSetWrapper rsw = getFirstResultSet(stmt);
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
validateResultMapsCount(rsw, resultMapCount);
while (rsw != null && resultMapCount > resultSetCount) {
ResultMap resultMap = resultMaps.get(resultSetCount);
//处理单行
handleResultSet(rsw, resultMap, multipleResults, null);
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount ;
}
其调用handleResultSet来处理单个结果集,最终又通过ResultHandler::handleResult来处理单字段:
代码语言:javascript复制@Override
public void handleResult(ResultContext<? extends V> context) {
final V value = context.getResultObject();
final MetaObject mo = MetaObject.forObject(value, objectFactory, objectWrapperFactory, reflectorFactory);
// TODO is that assignment always true?
final K key = (K) mo.getValue(mapKey);
mappedResults.put(key, value);
}