映射成数据库中的记录。
Mybatis通过xml或注解的方式将要执行的statement配置起来,并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射成java对象并返回。
(二)框架流程:
1.加载配置:配置来源于两个地方,一处是配置文件,一处是Java代码的注解,将SQL的配置信息加载成为一个个MappedStatement对象(包括了传入参数映射配置、执行的SQL语句、结果映射配置),存储在内存中。
2.SQL解析:当API接口层接收到调用请求时,会接收到传入SQL的ID和传入对象(可以是Map、JavaBean或者基本数据类型),Mybatis会根据SQL的ID找到对应的MappedStatement,然后根据传入参数对象对MappedStatement进行解析,解析后可以得到最终要执行的SQL语句和参数。
3.SQL执行:将最终得到的SQL和参数拿到数据库进行执行,得到操作数据库的结果。
4.结果映射:将操作数据库的结果按照映射的配置进行转换,可以转换成HashMap、JavaBean或者基本数据类型,并将最终结果返回。
(三)MyBatis优缺点
优点:
1、简单易学
mybatis本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。
2、灵活
mybatis不会对应用程序或者数据库的现有设计强加任何影响。 sql写在xml里,便于统一管理和优化。通过sql基本上可以实现我们不使用数据访问框架可以实现的所有功能,或许更多。
3、解除sql与程序代码的耦合
通过提供DAL层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。
4、提供xml标签,支持编写动态sql。
缺点:
1、编写SQL语句时工作量很大,尤其是字段多、关联表多时,更是如此。
2、SQL语句依赖于数据库,导致数据库移植性差,不能更换数据库。
3、二级缓存机制不佳
(四)功能架构
我们把Mybatis的功能架构分为三层:
(1)API接口层:提供给外部使用的接口API,开发人员通过这些本地API来操纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理。
(2)数据处理层:负责具体的SQL查找、SQL解析、SQL执行和执行结果映射处理等。它主要的目的是根据调用的请求完成一次数据库操作。
(3)基础支撑层:负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是共用的东西,将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基础的支撑。
(五)Mybatis工作原理
Mybatis原名Ibatis,在2011年从Ibatis2.x升级到Mybatis 3.X,并将项目地址从Apache迁移到了Google code,事实上我们看MyBatis的类全路径名,还是保留了Apache和Ibatis的的包前缀
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
不过MyBatis的配置文件以及操作类和实现方式都有了很大变化,这里我们重点讲述的是Mybatis,不是Ibatis;
Mybatis的配置文件一共由两类:
一类用于指定数据源、事务属性以及其他一些参数配置信息(通常是一个独立的文件,可以称之为全局配置文件);
另一类则用于 指定数据库表和程序之间的映射信息(可能不止一个文件,我们称之为映射文件)
这些文件的名字并没有确定的要求;只是要最从特定的dtd的xml文件约束,即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>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<!-- 配置数据库连接信息 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/mybatis" />
<property name="username" value="root" />
<property name="password" value="root" />
</dataSource>
</environment>
</environments>
<mappers>
<!-- 注册userMapper.xml文件,
userMapper.xml位于com.test.mapping这个包下,所以resource写成com/test/mapping/userMapper.xml-->
<mapper resource="com/test/mapping/userMapper.xml"/>
</mappers>
</configuration>
上述就是MyBatis的数据源,事务属性,以及映射文件的索引;
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 为这个mapper指定一个唯一的namespace,namespace的值习惯上设置成包名+sql映射文件名,这样就能够保证namespace的值是唯一的
例如namespace="com.test.mapping.userMapper"就是com.test.mapping(包名)+userMapper(userMapper.xml文件去除后缀)
-->
<mapper namespace="com.test.mapping.userMapper">
<!--
根据id查询得到一个user对象
-->
<select id="getUser" parameterType="int"
resultType="com.test.domain.User">
select * from users where id=#{id}
</select>
</mapper>
上面是数据库表与程序之间的映射文件,定义了一个根据id来获取User对象的sql
package com.test.domain;
/**
* users表所对应的实体类
*/
public class User {
//实体类的属性和表的字段名称一一对应
private int id;
private String name;
private int age;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User [id=" + id + ", name=" + name + ", age=" + age + "]";
}
}
问题:
mybatis是怎么在程序中顺利的找到sqlmapper的,这个的流程是怎么样??
// mybatis的配置文件
String resource = "conf.xml";
// 使用类加载器加载mybatis的配置文件(它也加载关联的映射文件)
InputStream is = Test1.class.getClassLoader().getResourceAsStream(resource);
// 构建sqlSession的工厂
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);
题主问的sqlmapper可以理解为两种组件,一种是mapping映射文件,通过id名来获取相应的sql语句,操作数据库;一种是sql的返回对象,
resultType="com.test.domain.User"
这个就是返回的sql结果映射成为具体的POJO(Plain Ordinary Java Object)对象;
两个重要的类即:
org.apache.ibatis.session.SqlSessionFactory;
org.apache.ibatis.session.SqlSession;
package org.apache.ibatis.session;
import java.sql.Connection;
public interface SqlSessionFactory {
SqlSession openSession();
SqlSession openSession(boolean autoCommit);
SqlSession openSession(Connection connection);
SqlSession openSession(TransactionIsolationLevel level);
SqlSession openSession(ExecutorType execType);
SqlSession openSession(ExecutorType execType, boolean autoCommit);
SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
SqlSession openSession(ExecutorType execType, Connection connection);
Configuration getConfiguration();
}
在构建SqlSessionFactory类的时候,将会对数据源及事务配置进行解析,具体在
org.apache.ibatis.builder.xml.XMLConfigBuilder类
org.apache.ibatis.builder.BaseBuilder类
XMLConfigBuilder类是解析产生org.apache.ibatis.Session.Configuration类的的具体类,Configuration类中将保存中所有的配置;
mybatis的源代码解析(1)--xml文件解析 - 王久勇 - 博客园
这篇博客介绍了一些xml文件解析的基本;
具体mybatis的xml解析使用到了XPath方式,具体解析过程参看
https://zhuanlan.zhihu.com/p/31418285
其实一般各种轮子都会有一个解析XML后信息的专用存储类,比如Config.Java,xxxConf.java,都是在启动组件时解析XML配置以用作程序中使用的。
通过跟踪源代码可以看到SqlSession通过mapper映射的id来查找数据的方法;
org.apache.ibatis.session.defaults.DefaultSqlSession类
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds)
{
try
{
MappedStatement ms = configuration.getMappedStatement(statement);
List<E> result = executor.<E> query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
return result;
}
catch (Exception e)
{
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
}
finally
{
ErrorContext.instance().reset();
}
}
org.apache.ibatis.session.Configuration类
public MappedStatement getMappedStatement(String id)
{
return this.getMappedStatement(id, true);
}
protected final Map<String, MappedStatement> mappedStatements =
new StrictMap<MappedStatement>("Mapped Statements collection");
public MappedStatement getMappedStatement(String id, boolean validateIncompleteStatements)
{
if (validateIncompleteStatements)
{
buildAllStatements();
}
return mappedStatements.get(id);
}
其实就是根据一个map映射,key就是定义mapping时候的id来拿到的;
上述org.apache.ibatis.session.defaults.DefaultSqlSession类对象中的 selectList方法中的executor对象,
在默认情况下,即没有设置settings的cache和executor属性时,默认使用的
org.apache.ibatis.executor.CachingExecutor类
public Executor newExecutor(Transaction transaction, ExecutorType executorType, boolean autoCommit)
{
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == execut