MyBatis学习
- mybatis基本使用步骤
- 1、创建Maven项目并在pom.xml文件中配置依赖与插件等信息
- 2、编写dao接口、表的实体类、daoImpl、mapper
- 3、编写mybatis主配置文件,实现数据库连接,指定mapper文件
- 4、封装创建SqlSession对象方法
- 5、传统 Dao开发方式,通过操作mybatis主配置文件,在DaoImpl中执行sql语句
- 解析步骤
- 总结:传统 Dao开发方式的分析
- mybatis使用进阶
- 减少接口实现类(daoImpl):jdk动态代理
- mybatis的sql执行流程
- getMapper方法执行流程
- Mapper接口及其映射文件加载到knownMappers中的流程
- sql执行流程分析
- 寻找sql
- 执行sql
通过这篇文章,你可以了解:
1、mybatis传统dao开发方式
2、mybatis的dao代理开发方式
3、mybatis的sql执行流程
mybatis基本使用步骤
1、创建Maven项目并在pom.xml文件中配置依赖与插件等信息
- 配置日志文件(settings传送门)
<!--控制mybatis全局配置 -->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
- 首先需要加入mybatis与mysql-connector-java的依赖(依赖代码在官方文档都有说明)
<dependency>
<groupId>org.mybatis< /groupId>
<artifactId>mybatis< /artifactId>
<version>3.5.1< /version>
</dependency>
<dependency>
<groupId>mysql< /groupId>
<artifactId>mysql-connector-java< /artifactId>
<version>8.0.25< /version>
</dependency>
</dependencies>
- 配置文件过滤插件
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<!--将java包下这两种格式文件让maven在编译过程中携带过去 -->
<include>**/ *.xml< /include>
<include>**/ *.properties< /include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources< /directory>
<includes>
<include>\*\*/\*.xml< /include>
<include>\*\*/\*.properties< /include>
</includes>
<filtering>false< /filtering>
</resource>
</resources>
</build>
2、编写dao接口、表的实体类、daoImpl、mapper
3、编写mybatis主配置文件,实现数据库连接,指定mapper文件
<!--namespace是dao接口的全限定名
id是dao接口下需要实现的功能方法
resultType是查询结果返回类型(全限定名)
这里表示CityDao接口下findCity方法查询city表中数据并返回为City这个类型-->
<mapper namespace="com.lanan.dao.CityDao">
<select id="findCity" resultType="com.lanan.domain.City">
select id,name,provinceid from city
</select>
</mapper>
4、封装创建SqlSession对象方法
public class MyBatisUtil {
//定义 SqlSessionFactory
private static SqlSessionFactory factory = null;
static {
//使用 静态块 创建一次 SqlSessionFactory
try{
String config = "mybatis-config.xml";
//读取配置文件
InputStream in = Resources.getResourceAsStream(config);
//创建 SqlSessionFactory 对象
factory = new SqlSessionFactoryBuilder().build(in);
}catch (Exception e){
factory = null;
e.printStackTrace();
}
}
/* 获取 SqlSession 对象 */
public static SqlSession getSqlSession(){
SqlSession session = null;
if( factory != null){
session = factory.openSession();
}
return session;
}
}
5、传统 Dao开发方式,通过操作mybatis主配置文件,在DaoImpl中执行sql语句
/**
* 最基本的使用方式,
*/
//1、获取SqlSession对象
SqlSession session =MyBatisUtil.getSqlSession();
//2、指定要执行sql的mapper文件的nameSpace+.+id
String sqlId ="com.lanan.dao.CityDao" + "." + "findCity";
//3、通过sqlId找到执行语句
List<City> list = session.selectList(sqlId);
//关闭资源
session.close();
return list;
解析步骤
主要解析第六步,在第六步可以看出有创建了大量对象,但其中最重要的是SqlSession对象的创建与通过sqlId找到执行语句。
- Resources:负责读取配置文件的对象
- SqlSessionFactory:SqlSessionFactory是接口,实现类DefaultSqlSessionFactory(spring与mybatis集成中就是创建了这个实现类对象), SqlSessionFactory对象创建会消耗大量资源,一个项目中有一个即可,这里可以通过封装(static SqlSessionFactory)
- SqlSession:类似于javaweb项目中编写得DbUtil包封装对数据库的操作,是接口,实现类:DefaultSqlSession
- sqlId:通过这个告诉mybatis怎么去找执行的sql语句
- 为解决openSession的线程安全问题:openSession()使用在sql语句之前,在sql语句之后关闭close
总结:传统 Dao开发方式的分析
Dao的实现类其实并没有干什么实质性的工作,真正对 DB进
行操作的工作其实是由框架通过 mapper中的 SQL完成的。
MyBatis 框架就抛开了 Dao 的实现类,直接定位到映射文件 mapper 中的相应 SQL 语句,对DB进行操作。这种对 Dao的实现方式称为 Mapper的动态代理方式
mybatis使用进阶
使用动态代理绕过接口实现类的编写
减少接口实现类(daoImpl):jdk动态代理
//访问mybatis读取city数据
SqlSession sqlSession = MyBatisUtil.getSqlSession();
//动态获取dao接口实现类
CityDao dao = sqlSession.getMapper(CityDao.class);
//数据插入
List<City> list = dao.findAngleCity(12, "呼伦贝尔");
sqlSession.close();
//处理数据
list.forEach(System.out::println);
从上面代码可以看出通过传入接口的class对象,mybatis便用getMapper方法,通过jdk的动态代理创建了impl类,减少了实现类的创建。
mybatis的sql执行流程
getMapper方法执行流程
使用该方法会创建一个MapperProxy对象,该对象就是jdk动态代理的产物
- 项目启动的时将Mapper加载并解析存储到Configuration对象
- 这个方法会先去Configuration对象中获取Mapper对象(this.configuration.getMapper(type, this))
- 通过Configuration对象中的MapperRegistry对象属性,继续调用getMapper方法(this.configuration.mapperRegistry.getMapper(type, sqlSession))
- knownMappers是一个HashMap为底层的map对象
- 根据type类型,从MapperRegistry对象中的knownMappers获取到当前类型对应的代理工厂类,然后通过代理工厂类生成对应Mapper的代理类((MapperProxyFactory)this.configuration.mapperRegistry.knownMappers.get(type).newInstance(sqlSession))
可以看出最后是使用代理工厂类下newInstance创建了代理对象
Mapper接口及其映射文件加载到knownMappers中的流程
- 读取mybatis的主配置文件,创建io流
- 调用SqlSessionFactoryBuilder方法中的build()方法创建SqlSessionFactory对象,过程中会调build(Reader reader, String environment, Properties properties)方法,会创建XMLConfigBuilder对象,并调用其parse方法
- 然后会继续调用自己的parseConfiguration来解析全部的配置文件
- 调用自己的mapperElement来解析mappers文件,4种mapper配置方式(两种直接配置的xml映射文件,两种解析直接配置Mapper接口的方式),但不论配置哪种方式,最终MyBatis都会将xml映射文件和Mapper接口进行关联(防止解析失败报错,开发过程中我们应该避免循环依赖的产生)
- 解析完映射文件之后,调用自身方法bindMapperForNamespace,开始绑定Mapper接口和映射文件
- 调用Configuration对象的属性MapperRegistry内的addMapper方法,这个方法就是正式将Mapper接口添加到knownMappers,所以上面getMapper可以直接获取
sql执行流程分析
寻找sql
- dao接口代理对象调用方法,是使用invoke方法
- 因为代理对象调用的方法不是Object类下的,会继续调用MapperProxy内部类MapperMethodInvoker中的方法cachedInvoker
- 构造一个MapperMethod对象,这个对象封装了Mapper接口中对应的方法信息以及对应的sql语句信息
执行sql
- 会根据语句类型以及返回值类型来决定如何执行方法(selectList)
- 方法会委派给了Execute去执行query方法,最终又会去调用queryFromDatabase方法
- 进入PreparedStatementHandler对象的query方法,可以看到,这一步就是调用了jdbc操作对象PreparedStatement中的execute方法,最后一步就是转换结果集然后返回