一、MyBatis 框架


一、MyBatis 概念

  • MyBatis 中文官网
  • MyBatis 是一款优秀的 持久层框架,它支持 定制化 SQL、存储过程 以及 高级映射
  • MyBatis 避免了几乎所有的 JDBC 代码 和 手动设置参数,以及 获取结果集
  • MyBatis 可以使用 简单的 XML 或 注解,来配置和映射 原生类型、接口 和 Java 的 POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

1. 其它持久层框架

  1. JDBC
  2. DataUtils
  3. JdbcTemplate
  4. Hibernate

2. 核心对象的 作用域 与 生命周期

  • SqlSessionFactoryBuilder 类:
    用于构建会话工厂,基于 SqlMapConfig.xml 的 environment、properties 构建会话工厂,构建完成后即可丢弃。

  • SqlSessionFactory 接口:
    用于生成会话的工厂,作用于整个应用运行期间,一般不需要构造多个工厂对象。

  • SqlSession 接口:
    作用于单次会话,如:WEB 一次请求期间,不能用作于某个对象属性,也不能在多个线程间共享,因为它是线程不安全的。

3. 接口式编程

  • 由于每次调用时都去找对应用 Statement 以及拼装参数,使用上不是特别友好。
    MyBatis 引入了 接口机制,将接口与 Mapper.xml 的 namespace 名称绑定,MyBatis 就可以根据 ASM 工具 动态构建该接口的实例。
  • Mapper 映射器接口实例。
    通过 session.getMapper(Class<T> type) 就可以获取 Mapper 实例,该实例一般作用于方法域。

二、全局配置 SqlMapConfig.xml

1. 属性

  • properties 元素:
  1. 可以通过 resource 或 url 加载外部 properties 文件中的属性,也可以直接设置 property 属性。
  2. 在 xml 中就可以通过 ${属性名} 进行引用替换。
  • 从 MyBatis-3.4.2 开始,占位符可以指定一个默认值。例如:${jdbc.user:root}

2. 设置

  • 设置 MyBatis 全局参数,约定 MyBatis 的全局行为。
<!-- 设置 -->
<settings>
    <!-- 开启二级缓存。-->
    <setting name="cacheEnabled" value="true"/>
    <!-- 开启驼峰命名适配。-->
    <setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

3. 别名

  • 在 MyBatis 中经常会用到 Java 中的类型。
    如:SQL 块中的 parameterType 参数引用的 JavaType,结果集映射的 JavaType,都要使用 Java 全路径名。
  • 提示:建议不要设置。
    因为常用的类 MyBatis 已经内置别名,而 自定义的类 设置别名 反而不好去找,影响阅读。

3. 环境

  • 一个项目经常需要在例如 开发坏境、测试环境、预演环境、生产环境中 等不同环境中进行部署。
    每个环境所对应的参数是不一样的,MyBatis 中可以通过 environment 来设置不同环境的属性。
  • 可通过 SqlSessionFactoryBuilder.build(String environment) 来指定初始化那个环境。

4. 类型处理器

  • 持久层框架 其中比较重要的工作就是 处理数据 的 映射转换。
    把 Java 类型转换成 JDBC 类型的参数,又需要把 JDBC 类型的结果集转换成 Java 类型。
  • 在 MyBatis 中是通过 TypeHandler 接口来实现的。
  • TypeHandler 两个作用 设置参数 与 获取结果。
    在这里插入图片描述

  • 两种方式指定处理的范围。
<!-- 类型处理器 -->
<typeHandlers>
    <typeHandler handler="com.qs.mybatisfast.config.LongTimeHandler" 
    			 javaType="long" jdbcType="TIMESTAMP"/>
</typeHandlers>

@MappedJdbcTypes(value = JdbcType.TIMESTAMP, includeNullJdbcType = true)
@MappedTypes(Long.class)
public class LongTimeHandler extends BaseTypeHandler<Long> {
}

  • 在 resultMap 中指定 typeHandler。
<!-- 
1、`javaType属性`:Java类型。
2、`jdbcType属性`:JDBC类型。
3、`typeHandler属性`:指定类型处理器。
-->
<result property="createTime" javaType="Long"
		column="create_time" jdbcType="TIMESTAMP"
 		typeHandler="com.qs.mybatisfast.config.LongTimeHandler"/>

5. 映谢器

<!-- 映谢器 -->
<!-- 加载`Mapper文件`。-->
<mappers>
    <!-- 1、`resource属性`:基于`classpath`查找`Mapper文件`。-->
    <mapper resource="mapper/fast.xml"/>
<!--        <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>-->
    <!-- 2、`url属性`:基于资源定位加载`Mapper文件`。-->
<!--        <mapper url="http://www.xxx.com/xml/BlogMapper.xml"/>-->

    <!-- 3、`class属性`:根据`接口名称`加载`Mapper文件`。
  	2.1、`Mapper映射文件`和`接口`在同一个目录下。
   	2.2、`Mapper映射文件`和`接口`名称一致。
   	2.3、`class`就是接口的权限定名。
   	2.4、`mapper.xml中的namespace`必须与接口名称对应。
   	2.5、通过`class或package`中加载时,`mapper.xml文件`必须与接口在同一级目录。
   	-->
    <!--        <mapper class="com.qs.mybatisfast.mapper.UserMapper"/>-->

    <!-- 4、`name属性`:使用`包扫描方式`加载`Mapper文件`。-->
    <package name="com.qs.mybatisfast.mapper"/>
</mappers>

三、Mapper 文件

1. SQL 语句块 statement

  • 通过原生 JDBC 写 DAO 的年代 ,程序员最怕莫过于拼接 SQL 语句(拼接参数 与 设置返回结果集)。
  • Hibernate 将拼接 SQL 时代成为过去,通过 ORM 映谢,完全不需要处理任何 SQL。
    但这又带来了新的问题,无法编写自定义 SQL,从而丧失了 灵活活 及 更好的性能。
  • MyBatis 通过 Mapper 映射 SQL 很好解决了这一点。
    它无需在 JAVA 代码中拼接 SQL,而是将其移至 Mapper 文件,集中处理 SQL 节约了大量的开发时间。

2. Mapper 文件中的标签

  • cache:对给定命名空间的缓存配置。
  • resultMap:结果集映射。
  • sql:可被其他语句引用的可重用语句块。
  • insert:插入语句。
  • update:更新语句。
  • delete:删除语句。
  • select:查询语句。

2.1 select 标签及属性
  • 子标签。
<!ELEMENT select 
(#PCDATA 
| include 
| trim 
| where 
| set 
| foreach 
| choose 
| if 
| bind
)*>

  • 属性。
<!ATTLIST select
<!-- `id属性`:语句块的唯一标识,与接口中方法名称对应。-->
id CDATA #REQUIRED 
<!-- `parameterMap属性`:参数映射。-->
parameterMap CDATA #IMPLIED
<!-- `parameterType属性`:参数Java类型。-->
parameterType CDATA #IMPLIED
 <!-- `resultMap属性`:返回结果映射。-->
resultMap CDATA #IMPLIED
<!-- `resultType属性`:返回结果Java类型。-->
resultType CDATA #IMPLIED
resultSetType (FORWARD_ONLY | SCROLL_INSENSITIVE | SCROLL_SENSITIVE) #IMPLIED
<!-- `statementType = PREPARED`:执行类型。-->
statementType (STATEMENT|PREPARED|CALLABLE) #IMPLIED
fetchSize CDATA #IMPLIED
<!-- `timeout = 10`-->
timeout CDATA #IMPLIED
<!-- `flushCache = true`:每次调用都会`刷新一二级缓存`。-->
flushCache (true|false) #IMPLIED
<!-- `useCache = true`:是否保存至`二级缓存`当中去。-->
useCache (true|false) #IMPLIED
databaseId CDATA #IMPLIED
lang CDATA #IMPLIED
resultOrdered (true|false) #IMPLIED
resultSets CDATA #IMPLIED 
>

2.2 insert 标签及属性
  • 子标签。
<!ELEMENT insert 
(#PCDATA 
| selectKey 
| include 
| trim 
| where 
| set 
| foreach 
| choose 
| if 
| bind
)*>

  • 属性。
<!ATTLIST insert
<!-- `id属性`:语句块的唯一标识,与接口中方法名称对应。-->
id CDATA #REQUIRED
parameterMap CDATA #IMPLIED
parameterType CDATA #IMPLIED
timeout CDATA #IMPLIED
flushCache (true|false) #IMPLIED
statementType (STATEMENT|PREPARED|CALLABLE) #IMPLIED
<!-- 主键对应的Java属性(多个用逗号分割)。-->
keyProperty CDATA #IMPLIED
<!-- 插入成功后,将值回设至原参数。-->
useGeneratedKeys (true|false) #IMPLIED
<!-- 主键列(多个用逗号分割)。-->
keyColumn CDATA #IMPLIED
databaseId CDATA #IMPLIED
lang CDATA #IMPLIED
>

3. 参数映射

  • 参数映射 是最强大功能之一,可以通过以下方式进行引用。
  1. 单个简单参数引用:如果方法中只有一个参数,可通过任意名称进行引用。
  2. 多个简单参数引用:通过参数下标引用 #{arg0}, #{arg1}#{param1}, #{param2}
  3. 对象属性引用:直接通过 属性名称 引用,嵌套对象通过 对象.属性名称 进行引用。
  4. Map key 值引用。
  5. 变量名称引用:需要 JDK-1.8 支持,通过方法中参数名称引用,且在编译时必须加上 -parameters 编译命令。
  • 注意:一旦可以通过 变量名称 引入,将不在支持 arg0 获取。

  • 在 IDEA 中添加 编译参数。
    在这里插入图片描述

  • 在 Maven 中添加 编译参数。
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.3</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
                <compilerArgs>
                    <arg>-parameters</arg>
                </compilerArgs>
            </configuration>
        </plugin>
    </plugins>
</build>

3.1 参数引用—相关属性
<!-- 
1、`javaType属性`:Java类型。
2、`jdbcType属性`:JDBC类型。
3、`typeHandler属性`:指定类型处理器。
-->
<result property="createTime" javaType="Long"
		column="create_time" jdbcType="TIMESTAMP"
 		typeHandler="com.qs.mybatisfast.config.LongTimeHandler"/>

3.2 参数拼接
  • 基于 #{参数引用} 其原理是通过 ?占位符,通过预处理能获得 更好的性能 和 安全性(防止SQL注入)。
  • 但有些需求是通过 ?占位符 无法实现的,就需要通过 ${拼接SQL}
  1. 比如:在一些分库分表的场景中,我们需要 动态的拼接表结构。
  2. 比如:某系统日志表 是 按年 进行切割的(2020_log、2021_log)。
@Select("SELECT * FROM ${table} WHERE id = #{id}")
User selectByTable(String table, int id);

4. 结果集映射

  • 结果集映射 是指将 resultSet 中的内容封装转换成 Java 对象。
  • 在纯 JDBC 时代全部都是用调用 resultSet 的 getXxx(columnName) 来获取属性并封装。
    代码量大编程效率低,尤其当数据模型是一对多,或多对多这种复杂关系,这种封装代码将会变得非常复杂。
  • 结果集映射 就是为解决这个问题,通过 resultMap 集中处理 结果集 与 Java 对象 的关系。

4.1 结果集自动映射
  • 在 select 中指定 resultType=“” 后无需任何配置。
    MyBatis 会基于 resultType 中的 Java 类型及属性,自动推断生成 一个隐示的 resultMap,从而完成结果映射。

4.2 resultMap 标签
  • 但有时候 JDBC 并不是与 Java Bean 完全贴合,这时就需要手动设置 resultMap。
<!-- `resultMap标签`。
1、`type属性`返回结果集映射的pojo,可以使用别名。
-->
<resultMap type="com.qs.mybatisfast.pojo.Order" id="order_list_result_map">
    <!-- `id标签`:主键的映射。
    1、`property属性`:是`pojo`中`主键属性`。
    2、`column属性`:是`数据库主键列`。
    -->
    <id property="id" column="id"/>
    <!-- `result标签`:普通列映射。
    1、`property`
    2、`jdbcType`
    3、`javaType`
    4、`column`
    5、`typeHandler`
    -->
    <result property="userId" column="user_id"/>
    <result property="money" column="money"/>
    <result property="note" column="note"/>
    <result property="createTime" column="create_time"/>
</resultMap>
<select id="getOrderListResultMap" resultMap="order_list_result_map">
    SELECT id, user_id, money, note, create_time
    FROM `order`
</select>

4.3 resultMap.association 标签
<resultMap type="com.qs.mybatisfast.pojo.Order" id="order_user_result_map">
    <id property="id" column="id"/>
    <result property="userId" column="user_id"/>
    <result property="money" column="money"/>
    <result property="note" column="note"/>
    <result property="createTime" column="create_time"/>
    <!-- `association标签`:配置`一对一`关联映射。
    1、`property属性`:对于`order`中的`user属性`。
    2、`javaType属性`:是`user属性`的`数据类型`
    -->
    <association property="user" column="user_id" javaType="com.qs.mybatisfast.pojo.User">
        <id property="id" column="user_id"/>
        <result property="username" column="username"/>
        <result property="address" column="address"/>
    </association>
</resultMap>

<select id="getOrderUserResultMap" resultMap="order_user_result_map">
    SELECT o.id,
           o.user_id,
           o.money,
           o.note,
           o.create_time,
           u.username,
           u.address
    FROM `order` o
             LEFT JOIN `user` u ON u.id = o.user_id
</select>

<resultMap type="com.qs.mybatisfast.pojo.Order" id="order_user_result_map">
    <id property="id" column="id"/>
    <result property="userId" column="user_id"/>
    <result property="money" column="money"/>
    <result property="note" column="note"/>
    <result property="createTime" column="create_time"/>
	<!-- 一对一 -->
    <association property="user" javaType="com.qs.mybatisfast.pojo.User"
    			 column="user_id" select="getUserById">
    </association>
</resultMap>

<select id="getOrderUserResultMap" resultMap="order_user_result_map">
    SELECT id, user_id, money, note, create_time
	FROM `order`
</select>

<select id="getUserById" parameterType="int" resultType="com.qs.mybatisfast.pojo.User">
    SELECT *
    FROM `user`
    WHERE id = #{id}
</select>

4.3 resultMap.collection 标签
<resultMap type="com.qs.mybatisfast.pojo.User" id="user_order_result_map">
    <id property="id" column="id"/>
    <result property="username" column="username"/>
    <result property="sex" column="sex"/>
    <result property="birthday" column="birthday"/>
    <result property="address" column="address"/>
    <!-- `collection标签`:配置`一对多`关联映射。
    1、`property属性`:对于`user对象`中的`集合属性`。
    2、`ofType属性`:集合中每个元素的数据类型。
    -->
    <collection notNullColumn="oid" property="orders" javaType="java.util.List"
                ofType="com.qs.mybatisfast.pojo.Order">
        <!-- id对于order的主键属性 -->
        <id property="id" column="oid"/>
        <result property="money" column="money"/>
        <result property="note" column="note"/>
        <result property="createTime" column="create_time"/>
    </collection>
</resultMap>

<select id="getUserWithOrders" resultMap="user_order_result_map">
    SELECT u.id,
           u.username,
           u.sex,
           u.birthday,
           u.address,
           o.id AS oid,
           o.money,
           o.note,
           o.create_time
    FROM `user` u
             LEFT JOIN `order` o ON o.user_id = u.id
</select>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

骑士梦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值