Mybatis框架

概述

原是Apache的一个开源项目iBatis, 2010年6月这个项目由Apache Software Foundation 迁移到了 Google Code,随着开发团队转投Google Code 旗下, iBatis3.x正式更名为MyBatis。是 一个基于Java的持久层框架。 iBatis提供的 持久层框架包括SQL Maps和Data Access Objects (DAO)。

MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映 射
优点:MyBatis 避免了几乎所有的 JDBC 代码手动设置参数以及获取结果集。
MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJO(Plain Old Java Objects,普通的 Java 对象)映射成数据库中的记录,是 一种 ORM(ORM Object Relational Mapping 对象关系映射)实现.

传统JDBC与MyBatis的对比

传统 JDBC 编程

  1. 加载数据库驱动
  2. 创建并获取数据库链接
  3. .创建 statement 对象
  4. .拼写 sql 语句
  5. 设置 sql 语句中的占位符的值
  6. .执行 sql 语句并获取结果
  7. 对 sql 执行结果进行解析处理
  8. 释放资源

JDBC 编程问题

  1. 数据库连接的创建、释放频繁造成系统资源浪费从而影响系统性能,如果使 用数据库连接池可解决该问题。
  2. SQL 语句编写在 Java 代码中,这种硬编码造成代码不易维护,当 SQL 变动 时需要修改 java 源代码。
  3. 使用 preparedStatement 向占位符传参数存在硬编码,因为 SQL 语句的 where 条件中占位符的个数可能会变化,修改 SQL 还要修改 Java 源代码, 系统不易维护。
  4. 对结果集解析存在硬编码,SQL 语句变化导致解析代码变化,系统不易维护。

MyBatis框架

MyBatis 是优秀的持久层框架,它能够解决 JDBC 编程中存在的问题。接下 来先了解 MyBatis 的架构。
在这里插入图片描述

Mybatis环境搭建与应用

导入 MyBatis jar 包数据库驱动包

<dependency> 
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis</artifactId>
   <version>3.4.2</version>
</dependency>

创建 MyBatis 全局配置文件

在resources包的mybatis里面配置属性

<?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>
 添加属性
</configuration>
  1. configuration(配置)
  2. properties(属性)
  3. settings(设置)
  4. typeAliases(类型别名)
  5. typeHandlers(类型处理器)
  6. objectFactory(对象工厂)
  7. plugins(插件)
  8. environments(环境配置)
  9. environment(环境变量)
  10. transactionManager(事务管理器)
  11. dataSource(数据源)
  12. databaseIdProvider(数据库厂商标识)
  13. mappers(映射器)

创建 sql 映射文件

<?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="com.ffyc.mybatispro.mapper.AdminMapper">
添加sql语句
<!--
         parameterType="参数类型"
         #{属性名} = values(?,?,?) 赋值方式是预编译,安全的,可以防止sql注入
         ${属性}values('admin2','111','男') 等于字符串拼接 不安全
               ${} 多用来传列名而不是数据
               useGeneratedKeys="true" keyColumn="id" keyProperty="id
               useGeneratedKeys="true 可以返回插入数据的主键
               keyColumn="id" 告知主键列
               keyProperty="id" 告知主键列对应的属性
         insert into admin(account,password,sex)values('${account}','${password}','${sex}')
    -->
    比如;
    <insert id="saveAdmin" parameterType="Admin" useGeneratedKeys="true" keyColumn="id" keyProperty="id">/*id与接口的方法名一致  后面的parameterType是model的对象地址  (使用mybatis.html定义的别名)*/
           insert into admin(account,password,sex)values(#{account},#{password},#{sex})
    </insert>

    <update id="updateAdmin" parameterType="Admin">
        update admin set account=#{account},
                         password=#{password},
                         sex=#{sex}
        where id = #{id}
    </update>

    <delete id="deleteAdmin">
        delete from admin where id = #{id}
    </delete>

    <!--查找的时候可能会出现列名与set属性名不一致的现象 用resultMap来解决-->
    <resultMap id="AdminMap" type="Admin">
        <id column="id" property="id"></id><!--id是主键列-->
        <result column="account" property="account"></result><!--column是列名 右边是set属性名-->
        <result column="password" property="password"></result>
        <result column="sex" property="sex"></result>
    </resultMap>

    <select id="findAdminById" resultMap="AdminMap">/*已经设置了别名 所以可以不用地址,直接用别名*/
        select * from admin where id = #{id}
    </select><!--将数据库数据自动映射到java对象中,有前提表中的列明与类中的属性名相同-->

    <select id="getAdmin" resultMap="AdminMap">
        select * from admin where account=#{acc} and sex=#{sex}
    </select>

    <select id="getAdminList" resultMap="AdminMap">
        select * from admin
    </select>
    <select id="getLogin" resultType="Admin">
        select id,account from admin where account=#{account} and password=#{password}
    </select>
</mapper>

在这里插入图片描述

加载mysql驱动

 <!--
       environments 配置与数据库连接的相关信息
    -->
    <environments default="development">
        <!--配置本地开发信息-->
        <environment id="development">
            <!--配置事务管理类型,使用JDBC事务管理
                事务:一次对数据库操作的若干流程管理
            -->
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <!--数据库驱动-->
                <property name="driver" value="${Driver}"/>
                <!--数据库地址-->
                <property name="url" value="${Url}"/>
                <property name="username" value="${UserName}"/>
                <property name="password" value="${PassWord}"/>
            </dataSource>
        </environment>
    </environments>

这里需要在resources里面创建config.properites来定义驱动mysql的别名

Driver=com.mysql.cj.jdbc.Driver
Url=jdbc:mysql://127.0.0.1:3306/mybatis_db?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
UserName=(数据库账号)
PassWord=(数据库密码)

mybatis日志

Mybatis 内置的日志工厂提供日志功能,具体的日志实现有以下几种方式: SLF4J | LOG4J | LOG4J2 JDK_LOGGING COMMONS_LOGGING STDOUT_LOGGING NO_LOGGING 具体选择哪个日志实现由 MyBatis 的内置日志工厂确定。它会使用最先找到的。

配置日志:

<settings> 
  <setting name="logImpl" value="LOG4J"/> 
</settings>

参数传递

简单的参数形式不需要使用 parameterType 参数定义,
例如:User selectUsers(int id);
多个参数使用@Param(“id”)绑定
User selectUsers(@Param(“id”)int id,@Param(“name”)String name);
在这里插入图片描述

<select id="selectUsers" resultType="User"> 
     select id, username, password from users where id = #{id}
</select>

如果传入一个复杂的对象,就需要使用 parameterType 参数进行类型定义,例如: void insertUser(User user);

<insert id="insertUser" parameterType="User"> 
     insert into users (id, username, password) values (#{id}, #{username}, #{password}) 
</insert>

也可以使用 Map 对象传递 void insertUser(Map<String,Object> map); 在 sql 中使用表达式获取 map 的键即可.

多表关联处理结果集

resultMap 元素中 association , collection 元素 association – 复杂类型联合;
许多查询结果合成这个类型 一对一结果映射– association 能引用自身, 或者从其它地方引用 collection – 复杂类型集合 嵌套结果映射– collection 能引用自身, 或者从其它地方引用 多对一与一对多
association 关联元素处理“有一个”类型的关系,即一对一关联。它有两种关联 方式

  1. 嵌套查询:通过执行另外一个 SQL 映射语句来返回预期的复杂类型。
  2. 嵌套结果:使用嵌套结果映射来处理重复的联合结果的子集。

多表查询关系到别名 所以先定义类型

Collection 关联元素处理一对多关联。
员工多方,在多方配置一方
在这里插入图片描述
使用 resultMap 组装查询结果

<resultMap id="empmap" type="Employee">
        <id column="id" property="id"></id>
        <result column="ename" property="name"></result>
        <result column="age" property="age"></result>
        <association property="dept" javaType="Dept">
            <result column="dname" property="name"></result>
        </association>
        <association property="admin" javaType="Admin">
            <result column="account" property="account"></result>
        </association>
    </resultMap>

然后用别名来查询

<select id="getEmployeeById" resultMap="empmap">
        SELECT
     emp.id,
     emp.name ename,
     emp.age,
     d.name dname,
     a.account
FROM employee emp LEFT JOIN dept d ON emp.deptId = d.id
                  LEFT JOIN admin a ON emp.adminId = a.id
WHERE emp.id = #{id}
    </select>

部门一方,配置多方集合
在这里插入图片描述

<resultMap id="deptmap" type="Dept">
        <id column="id" property="id"></id>
        <result column="dname" property="name"></result>
        <association property="admin" javaType="Admin">
            <result column="account" property="account"></result>
        </association>
        <collection property="employeeList" javaType="list" ofType="Employee"> <!--声名他是集合 在声名他是Employee  第一个名字直接跟变量名对应就行-->
            <result column="ename" property="name"></result>
        </collection>
    </resultMap>

查询

<select id="getDeptById" resultMap="deptmap">
SELECT
   d.id,
   d.name dname,
   a.account,
   emp.name ename
FROM dept d LEFT JOIN admin a ON d.adminId = a.id
            LEFT JOIN employee emp ON d.id = emp.deptId
WHERE d.id = #{id}
    </select>

懒加载

    需要查询关联信息时,使用 Mybatis 懒加载特性可有效的减少数据库压力首次查询只查询主表信息,关联表的信息在用户获取时再加载。
    Mybatis 一对一关联的 association 和一对多的 collection 可以实现懒加 载。懒加载时要使用 resultMap,不能使用 resultType。

启动懒加载:
Mybatis 默认没有打开懒加载配置,需要在 SqlMapperConfig.xml 中通过 settings 配置 lazyLoadingEnabled 来开启懒加载。
在这里插入图片描述

<setting name="lazyLoadTriggerMethods" value="true"/>

代码:

<resultMap id="empmap1" type="Employee">
        <id property="id" column="id"></id>
        <result property="name"  column="name"></result>
        <result property="age" column="age"></result>
        <association property="admin" javaType="Admin" fetchType="lazy"
                     select="findAdminById" column="adminId">
            <result property="account" column="account"></result>
        </association>

        <association property="dept" javaType="Dept" fetchType="lazy"
                     select="findDeptById" column="deptId">
            <result property="name" column="name"></result>
        </association>

    </resultMap>
  1. Select:指定关联查询懒加载对象的 Mapper Statement ID 是findDeptByID
  2. column=“dept_id”:关联查询时将 dept_id 列的值传入 findDeptByID, 并将 findDeptByID 查询的结果映射到 Emp 的 dept 属性中
  3. collection 和 association 都需要配置 select 和 column 属性,两者配置方法 相同

注解

常用注解标签

  1. @Insert : 插入 sql , 和 xml insert sql 语法完全一样
  2. @Select : 查询 sql, 和 xml select sql 语法完全一样
  3. @Update : 更新 sql, 和 xml update sql 语法完全一样
  4. @Delete : 删除 sql, 和 xml delete sql 语法完全一样
  5. @Param : 入参 @Results : 设置结果集合
  6. @Result : 结果

使用案例

查询所有信息:

@Select("select * from t_emp") 
@Results(id = "empMap",value = { 
@Result(column = "emp_id",property = "empId",id = true), 
@Result(column = "emp_name",property = "empName"),
@Result(column = "emp_tel",property = "empTel"), 
@Result(column = "emp_education",property = "empEducation"), @Result(column = "emp_birthday",property = "empBirthday") 
})
List<Employee> getAll();

查询单个信息

@Select("select * from t_emp where emp_id=#{empId}")
@ResultMap(value="empMap") 
Employee getById(@Param("empId") Integer empId);

插入信息

@Insert("insert into t_emp (emp_id, emp_name, emp_tel, " + " emp_education, emp_birthday, fk_dept_id" + " )" 
values (#{empId}, #{empName}, #{empTel}, " 
+ " #{empEducation}, #{empBirthday}, #{fkDeptId}" + " )")

int insert(Employee record);

删除信息

@Delete("delete from t_emp where emp_id=#{empId}")
int deleteByPrimaryKey(@Param("empId") Integer empId);

Mybatis 动态 SQL

**MyBatis 的一个强大的特性之一通常是它的动态 SQL 能力。**MyBatis 中用于实现动态 SQL 的元素主要有: Ifwhere trimset choose (when, otherwise) foreach

If 元素

if 标签可以对传入的条件进行判断

<select id="getEmployee" parameterType="Employee" resultMap="empmap" useCache="true">
SELECT
     emp.id,
     emp.name ename,
     emp.age,
     d.name dname,
     a.account
FROM employee emp LEFT JOIN dept d ON emp.deptId = d.id
                  LEFT JOIN admin a ON emp.adminId = a.id
    <where>
        <if test=" name!=null &amp; name !='' ">
            emp.name = #{name}
        </if>
        <if test="age != 0">
            and emp.age = #{age}
        </if>
    </where>
</select>

对于查询条件个数不确定的情况,可使用元素。
元素会进行判断,如果它包含的标签中有返回值的话,它就插入一个 ‘where’。此外,如果标签返回的内容是以 AND 或 OR 开头,它会剔除掉 AND 或 OR。

trim 元素

<!--trim来实现  prefix 关键字  prefixOverrides覆盖首部指定内容
Choose 元素
-->
        SELECT
        emp.id,
        emp.name ename,
        emp.age,
        d.name dname,
        a.account
        FROM employee emp LEFT JOIN dept d ON emp.deptId = d.id
        LEFT JOIN admin a ON emp.adminId = a.id
        <trim prefix="where" prefixOverrides="and">
        <choose>
            <when test="name!=null &amp; name!=''">
                and emp.name = #{name}
            </when>
            <otherwise>
                and emp.name = '张三'
            </otherwise>
        </choose>
        </trim>
    </select>

Choose 元素

SELECT
        emp.id,
        emp.name ename,
        emp.age,
        d.name dname,
        a.account
        FROM employee emp LEFT JOIN dept d ON emp.deptId = d.id
        LEFT JOIN admin a ON emp.adminId = a.id
        <trim prefix="where" prefixOverrides="and">
        <choose>
            <when test="name!=null &amp; name!=''">
                and emp.name = #{name}
            </when>
            <otherwise>
                and emp.name = '张三'
            </otherwise>
        </choose>
        </trim>
    </select>

Set 元素

Set 元素可以把最后一个逗号去掉

<update id="updateEmployee" parameterType="Employee">
        update employee
        <set>
            <if test="name!=null">
                name = #{name},
            </if>
            <if test="age!=null">
                age = #{age},
            </if>
            <if test="dept.id!=0">
                deptId = #{dept.id},
            </if>
        </set>
        where id = #{id}
    </update>

多用于和if搭配来修改数据库
也可以使用 trim 实现

Foreach 元素

主要用在构建 in 条件中,它可以在 SQL 语句中进行迭代一个集合。foreach 元素的属性主要有== item,index,collection,open,separator,close。 item ==表示集合中每一个元素进行迭代时的别名,index 指定一个名字,用于 表示在迭代过程中,每次迭代到的位置,open 表示该语句以什么开始, separator 表示在每次进行迭代之间以什么符号作为分隔符,close 表示以什 么结束,在使用 foreach 的时候最关键的也是最容易出错的就是 collection 属性,该属性是必须指定的,但是在不同情况下,该属性的值是不一样的。

  1. 如果传入的是单参数且参数类型是一个 List 的时候,collection 属 性值为 list
  2. 如果传入的是单参数且参数类型是一个 array 数组的时候, collection 的属性值为 array

特殊符号处理

在 mybatis 中的 xml 文件中,存在一些特殊的符号,比如:<、>、"、&、<> 等,正常书写 mybatis 会报错,需要对这些符号进行转义。具体转义如下所示:
特殊字符 转义字符

<        &lt; 
>       &gt; 
"       &quot;&apos; 
&       &amp;

除了可以使用上述转义字符外,还可以使用<![CDATA[]]>来包裹特殊字符。如 下所示:

<if test="id != null"> AND <![CDATA[ id <> #{id} ]]> </if> <![CDATA[ ]]>是 XML 语法。在 CDATA 内部的所有内容都会被解析器忽略

但是有个问题那就是

<if> </if> <where> </where> <choose> </choose> <trim> </trim>

等这些标签都不会被解析,所以 我们只把有特殊字符的语句放在 <![CDATA[ ]]> 尽量缩小<![CDATA[ ]]> 的范围。

Mybatis的一级缓存和二级缓存

缓存(cache)的作用是为了减去数据库的压力(查询对数据库的压力是最大的),提高数据库的性能。缓存实现的 原理是从数据库中查询出来的对象在使用完后不要销毁,而是存储在内存(缓存) 中,当再次需要获取该对象时,直接从内存(缓存)中直接获取,不再向数据库 执行 select 语句,从而减少了对数据库的查询次数,因此提高了数据库的性能。缓存是使用 Map 集合缓存数据的。

Mybatis 有一级缓存和二级缓存。一级缓存的作用域是同一个 SqlSession, 在同一个 sqlSession 中两次执行相同的 sql 语句,第一次执行完毕会将数据库 中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查 询,从而提高查询效率。当一个 sqlSession 结束后该 sqlSession 中的一级缓存 也就不存在了。Mybatis 默认开启一级缓存。

二 级 缓 存 是 多 个 SqlSession 共 享 的 , 其 作 用 域 是 mapper 的 同 一 个 namespace,不同的 sqlSession 两次执行相同 namespace 下的 sql 语句且向 sql 中传递参数也相同即最终执行相同的 sql 语句,第一次执行完毕会将数据库 中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。Mybatis 默认没有开启二级缓存需要在 setting 全局参 数中配置开启二级缓存

一级缓存

Mybatis 对缓存提供支持,但是在没有配置的默认情况下,它只开启一级缓 存,一级缓存只是相对于同一个 SqlSession 而言。所以在参数和 SQL 完全一样 的情况下,我们使用同一个 SqlSession 对象调用一个 Mapper 方法,往往只执 行一次 SQL,因为使用 SelSession 第一次查询后,MyBatis 会将其放在缓存中, 以后再查询的时候,如果没有声明需要刷新,并且缓存没有超时的情况下, SqlSession 都会取出当前缓存的数据,而不会再次发送 SQL 到数据库。
在这里插入图片描述
一级缓存的生命周期

  1. MyBatis 在开启一个数据库会话时,会 创建一个新的 SqlSession 对象, SqlSession 对象中会有一个新的 Executor 对象。Executor 对象中持有一个新 的 PerpetualCache 对象,如果 SqlSession 调用了 close()方法,会释放掉一级 缓存 PerpetualCache 对象,一级缓存将不可用。
  2. 如果 SqlSession 调用了 clearCache(),会清空 PerpetualCache 对象 中的数据,但是该对象仍可使用
  3. SqlSession 中执行了任何一个 update 操作(update()、delete()、 insert()) ,都会清空 PerpetualCache 对象的数据,但是该对象可以继续使用

二级缓存

二级缓存是 SqlSessionFactory 级别的,根据 mapper 的 namespace 划分区域 的,相同 namespace 的 mapper 查询的数据缓存在同一个区域,如果使用 mapper 代理方法每个 mapper 的 namespace 都不同,此时可以理解为二级缓 存区域是根据 mapper 划分。

每次查询会先从缓存区域查找,如果找不到则从数据库查询,并将查询到数据写 入缓存。Mybatis 内部存储缓存使用一个 HashMap,key 为hashCode+sqlId+Sql 语句。value 为从查询出来映射生成的 java 对象。 sqlSession 执行 insert、update、delete 等操作 commit 提交后会清空缓存区域,防止脏读。

MyBatis 的缓存机制整体设计以及二级缓存的工作模式
在这里插入图片描述
配置二级缓存配置
第一步:启用二级缓存
mybatis的配置如下:

<settings>    
        <!--二级缓存-->
        <setting name="cacheEnabled" value="true"/>
</settings>

第二步:POJO 序列化
将所有的 model 类实现序列化接口 Java.io. Serializable。
在这里插入图片描述
第三步:配置映射文件
在 Mapper 映射文件中添加,表示此 mapper 开启二级缓存。 当 SqlSeesion 关闭时,会将数据存入到二级缓存。

<!--
     flushCache="true" 是否刷新缓存区数据  true-刷新 false-不刷新
    -->
    <cache flushInterval="3000"></cache>

在这里插入图片描述
useCache=“true” 开启二级缓存

增加、修改、删除的标签后面可以加flusrCache 值为true或false 表示每次操作是否清空缓存区 true为清空 放置脏读
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值