Java面试必会之-Mybatis

基础

  • Mybatis是和数据库交互的半自动化的持久化层框架(SQL映射框架),而Hibernate是数据交互框架(ORM对象关系映射框架)
  • Mybatis底层就是对原生JDBC的一个简单封装,只抽取出【写SQL】过程供程序员使用,其他都被Mybatis封装
  • 导包:mysql-connector-java-5.1.37-bin.jar 和 mybatis-3.4.1.jar

基于XML配置文件

  • mybatis-config.xml:mybatis全局配置文件
<configuration>
	<!-- 从类路径里,配置连接池的数据 -->
	<properties resource="dbconfig.properties"></properties>
	<settings>
		<!-- 开启延迟加载开关 -->
		<setting name="lazyLoadingEnabled" value="true"/>
		<!-- 开启属性按需加载 -->
		<setting name="aggressiveLazyLoading" value="false"/>
		
		 <!-- 开启驼峰命名规则:javaBean字段符合驼峰命名规则,sql字段以‘_’分隔,也可以在sql映射文件起别名-->
		<setting name="mapUnderscoreToCamelCase" value="true" /> 
	</settings>
	<environments default="development">
		<environment id="development">
			<transactionManager type="JDBC" />  <!--  除了查询其他操作都需要openSession.commmit()提交会话-->
			<dataSource type="POOLED">
				<property name="driver" value="${driverclass}" />
				<property name="url" value="${jdbcurl}" />
				<property name="username" value="${username}" />
				<property name="password" value="${password}" />
			</dataSource>
		</environment>
	</environments>
	// 提高数据库移植性
	<databaseIdProvider type="DB_VENDOR">
		<property name="MySQL" value="mysql"/>
	</databaseIdProvider>
	<mappers>  
		<package name="com.atguigu.dao"/> <!-- 批量注册SQL映射文件-->
		<mapper resource="Employee"/> <!-- resource:类路径下找该sql映射文件 -->
	</mappers>
</configuration>

SQL映射文件:相当于对Dao接口的一个实现描述

<mapper namespace="com.atguigu.dao.EmployeeDao">
<!--public Map<String, Object> getEmpByIdReturnMap(Integer id);  -->
<!-- id:跟该namespace接口对应的方法。参数id跟接口的方法同名 -->

<!-- 查询数据,resultType为返回值类型,Employee为在该bean类上标注@Alias(“Employee”)的别名 -->
<select id="getEmpByIdReturnMap" resultType="Employee" databaseId="mysql" order="BEFORE"> <!-- databaseId:使用mysql数据库-->
	select * from t_employee where id=#{id}
</select>

<!-- 插入数据。 useGeneratedKeys:自增id,后台可以直接获取到生成的自增id。-->
<insert id="insertEmployee" useGeneratedKeys="true" keyProperty="id">
    INSERT INTO t_employee(empname,gender,email)
    VALUES(#{empName},#{gender},#{email})
</insert>
  • 测试
	@Before   // 初始化一个sql会话工厂sqlSessionFactory
	public void initSqlSessionFactory() throws IOException {
		String resource = "mybatis-config.xml";
		InputStream inputStream = Resources.getResourceAsStream(resource);
		sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); // sqlSessionFactory负责创建SqlSession对象 
	}
	
	public void test() {
		SqlSession openSession = sqlSessionFactory.openSession();
		try {
			// sql会话获取dao接口实现。employeeDao.getClass()是一个代理对象proxy。并没有给接口写实现类,而在sql映射文件里实现接口方法
			EmployeeDao employeeDao = openSession.getMapper(EmployeeDao.class); 
			Employee empById = employeeDao.getEmpById(1);  // 调用接口方法操作数据库
			System.out.println(empById);
		} finally {
			openSession.close();  // 关闭会话
		}
	}

image-20200718170615445

  • {属性名}:是参数预编译的方式,参数的位置都是用?替代,参数后来都是预编译设置进去的;安全,不会有sql注入问题

    ${属性名}:不是参数预编译,而是直接和sql语句进行拼串;不安全;一般用于往某个日志写数据

  • 自定义映射规则:(作为sql语句结果的返回值)

    • <resultMap type="com.atguigu.bean.Cat" id="mycat">
          <!-- 指定主键列的对应规则;
          column="id":指定哪一列是主键列
          property="":指定cat的哪个属性封装id这一列数据
          -->
          <id property="id" column="id"/>
          <!-- 普通列 -->
          <result property="name" column="cName"/>
          <result property="age" column="cAge"/>
          <result property="gender" column="cgender"/>
      </resultMap>
      
  • 联合查询

 	<select id="getKeyById" resultMap="mykey">
 		select k.id,k.`keyname`,k.`lockid`,
		       l.`id` lid,l.`lockName` from t_key k
			left join t_lock l on k.`lockid`=l.`id`
			where k.`id`=#{id}
 	</select>
 	
 	 <!-- 自定义封装规则:使用级联属性封装联合查询出的结果 -->
	<!--<resultMap type="com.atguigu.bean.Key" id="mykey">
 		<id property="id" column="id"/>
 		<result property="keyName" column="keyname"/>
 		<result property="lock.id" column="lid"/>
 		<result property="lock.lockName" column="lockName"/>
 	</resultMap> -->
 	
 	<!-- mybatis推荐的   <association property=""></association>-->
 	<resultMap type="com.atguigu.bean.Key" id="mykey">
 		<id property="id" column="id"/>
 		<result property="keyName" column="keyname"/>
 		<!-- 接下来的属性是一个对象,自定义这个对象的封装规则;使用association;表示联合了一个对象 -->
 		<!-- javaType:指定这个属性的类型 -->
 		<association property="lock" javaType="com.atguigu.bean.Lock">
 			<!-- 定义lock属性对应的这个Lock对象如何封装 -->
 			<id property="id" column="lid"/>
 			<result property="lockName" column="lockName"/>
 		</association>
 	</resultMap>
  • 查询集合
 		<collection property="keys" ofType="com.atguigu.bean.Key">
 			<!-- 标签体中指定集合中这个元素的封装规则 -->
 			<id property="id" column="kid"/>
 			<result property="keyName" column="keyname"/>
 		</collection>
  • 指定分布查询
 		 <!--告诉mybatis自己去调用一个查询查锁子
 		select="":指定一个查询sql的唯一标识;mybatis自动调用指定的sql将查出的lock封装进来
 		public Lock getLockByIdSimple(Integer id);需要传入锁子id
 		告诉mybatis把哪一列的值传递过去。column:指定将哪一列的数据传递过去。多列则需要{key1=col1,key2=col2}-->
 		<association property="lock" 
 			select="com.atguigu.dao.LockDao.getLockByIdSimple"
 			column="lockid" fetchType="lazy"> // fetchType="lazy":按需加载.
 		</association>
  • 简单的sql语句可以在dao接口上直接@Select("sql语句")、@Update("") , @Delete("") , @Insert("")

  • 参数大于1,参数自动被封装为map,key是索引。@Param就是为了避免被自动封装为map时是索引作为key,而是自定义key。sql映射文件才能直接#{id}使用
    public Employee getEmpByIdAndEmpName(@Param("id")Integer id, String empName);
    
  • @MapKey("id") :把查询出来的结果集中的id作为key,存放在map里

动态SQL

<!-- if:判断 -->
	<!--public List<Teacher> getTeacherByCondition(Teacher teacher); -->
	<select id="getTeacherByCondition" resultMap="teacherMap">
		select * from t_teacher
		<!-- test="":编写判断条件 id!=null:取出传入的javaBean属性中的id的值,判断其是否为空 -->
		<!-- where可以帮我们去除掉前面的and; -->
		<!-- trim:截取字符串 
			prefix="":前缀;为我们下面的sql整体添加一个前缀 
			prefixOverrides="": 取出整体字符串前面多余的字符 
			suffix="":为整体添加一个后缀 
			suffixOverrides="":后面哪个多了可以去掉; -->
		<!-- 我们的查询条件就放在where标签中;每个and写在前面,
			where帮我们自动取出前面多余的and -->
		<trim prefix="where" prefixOverrides="and" suffixOverrides="and">
			<if test="id!=null">
				id > #{id} and
			</if>
			<!-- 空串 "" and; && or: ||; if():传入非常强大的判断条件;
			OGNL表达式;对象导航图
			
			方法、静态方法、构造器。xxx
			在mybatis中,传入的参数可以用来做判断;
			额外还有两个东西;
			_parameter:代表传入来的参数;
				1)、传入了单个参数:_parameter就代表这个参数
				2)、传入了多个参数:_parameter就代表多个参数集合起来的map
			_databaseId:代表当前环境
				如果配置了databaseIdProvider:_databaseId就有值
				
			 -->
			<!-- 绑定一个表达式的值到一个变量 -->
			<!-- <bind name="_name" value="'%'+name+'%'"/> -->
			<if test="name!=null &amp;&amp; !name.equals(&quot;&quot;)">
				teacherName like #{_name} and
			</if>
			<if test="birth!=null">
				birth_date &lt; #{birth} and
			</if>
		</trim>
	</select>
	
	<!-- public List<Teacher> getTeacherByIdIn(List<Integer> ids); -->
	<select id="getTeacherByIdIn" resultMap="teacherMap">
		
		SELECT * FROM t_teacher WHERE id IN
		<!-- 帮我们遍历集合的; collection="":指定要遍历的集合的key 
		close="":以什么结束 
		index="i":索引; 
			如果遍历的是一个list; 
				index:指定的变量保存了当前索引 
				item:保存当前遍历的元素的值 
			如果遍历的是一个map: 
				index:指定的变量就是保存了当前遍历的元素的key 
				item:就是保存当前遍历的元素的值
		item="变量名":每次遍历出的元素起一个变量名方便引用 
		open="":以什么开始 
		separator="":每次遍历的元素的分隔符 
			(#{id_item},#{id_item},#{id_item} -->
		<if test="ids.size >0">
			<foreach collection="ids" item="id_item" separator="," open="("
				close=")">
				#{id_item}
			</foreach>
		</if>
	</select>

	<!--public List<Teacher> getTeacherByConditionChoose(Teacher teacher); -->
	<select id="getTeacherByConditionChoose" resultMap="teacherMap">
		select * from t_teacher
		<where>
			<choose>
				<when test="id!=null">
					id=#{id}
				</when>
				<when test="name!=null and !name.equals(&quot;&quot;)">
					teacherName=#{name}
				</when>
				<when test="birth!=null">
					birth_date = #{birth}
				</when>
				<otherwise>
					1=1
				</otherwise>
			</choose>
		</where>
	</select>

	<!-- public int updateTeacher(Teacher teacher); -->
	<update id="updateTeacher" >
		UPDATE t_teacher
		<set>
			<if test="name!=null and !name.equals(&quot;&quot;)">
				teacherName=#{name},
			</if>
			<if test="course!=null and !course.equals(&quot;&quot;)">
				class_name=#{course},
			</if>
			<if test="address!=null and !address.equals(&quot;&quot;)">
				address=#{address},
			</if>
			<if test="birth!=null">
				birth_date=#{birth}
			</if>
		</set>
		<where>
			id=#{id}
		</where>
		 
	</update>

Mybatis缓存机制

  • 缓存:暂时的存储一些数据;加快系统的查询速度...

  • MyBatis缓存机制:Map;能保存查询出的一些数据;只要之前查询过的数据,mybatis就会保存在一个缓存中(Map);下次获取直接从缓存中拿;

  • 一级缓存:

    • 线程级别的缓存;本地缓存;SqlSession级别的缓存;默认存在;
    • SqlSession关闭或者提交以后,一级缓存的数据会放在二级缓存中;
  • 二级缓存:

    • 全局范围的缓存;除过当前线程、SqlSession能用外其他也可以使用;

    • namespace级别的缓存;POJO需要实现Serializable

    • 二级缓存在SqlSession关闭或提交之后,一级缓存的数据才会放在二级缓存中

      1. 首先全局配置开启二级缓存:
      <setting name="cacheEnabled" value="true"/>  <!-- 开启全局缓存开关; -->
      
      1. 然后配置某个dao.xml文件,让其使用二级缓存
      • <!-- 使用mybatis默认二级缓存<cache></cache> 。在<select>里可以使用useCache=false可以设置该sql语句不使用二级缓存-->
        	<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>
        
  • 一级缓存失效的几种情况:

    • 1、不同的SqlSession对应不同的一级缓存
    • 2、同一个SqlSession但是查询条件不同
    • 3、同一个SqlSession两次查询期间执行了任何一次增删改操作
    • 4、同一个SqlSession两次查询期间手动清空了缓存:openSession.clearCache();
  • 不会出现一级缓存和二级缓存中有同一个数据,二级缓存中,一级缓存关闭了就有了

  • 一级缓存中:二级缓存中没有此数据,就会看一级缓存,一级缓存没有去查数据库;数据库的查询后的结果放在一级缓存中了;

  • 因此任何时候,都会先到二级缓存查-》一级缓存查=》数据库

  • 缓存有关设置

    • 1、全局setting的cacheEnable:配置二级缓存的开关。一级缓存一直是打开的。
    • 2、select标签的useCache属性:配置这个select是否使用二级缓存。一级缓存一直是使用的
    • 3、sql标签的flushCache属性:增删改默认flushCache=true。sql执行以后,会同时清空一级和二级缓存。查询默认flushCache=false。
    • 4、sqlSession.clearCache():只是用来清除一级缓存。
    • 5、当在某一个作用域 (一级缓存Session/二级缓存Namespaces) 进行了 C/U/D 操作后,默认该作用域下所有 select 中的缓存将被clear。

整合第三方缓存

  • 整合ehcache;ehcache非常专业的java进程内的缓存框架;

  • 1、导包

    ehcache-core-2.6.8.jar(ehcache核心包)
    mybatis-ehcache-1.0.3.jar(ehcache的整合包) 
    slf4j-api-1.7.21.jar
    slf4j-log4j12-1.7.21.jar
    
  • 2、ehcache要工作有一个配置文件; 文件名叫ehcache.xml;放在类路径的根目录下

  • 3、在mapper.xml(sql映射文件)中配置使用自定义的缓存

    <cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>
    
  • 4、别的dao还要用这个缓存;缓存引用cache-ref

    <!-- 和别的dao共用一块缓存-->  <cache-ref namespace="com.atguigu.dao.TeacherDao"/>
    
  • <cache-ref namespace="dao接口类的全类名">  // 和别的dao共用一块缓存
    <!-- 磁盘保存路径 -->
     <diskStore path="D:\44\ehcache" />
     <defaultCache 
       maxElementsInMemory="1" 
       maxElementsOnDisk="10000000"
       eternal="false" 
       overflowToDisk="true" 
       timeToIdleSeconds="120"
       timeToLiveSeconds="120" 
       diskExpiryThreadIntervalSeconds="120"
       memoryStoreEvictionPolicy="LRU">
     </defaultCache>
    </ehcache>
    <!-- 
    属性说明:
    l diskStore:指定数据在磁盘中的存储位置。
    l defaultCache:当借助CacheManager.add("demoCache")创建Cache时,EhCache便会采用<defalutCache/>指定的的管理策略
     
    以下属性是必须的:
    l maxElementsInMemory - 在内存中缓存的element的最大数目 
    l maxElementsOnDisk - 在磁盘上缓存的element的最大数目,若是0表示无穷大
    l eternal - 设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断
    l overflowToDisk - 设定当内存缓存溢出的时候是否将过期的element缓存到磁盘上
     
    以下属性是可选的:
    l timeToIdleSeconds - 当缓存在EhCache中的数据前后两次访问的时间超过timeToIdleSeconds的属性取值时,这些数据便会删除,默认值是0,也就是可闲置时间无穷大
    l timeToLiveSeconds - 缓存element的有效生命期,默认是0.,也就是element存活时间无穷大
     diskSpoolBufferSizeMB 这个参数设置DiskStore(磁盘缓存)的缓存区大小.默认是30MB.每个Cache都应该有自己的一个缓冲区.
    l diskPersistent - 在VM重启的时候是否启用磁盘保存EhCache中的数据,默认是false。
    l diskExpiryThreadIntervalSeconds - 磁盘缓存的清理线程运行间隔,默认是120秒。每个120s,相应的线程会进行一次EhCache中数据的清理工作
    l memoryStoreEvictionPolicy - 当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出)
     -->
    

MBG逆向工程

  • 正向:

    table----javaBean---BookDao---dao.xml---xxx

    逆向工程:

    根据数据表table,逆向分析数据表,自动生成javaBean---BookDao---dao.xml---xxx

  • MBG:MyBatis Generator:代码生成器;MyBatis官方提供的代码生成器;帮我们逆向生成;

  • 1、导包:mbg的核心包

  • 2、编写mbg.xml配置文件

    • <?xml version="1.0" encoding="UTF-8"?>
      <!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
      
      <generatorConfiguration>
      
          <!--
          MyBatis3Simple:基础班CRUD
          MyBatis3:复杂版CRUD
           -->
          <context id="DB2Tables" targetRuntime="MyBatis3">
              <commentGenerator>
                  <property name="suppressAllComments" value="true"/>
              </commentGenerator>
              <!-- jdbcConnection:指导连接到哪个数据库 -->
              <jdbcConnection
      
                  driverClass="com.mysql.jdbc.Driver"
                  connectionURL="jdbc:mysql://localhost:3306/mybatis_0325"
      
                  userId="root"
      
                  password="123456">
              </jdbcConnection>
      
              <javaTypeResolver>
                  <property name="forceBigDecimals" value="false" />
              </javaTypeResolver>
      
              <!-- javaModelGenerator:生成pojo
      
              targetPackage:生成的pojo放在哪个包
              targetProject:放在哪个工程下
              -->
              <javaModelGenerator targetPackage="com.atguigu.bean"
                  targetProject=".\src">
                  <property name="enableSubPackages" value="true" />
                  <property name="trimStrings" value="true" />
              </javaModelGenerator>
      
              <!--sqlMapGenerator:sql映射文件生成器;指定xml生成的地方  -->
              <sqlMapGenerator targetPackage="com.atguigu.dao"
                  targetProject=".\conf">
                  <property name="enableSubPackages" value="true" />
              </sqlMapGenerator>
      
              <!-- javaClientGenerator:dao接口生成的地方 -->
              <javaClientGenerator type="XMLMAPPER"
                  targetPackage="com.atguigu.dao"
      
                  targetProject=".\src">
                  <property name="enableSubPackages" value="true" />
              </javaClientGenerator>
      
              <!-- table:指定要逆向生成哪个数据表
              tableName="t_cat":表名
              domainObjectName="":这个表对应的对象名
               -->
              <table tableName="t_cat" domainObjectName="Cat"></table>
              <table tableName="t_employee" domainObjectName="Employee"></table>
              <table tableName="t_teacher" domainObjectName="Teacher"></table>
      
          </context>
      </generatorConfiguration>
      
  • 3、运行代码生成

    public class MBGTest {
    
        public static void main(String[] args) throws Exception {
            List<String> warnings = new ArrayList<String>();
            boolean overwrite = true;
            File configFile = new File("mbg.xml");
            ConfigurationParser cp = new ConfigurationParser(warnings);
            Configuration config = cp.parseConfiguration(configFile);
            DefaultShellCallback callback = new DefaultShellCallback(overwrite);
            MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config,
                    callback, warnings);
            //代码生成
            myBatisGenerator.generate(null);
            System.out.println("生成ok了!");
        }
    
    }
    
  • 4、测试复杂查询

public class MyBatisTest {

    // 工厂一个
    SqlSessionFactory sqlSessionFactory;


    @Test
    public void test02(){
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //1、测试
        TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
        List<Teacher> teachers = new ArrayList<Teacher>();
        for (int i = 0; i < 1000; i++) {
            Teacher teacher = new Teacher();
            teacher.setTeachername(UUID.randomUUID().toString().substring(0, 5));
            teacher.setClassName(UUID.randomUUID().toString().substring(0, 5));
            teachers.add(teacher);
        }
        System.out.println("批量保存.....");
        mapper.insertBatch(teachers);
        sqlSession.commit();
        sqlSession.close();


    }

    /**
     * 测试代码生成器
     * @throws IOException
     */
    @Test
    public void test01(){
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //1、测试
        TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
        //2、测试查询所有teacher
        List<Teacher> list = mapper.selectByExample(null);
        for (Teacher teacher : list) {
            System.out.println(teacher);
        }

        //3、带复杂条件的查询
        //select * from t_teacher id=? and teacherName like ?
        //封装查询条件的
        TeacherExample example = new TeacherExample();
        example.setOrderByClause("id DESC");
        //1、使用example创建一个Criteria(查询准则)
        Criteria criteria = example.createCriteria();
        criteria.andIdEqualTo(1);
        criteria.andTeachernameLike("%a%");

        System.out.println("======================");
        List<Teacher> list2 = mapper.selectByExample(example);
        for (Teacher teacher : list2) {
                System.out.println(teacher);
        }

        /**
         * 多个复杂条件
         * select * from t_teacher where  (id=? and teacherName like ?) or (address like ? and birth bet)
         */
        TeacherExample example2 = new TeacherExample();


        //一个Criteria能封装一整个条件
        Criteria criteria2 = example2.createCriteria();
        criteria2.andIdGreaterThan(1);
        criteria2.andTeachernameLike("%a%");

        //创建第二个查询条件
        Criteria criteria3 = example2.createCriteria();
        criteria3.andAddressLike("%%");
        criteria3.andBirthDateBetween(new Date(), new Date());

        example2.or(criteria3);
        System.out.println("=======-=-=-=-=-=-=-");
        mapper.selectByExample(example2);

    }

    @Before
    public void initSqlSessionFactory() throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    }

}

原理剖析

MyBatis 流程图

image-20200916133200183

Configuration.xml

该配置文件是 MyBatis 的全局配置文件,在这个文件中可以配置诸多项目。常用的内容是别名设置,拦截器设置等。

Properties属性)

将数据库连接参数单独配置在 db.properties 中,放在类路径下。这样只需要在SqlMapConfig.xml 中加载 db.properties 的属性值。这样在 SqlMapConfig.xml 中就不需要对数据库连接参数硬编码。

将数据库连接参数只配置在 db.properties 中,原因:方便对参数进行统一管理.

Settings(全局配置参数):

Mybatis 全局配置参数,全局参数将会影响 mybatis 的运行行为。比如:开启二级缓存、开启延迟加载。

TypeAliase(类型别名):

类型别名是为 Java 类型命名一个短的名字。它只和 XML 配置有关, 只用来减少类完全限定名的多余部分。

Plugin(插件):

MyBatis 允许你在某一点拦截已映射语句执行的调用。默认情况下,MyBatis 允许使用插件来拦截方法调用

**Environments*(环境集合属性对象):

MyBatis 可以配置多种环境。这会帮助你将 SQL 映射应用于多种数据库之中。但是要记得一个很重要的问题:你可以配置多种环境,但每个数据库对应一个 SqlSessionFactory。

所以,如果你想连接两个数据库,你需要创建两个 SqlSessionFactory 实例,每个数据库对应一个。

Environment(环境子属性对象)

TransactionManager****(事务管理):在 MyBatis 中有两种事务管理器类型(也就是 type=”[JDBC|MANAGED]”)

DataSource(数据源):UNPOOLED|POOLED|JNDI

Mappers(映射器)

指定映射配置文件位置

<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
<mapper class="org.mybatis.builder.AuthorMapper"/>
<package name="org.mybatis.builder"/>

Mapper.xml

Mapper.xml 映射文件中定义了操作数据库的 sql,每个 sql 是一个 statement,映射文件是 mybatis 的核心

ResultMap :Mybatis 中可以使用 resultMap 完成高级输出结果映射。如果查询出来的列名和定义的pojo 属性名不一致,就可 以通过定义一个 resultMap 对列名和 pojo 属性名之间作一个映射关系。

Cache:开启二级缓存

Sql:可以重用的 SQL 块,也可以被其他语句引用

Resources

Resources 工具类会从路径中加载资源,并返回一个输入流对象,对于资源文件的加载提供了简易的使用方法。

加载一个资源有很多方式:

对于简单的只读文本数据,加载为 Reader。

对于简单的只读二进制或文本数据,加载为 Stream。

对于可读写的二进制或文本文件,加载为 File。

对于只读的配置属性文件,加载为 Properties。

对于只读的通用资源,加载为 URL。

按以上的顺序,Resources 类加载资源的方法如下:

Reader getResourceAsReader(String resource);

Stream getResourceAsStream(String resource);

File getResourceAsFile(String resource);

Properties getResourceAsProperties(String resource);

Url getResourceAsUrl(String resource);

SqlSessionFactoryBuilder:

该类是 SqlSessionFactory(会话工厂)的构建者类,之前描述的操作其实全是从这里面开启的,首先就是调用 XMLConfigBuilder 类的构造器来创建一个 XML 配置构建器对象, 利用这个构建器对象来调用其解析方法 parse()来完成 Configuration 对象的创建,之后以这个配置对象为参数调用会话工厂构建者类中的 build(Configuration config)方法来完成SqlSessionFactory(会话工厂)对象的构建。

XMLConfigBuilder:

该类是 XML 配置构建者类,是用来通过 XML 配置文件来构建 Configuration 对象实例,构建的过程就是解析 Configuration.xml 配置文件的过程,期间会将从配置文件中获取到的指定标签的值逐个添加到之前创建好的默认 Configuration 对象实例中

Configuration

该对象是 Mybatis 的上下文对象,实例化这个类的目的就是为了使用其对象作为项目全局配置对象,这样通过配置文件配置的信息可以保存在这个配置对象中,而这个配置对象在创建好之后是保存在 JVM 的 Heap 内存中的,方便随时读取。不然每次需要配置信息的时候都要临时从磁盘配置文件中获取,代码复用性差的同时,也不利于开发

DefaultSqlSessionFactory :

SqlsessionFactory该接口是会话工厂,是用来生产会话的工厂接口,DefaultSqlSessionFactory是其实现类,是真正生产会话的工厂类,这个类的实例的生命周期是全局的,它只会在首次调用时生成一个实例(单例模式),就一直存在直到服务器关闭。

Executor

执行器接口,SqlSession 会话是面向程序员的,而内部真正执行数据库操作的却是Executor 执行器,可以将 Executor 看作是面向 MyBatis 执行环境的,SqlSession 就是门面货,Executor 才是实干家。通过 SqlSession 产生的数据库操作,全部是通过调用 Executor 执行器来完成的。Executor 是跟 SqlSession 绑定在一起的,每一个 SqlSession 都拥有一个新的 Executor 对象,由 Configuration 创建。

Executor 继承结构 :

image-20200916133207956

BaseExecutor:

SimpleExecutor:每执行一次 update 或 select,就开启一个 Statement 对象,用完立刻关闭 Statement 对象。(可以是 Statement 或 PrepareStatement 对象)

ReuseExecutor:执行 update 或 select,以 sql 作为 key 查找 Statement 对象,存在就使用,不存在就创建,用完后,不关闭 Statement 对象,而是放置于 Map<String, Statement>内,供下一次使用。(可以是 Statement 或 PrepareStatement 对象)

BatchExecutor:执行 update(没有 select,JDBC 批处理不支持 select),将所有 sql 都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个 Statement对象,每个 Statement 对象都是 addBatch()完毕后,等待逐一执行

CachingExecutor:先从缓存中获取查询结果,存在就返回,不存在,再委托给 Executor delegate 去数据库取,delegate 可以是上面任一的 SimpleExecutor、ReuseExecutor、BatchExecutor。

StatementHandler

该类是 Statement 处理器,封装了 Statement 的各种数据库操作方法 execute(),可见MyBatis 其实就是将操作数据库的 JDBC 操作封装起来的一个框架,同时还实现了 ORM 罢了。RoutingStatementHandler,这是一个封装类,它不提供具体的实现,只是根据 Executor的类型,创建不同的类型 StatementHandler。

ResultSetHandler

结果集处理器,如果是查询操作,必定会有返回结果,针对返回结果的操作,就要使用ResultSetHandler 来进行处理,这个是由 StatementHandler 来进行调用的。这个处理器的作用就是对返回结果进行处理。

面试

posted @ 2020-09-16 13:33  JavaJayV  阅读(214)  评论(0)    收藏  举报