【Java劝退师】MyBatis 知识脑图 - ORM持久层框架

756 阅读3分钟

MyBatis

MyBatis

ORM 持久层开源框架

一、解决的问题(原生 JDBC 存在)

  1. 数据库配置消息 存在硬编码问题 - 配置文档(不常变动)
  2. 频繁创建、释放数据库连接,造成资源浪费 - 连接池
  3. SQL语句、参数设置、获取结果集 存在硬编码问题 - 配置文档(常变动)
  4. 手动封装结果集,较为繁琐 - 反射、内省

注: 常变动 与 不常变动 的配置文档要分开存放

二、名词解释

  1. 反射: 在程序运行时,可以动态获取到某个对象所有的属性及方法,且能进行调用。

    // 目标类
    Object target = new Object();
    
    // Class 定义
    Class<?> clazz = Class.forName("类全限定路径");
    
    // -------- 属性 ---------
    
    // 属性定义
    Field declaredField = clazz.getDeclaredField("属性名称");
    
    // 该属性允许暴力访问
    declaredField.setAccessible(true);
    
    //݇ 反射获取属性值
    Object value = declaredField.get(target);
    
    
    // -------- 方法 ---------
    
    // 方法定义
    Method method = clazz.getMethod("方法名", args); 
    
    // 方法调用
    method.invoke(target, args); 
    
  2. 内省: 获取指定类,指定属性的 读、写 方法。

    // 目标类
    Object target = new Object();
    
    // Class 定义
    Class<?> clazz = Class.forName("类全限定路径");
    
    // 属性描述器
    PropertyDescriptor propertyDescriptor = new PropertyDescriptor("属性名", clazz);
    
    // ----------- 写方法 ----------
    
    // 获得写方法
    Method writeMethod = propertyDescriptor.getWriteMethod();
    
    // 调用写方法
    writeMethod.invoke(target, "值");
    
    // ----------- 读方法 ----------
    
    // 获得读方法
    Method readMethod = propertyDescriptor.getReadMethod();
    
    // 调用读方法
    Object value = readMethod.invoke(target);
    
  3. ORM: Object/Relation Mapping - 对象关系映射 - 通过操作 POJO 对象,达到操作数据库的目的

  4. JDBC: Java Database Connectivity - Java数据库连接规范

三、动态 SQL

  1. where : 加上 where 1=1
  2. if : test 条件为true,拼接此 SQL 语句
  3. foreach
    • collection : 要遍历的元素集合 array - 数组,list - 集合

    • open : 开头

    • close : 结尾

    • item : 遍历出来元素的别名

    • seperator : 分割占位符

  4. sql : SQL 抽取,使用 include 重复利用
<!-- 重复SQL语句抽取 -->
<sql id="selectUser">select * from User</sql>

<select id="findById" parameterType="user" resultType="user">
    
    <!-- 重复SQL语句使用 -->
	<include refid="selectUser"></include> where id=#{id}
    
    <!-- 条件语句 -->
    <if test="username != null">
    	and username=#{username}
    </if>
    
</select>

<select id="findByIds" parameterType="list" resultType="user">
    
    select * from User
    
    <!-- 拼接 where 1=1 -->
    <where>
        
        <!-- 遍历集合 -->
        <foreach collection="array" open="id in(" close=")" item="id" separator=",">
            #{id}
        </foreach>
        
    </where>
        
</select>

四、复杂映射

如果表字段有相同名称,需要进行重命名

1. 一对一

方式 1 :

<mapper namespace="com.lagou.mapper.OrderMapper">
    
    <resultMap id="orderMap" type="com.lagou.domain.Order">
        
        <!-- 映射 SQL语句查出来的表字段 与 order类中的属性名与对应字段 -->
        <result column="uid" property="user.id"></result>
        <result column="username" property="user.username"></result>
        <result column="password" property="user.password"></result>
        <result column="birthday" property="user.birthday"></result>
        
    </resultMap>
    
    <select id="findAll" resultMap="orderMap">
    	select * from orders o, user u where o.uid=u.id
    </select>
</mapper>

方式 2 :

<mapper namespace="com.lagou.mapper.OrderMapper">
    <resultMap id="orderMap" type="com.lagou.domain.Order">
        <result property="id" column="id"></result>
        <result property="ordertime" column="ordertime"></result>
        <result property="total" column="total"></result>

        <!-- 设置 order 类中的属性名与类型 -->
        <association property="user" javaType="com.lagou.domain.User">

            <!-- 设置表字段与属性的对应关系 -->
            <result column="uid" property="id"></result>
            <result column="username" property="username"></result>
            <result column="password" property="password"></result>
            <result column="birthday" property="birthday"></result>
        </association>
    </resultMap>
</mapper>

2. 一对多

<mapper namespace="com.lagou.mapper.UserMapper">
    
    <resultMap id="userMap" type="com.lagou.domain.User">
        
        <result column="id" property="id"></result>
        <result column="username" property="username"></result>
        <result column="password" property="password"></result>
        <result column="birthday" property="birthday"></result>
        
        <!-- 标记该属性为集合类型,并设置属性名称,与属性对应的类型 -->
        <collection property="orderList" ofType="com.lagou.domain.Order">
            <result column="oid" property="id"></result>
            <result column="ordertime" property="ordertime"></result>
            <result column="total" property="total"></result>
        </collection>
       
    </resultMap>
    
    <select id="findAll" resultMap="userMap">
        <!-- 如果表字段重名,则可以进行重命名,否则 MyBatis 将无法判断 -->
    	select *,o.id oid from user u left join orders o on u.id=o.uid
    </select>
    
</mapper>

3. 多对多 (两个一对多)

image-20201006104136144

<mapper namespace="com.lagou.mapper.UserMapper">

    <resultMap id="userRoleMap" type="com.lagou.domain.User">
        <result column="id" property="id"></result>
        <result column="username" property="username"></result>
        <result column="password" property="password"></result>
        <result column="birthday" property="birthday"></result>
        
        <!-- 标记该属性为集合类型,并设置属性名称,与属性对应的类型 -->
        <collection property="roleList" ofType="com.lagou.domain.Role">
            <result column="rid" property="id"></result>
            <result column="rolename" property="rolename"></result>
        </collection>
        
    </resultMap>
    
    <select id="findAllUserAndRole" resultMap="userRoleMap">
        select u.*,r.*,r.id rid from user u 
        left join user_role ur on u.id = ur.user_id
        inner join role r on ur.role_id=r.id
    </select>
    
</mapper>

五、缓存

1. 一级缓存 (默认开启)

SqlSession 级别的缓存,仅在同一个连接内有效

缓存刷新

  1. 中途运行 commit 操作 (增、删、改)
  2. 手动刷新 SqlSession.clearCache()

2. 二级缓存 (默认不开启,通常与Redis集成实现)

Mapper 级别的缓存,多个 SqlSession,在使用同一个 Mapper 内有效。

  1. 当SqlSession运行close()方法并且一级缓存有内容时,才会将一级缓存的内容存到二级缓存中

  2. 多表查找不会触发二级缓存

  3. 当对 Mapper 进行 commit 操作 (增、删、改),刷新缓存

  4. 使用 useCache = false 属性,可以禁用特定 SQL 的二级缓存

六、插件

允许拦截的类

  1. Executor 运行器 : 负责增、删、改、查的行为。
  2. StatementHandler SQL语法构建器 : 完成SQL的预编译。
  3. ParameterHandler 参数处理器 : 设置参数。
  4. ResultHandler 结果集处理器 : 负责处理返回结果集。