Mybatis

Mybatis

一、什么是 MyBatis?

官方解释

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

理解

  1. MyBatis内部封装了JDBC,不需要花费精力去处理加载驱动、创建连接、创建statement、释放资源等繁杂的过程,开发时只需要关注SQL语句。
  1. MyBatis可以使用XML或注解来配置和映射原生信息,将POJO映射成数据库中的记录,避免了手动配置参数和获取结果集。

在这里插入图片描述

二、XML中的标签

增删改查(insert、delete、update、select)

在Mybatis中使用XML映射文件方式开发,需要符合一定的规范:

  1. XML映射文件的名称与Mapper接口名称一致,并且将XML映射文件和Mapper接口放置在相同包下
    (同包同名)
  2. XML映射文件的namespace属性为Mapper接口全限定名一致
  3. XML映射文件中sql语句的id与Mapper接口中的方法名一致,并保持返回类型一致。

注解开发:

@Mapper
public interface EmpMapper {
@Select("select * from emp " +
"where name like concat('%',#{name},'%') " +
"and gender = #{gender} " +
"and entrydate between #{begin} and #{end} " +
"order by update_time desc")
public List<Emp> list(String name, Short gender, LocalDate
begin, LocalDate end);
}

标签开发:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.EmpMapper">
<!--查询操作-->
<select id="list" resultType="com.itheima.pojo.Emp">
 select * from emp
 where name like concat('%',#{name},'%')
 and gender = #{gender}
 and entrydate between #{begin} and #{end}
 order by update_time desc
</select>
</mapper>
三、动态sql

if :用于判断条件是否成立。使用test属性进行条件判断,如果条件为true,则拼接SQL。

<if test="条件表达式">
要拼接的sql语句
</if>

where:只会在子元素有内容的情况下才插入where子句,而且会自动去除子句的开头的AND或Or

<select id="list" resultType="com.itheima.pojo.Emp">
    select * from emp
    <where>
        <!-- if做为where标签的子元素 -->
        <if test="name != null">
        and name like concat('%',#{name},'%')
        </if>
        <if test="gender != null">
        and gender = #{gender}
        </if>
        <if test="begin != null and end != null">
        and entrydate between #{begin} and #{end}
        </if>
    </where>
     order by update_time desc
</select>

set :动态的在SQL语句中插入set关键字,并会删掉额外的逗号。(用于update语句中)

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.EmpMapper">
    <!--更新操作-->
    <update id="update">
    update emp
    <!-- 使用set标签,代替update语句中的set关键字 -->
    <set>
        <if test="username != null">
            username=#{username},
        </if>
        <if test="name != null">
            name=#{name},
        </if>
        <if test="gender != null">
            gender=#{gender},
        </if>
        <if test="image != null">
            image=#{image},
        </if>
        <if test="job != null">
            job=#{job},
        </if>
        <if test="entrydate != null">
            entrydate=#{entrydate},
        </if>
        <if test="deptId != null">
            dept_id=#{deptId},
        </if>
        <if test="updateTime != null">
            update_time=#{updateTime}
        </if>
    </set>
    where id=#{id}
    </update>
</mapper>

foreach:用于批量操作

<foreach collection="集合名称" item="集合遍历出来的元素/项" separator="每一次遍历使用的分隔符" open="遍历开始前拼接的片段" close="遍历结束后拼接的片段">
</foreach>

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.EmpMapper">
<!--删除操作-->
<delete id="deleteByIds">
 delete from emp where id in
    <foreach collection="ids" item="id" separator="," open="(" close=")">
        #{id}
    </foreach>
</delete>
</mapper>

我们可以对重复的代码片段进行抽取,将其通过 sql标签封装到一个SQL片段,然后再通include标签进行引用。

sql :定义可重用的SQL片段
include :通过属性refid,指定包含的SQL片段

<sql id="commonSelect">
 select id, username, password, name, gender, image, job,
entrydate, dept_id, create_time, update_time from emp
</sql>

<select id="list" resultType="com.itheima.pojo.Emp">
<include refid="commonSelect"/>
<where>
    <if test="name != null">
        name like concat('%',#{name},'%')
    </if>
    <if test="gender != null">
        and gender = #{gender}
    </if>
    <if test="begin != null and end != null">
        and entrydate between #{begin} and #{end}
    </if>
</where>
 order by update_time desc
</select>
四、#{}与${}的区别

在Mybatis中提供的参数占位符有两种:${…} 、#{…}

  • #{…}
    • 执行SQL时,会将#{…}替换为?,生成预编译SQL,会自动设置参数值
    • 使用时机:参数传递,都使用#{…}
  • ${…}
    • 拼接SQL。直接将参数拼接在SQL语句中,存在SQL注入问题
    • 使用时机:如果对表名、列表进行动态设置时使用

预编译优势:

  1. 性能更高:预编译SQL,编译一次之后会将编译后的SQL语句缓存起来,后面再次执行这条语句
    时,不会再次编译。(只是输入的参数不同)
  1. 更安全(防止SQL注入):将敏感字进行转义,保障SQL的安全性。

在这里插入图片描述

五、实体类中属性名与表中字段名不一样,怎么办

实体类属性名和数据库表查询返回的字段名一致,mybatis会自动封装。
如果实体类属性名和数据库表查询返回的字段名不一致,不能自动封装。
有了列名与属性名的映射关系后,Mybatis通过反射创建对象,同时使用反射给对象的属性逐一赋 值并返回,那些找不到映射关系的属性,是无法完成赋值的。
解决方案:

1.起别名:在SQL语句中,对不一样的列名起别名,别名和实体类属性名一样

2.手动结果映射:通过 @Results及@Result 进行手动结果映射

3.开启驼峰命名(推荐):如果字段名与属性名符合驼峰命名规则,mybatis会自动通过驼峰命名规则映射

mybatis.configuration.map-underscore-to-camel-case=true
六、MyBatis和ORM的区别

mybatis属于半orm,因为sql语句需要自己写。

与其他比较标准的ORM框架(比如Hibernate )不同,mybatis 并没有将java对象与数据库关联起来,而是将java方法与sql语句关联起来,mybatis 允许用户充分利用数据库的各种功能,例如存储、视图、各种复杂的查询以及某些数据库的专有特性。
自己写sql语句的好处是,可以根据自己的需求,写出最优的sql语句,灵活性高。但是,由于是自己写sql语句,导致平台可移植性不高。MySQL语句和Oracle语句不同

七、MyBatis分页

1.Mybatis使用RowBounds对象进行分页,它是针对ResultSet结果集执行的内存分页,而非物理分页。

    @Override
    @Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.SUPPORTS)
    public List<RoleBean> queryRolesByPage(String roleName, int start, int limit) {
        return roleDao.queryRolesByPage(roleName, new RowBounds(start, limit));
    }

2.原始方法,使用 limit,需要自己处理分页逻辑

mybatis接口

List<Student> queryStudentsBySql(Map<String,Object> data);

xml文件

<select id="queryStudentsBySql" parameterType="map" resultMap="studentmapper">
        select * from student limit #{currIndex} , #{pageSize}
</select>

service

//接口
List<Student> queryStudentsBySql(int currPage, int pageSize);
//实现类
public List<Student> queryStudentsBySql(int currPage, int pageSize) {
        Map<String, Object> data = new HashedMap();
        data.put("currIndex", (currPage-1)*pageSize);
        data.put("pageSize", pageSize);
        return studentMapper.queryStudentsBySql(data);
}

3.使用分页插件

数据量小时,RowBounds不失为一种好办法。但是数据量大时,实现拦截器就很有必要了

拦截StatementHandler,然后重写sql,添加对应的物理分页语句和物理分页参数。其实质还是在最后生成limit语句

八、MyBatis如何获取自动生成的id

默认情况下,执行插入操作时,是不会主键值返回的。如果我们想要拿到主键值,需要在Mapper 接口中的方法上添加一个Options注解,并在注解中指定属性useGeneratedKeys=true和 keyProperty=“实体类属性名”

@Mapper
public interface EmpMapper {
//会自动将生成的主键值,赋值给emp对象的id属性
@Options(useGeneratedKeys = true,keyProperty = "id")
@Insert("insert into emp(username, name, gender, image, job,entrydate, dept_id, create_time, update_time) values (#{username}, #{name}, #{gender}, #{image}, #{job}, #{entrydate}, #{deptId}, #{createTime}, #{updateTime})")
public void insert(Emp emp);
}
九、MyBatis缓存

缓存机制减轻数据库压力,提高数据库性能

mybatis的缓存分为两级:一级缓存、二级缓存

一级缓存:

一级缓存为 SqlSession 缓存,缓存的数据只在 SqlSession 内有效。在操作数据库的时候需要先创建 SqlSession 会话对象,在对象中有一个 HashMap 用于存储缓存数据,此 HashMap 是当前会话对象私有的,别的 SqlSession 会话对象无法访问。

具体流程:

第一次执行 select 完毕会将查到的数据写入 SqlSession 内的 HashMap 中缓存起来

第二次执行 select 会从缓存中查数据,如果 select 同传参数一样,那么就能从缓存中返回数据,不用去数据库了,从而提高了效率

注意:

1、如果 SqlSession 执行了 DML 操作(insert、update、delete),并 commit 了,那么 mybatis 就会清空当前 SqlSession 缓存中的所有缓存数据,这样可以保证缓存中的存的数据永远和数据库中一致,避免出现差异

2、当一个 SqlSession 结束后那么他里面的一级缓存也就不存在了, mybatis 默认是开启一级缓存,不需要配置

3、 mybatis 的缓存是基于 [namespace:sql语句:参数] 来进行缓存的,意思就是, SqlSession 的 HashMap 存储缓存数据时,是使用 [namespace:sql:参数] 作为 key ,查询返回的语句作为 value 保存的

二级缓存

二级缓存是 mapper 级别的缓存,也就是同一个 namespace 的 mapper.xml ,当多个 SqlSession 使用同一个 Mapper 操作数据库的时候,得到的数据会缓存在同一个二级缓存区域

二级缓存默认是没有开启的。需要在 setting 全局参数中配置开启二级缓存

开启二级缓存步骤:

1、conf.xml 配置全局变量开启二级缓存

<settings>
    <setting name="cacheEnabled" value="true"/>默认是false:关闭二级缓存
<settings>

2、在userMapper.xml中配置

<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>当前mapper下所有语句开启二级缓存

这里配置了一个 FIFO 缓存,并每隔60秒刷新,最大存储512个对象,而返回的对象是只读的,因此在不同线程中的调用者之间修改它们会导致冲突。可用的收回策略有,默认的是LRU

  1. LRU - 最近最少使用的;移除最长时间不被使用的对象。
  2. FIFO - 先进先出;按对象进入缓存的顺序来移除它们。
  3. SOFT- 软引用;移除基于垃圾回收器状态和软引用规则的对象
  4. WEAK - 弱引用;更积极地移除基干垃圾收集器状态和弱引用规则的对象

若想禁用当前select语句的二级缓存,添加 useCache="false"修改如下:

<select id="getCountByName" parameterType="java.util.Map" resultType="INTEGER" statementType="CALLABLE" useCache="false">

具体流程:

1.当一个sqlseesion执行了一次 select 后,在关闭此 session 的时候,会将查询结果缓存到二级缓存

2.当另一个sqlsession执行 select 时,首先会在他自己的一级缓存中找,如果没找到,就回去二级缓存中找,找到了就返回,就不用去数据库了,从而减少了数据库压力提高了性能

注意:

1、如果 SqlSession 执行了 DML 操作(insert、update、delete),并 commit 了,那么 mybatis 就会清空当前 mapper 缓存中的所有缓存数据,这样可以保证缓存中的存的数据永远和数据库中一致,避免出现差异

2、 mybatis 的一级缓存是基于[namespace:sql语句:参数]来进行缓存的,意思就是,SqlSessionHashMap 存储缓存数据时,是使用 [namespace:sql:参数] 作为 key ,查询返回的语句作为 value 保存的。

十、MyBatis-Plus
  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
  • 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
  • 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
  • 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
  • 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
  • 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
  • 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
  • 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
  • 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
  • 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
  • 内置性能分析插件:可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
  • 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

秋千水竹马道

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

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

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

打赏作者

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

抵扣说明:

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

余额充值