SSM-MyBatis-总结

news2025/1/30 19:15:19

文章目录

    • 一、Hello MyBatis
      • 1.1 流程
      • 1.2 总结
    • 二、Crud 的一些注意点
    • 三、参数传递
      • 3.1 #{ } VS ${ }
      • 3.2 单、复参数传递
        • (1)单参数
        • (2)多参数 -- @Param
        • (3)总结
    • 四、查询结果返回--结果封装
        • 4.1 @ResultType 一般返回
        • 4.2 @ResultMap 使用自定义映射规则
        • 4.3 总结,最佳实践
    • 五、自定义结果集
      • ▽ 关联关系
      • 5.1 关联查询
        • (1) 一对一 ^【association】^
        • (2) 一对多^【collection】^
      • 5.2 分布查询
        • (1)原生分布查询
        • (2)MyBatis自动分布查询
        • (3)延迟查询^(延迟加载)^
    • 六、动态SQL语句
      • 6.1 < if >标签
      • 6.2 < where >标签
      • 6.3 < set >标签
      • 6.4 < trim >标签^(自定义截串规则)^
        • (1)内部属性
        • (2)对where标签的替换:
        • (3)对于set标签的替换
      • 6.5 choose--when--otherwise 标签
      • 6.6 < foreach > 标签
        • (1)内部属性
        • (2)基本使用:遍历插入/查询
        • (3)基本使用:遍历更新/删除
        • ▽ 是否使用多sql一起发送
      • ▽ 可重复字段
      • 6.7 总结
    • ▽ XML文件的转义字符
    • 七、缓存机制
      • 6.1 什么是缓存机制
      • 6.2 MyBatis的缓存机制
    • 八、插件机制
      • 8.1 插件拦截
      • 8.2 应用:PageHelper 分页插件
        • (1)基本使用
        • (2)进阶使用^(前后端请求响应交互)^

一、Hello MyBatis

1.1 流程

  1. 导入MyBatis、配置数据库

  2. 创建Bean组件

@Data
public class Employee {
    Integer id;
    String name;
    Integer age;
    Double salary;

    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", salary=" + salary +
                '}';
    }
}
  1. 创建Dao层接口(在MyBatis中文件目录写成mapper),并标识@Mapper
@Mapper
public interface EmpMapper {

    Employee getEmpById(Integer id);

}
  1. 通过IDEA的插件创建resources目录中的mapper.xml,xml文件中的select、update…方法会被MyBatis自动通过代理对象生成对应的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.syq.mybatis02.dao.EmpMapper">
    <select id="getEmpById" resultType="com.syq.mybatis02.bean.Employee">
        select id, emp_name as name, age, emp_salary as salary from t_emp where id = #{id}
    </select>
</mapper>

注意:

  • 数据库查询的字段和bean对象的名字是否一样,如果不一样需要使用as 把查询到的数据库的字段名改掉
  • 注意xml文件中的方法和mapper接口的方法名、返回类型、(参数类型)是否一样,
  1. **在 [application.properties]中配置对应的mapper.xml **
mybatis.mapper-locations=classpath:mapper/**.xml
  1. 实际使用
@SpringBootTest
class MyBatis02ApplicationTests {

    @Autowired
    EmpMapper empMapper;// 如果配置错误那么会显示无法注入
    @Test
    void contextLoads() {
        Employee empById = empMapper.getEmpById(1);
        System.out.println("emp:"+empById);

    }

}

1.2 总结

  • MyBatis是个半自动的Dao工具,配置比较复杂但后续使用还算方便,一定要认真检查配置
  • MyBatis的底层是接口的代理实现,实际使用的也是代理出来的对象和方法,当配置有误时–>代理没有正常创建,就无法通过接口的多态性注入到容器中

二、Crud 的一些注意点

  1. 打开sql日志

    Mybatis默认在运行时,会根据xml文件中的sql生成sql,如果想要看自动生成的sql是什么样的可以开启sql日志,会在控制台中显示

    application.properties中:

    mybatis.mapper-locations=classpath:mapper/**.xml
    
  2. 获取数据库自增字段信息(自增信息回填)

    对于自增的数据库属性,比如传入一个Employee对象后、把数据库中自动生成的值传入原Employee对象的对应属性

    • useGeneratedKeys 使用生成的键 ture/false
    • keyProperty 键属性 对应属性

    mapper.xml中:

    <insert id="addEmp" parameterType="com.syq.mybatis02.bean.Employee" useGeneratedKeys="true" keyProperty="id"><!-- 这里 -->
        insert into t_emp(emp_name, age, emp_salary) values (#{name}, #{age}, #{salary})
    </insert>
    
  3. **查询所有或者查询多个 **

    在JDBC中需要把Dao方法返回类型设置为List,但是在MyBatis中的,mapper.xml直接使用Bean接收就行了,会自动封装成List

    @Mapper
    public interface EmpMapper {
    	List<Employee> getAllEmp();
    }
    
    <select id="getAllEmp" resultType="com.syq.mybatis02.bean.Employee">
        select id, emp_name as name, age, emp_salary as salary from t_emp
    </select>
    
  4. 开启驼峰命名与_命名自动转换

    sql中的组合词以_分割,java中使用驼峰,开启后自动转换

    mybatis.configuration.map-underscore-to-camel-case=true
    

三、参数传递

sql语句需要一定灵活性,所以sql一些参数使用#{ }或者${ }传递

3.1 #{ } VS ${ }

他们都可以用于参数传递,但是仍有区别

  1. #{ }

    本质上就是预编译,将空位留给参数输入

    • 不会有sql注入问题
    • 只能传递属性的具体值–>比如age=18
  2. ${ }

    本质上是字符串拼接,直接把参数放到sql中

    • 有sql注入问题

      • 可以通过工具类先判断是否有sql注入风险,然后使用if限定
    • 除了值传递的功能以外,还可以传递类型–>比如指定具体的表名、字段名

3.2 单、复参数传递

(1)单参数

例如:

  1. 根据List< Integer >数组,通过第2个id查询

    Employee getEmpByIds(List<Integer> ids);
    
    <select id="getEmpByIds" resultType="com.syq.mybatis02.bean.Employee">
        select id, emp_name as name, age, emp_salary as salary from t_emp where id in=#{ids{1}}<!-- /索引从0开始 /只有一个容器时ids可以随便写比如abcd{1} -->
    </select>
    
  2. 根据对象查询

    Employee getEmpByEmp(Employee e);
    
    <select id="getEmpByEmp" resultType="com.syq.mybatis02.bean.Employee">
        select id, emp_name as name, age, emp_salary as salary from t_emp where id = #{id} <!-- 不用写e.id,如果写了相当于:有一个e的属性,e里面还有一个id属性 -->
    </select>
    

    此时,在mapper.xml配置中不一定要使用对应的形参名,因为只有一个参数,MyBatis自动会给匹配上

    但是,如果使用**@Param(“e”) 精确指定了参数名为e,则必须使用 e.属性** 来调用

  3. 根据Map内容查询

    Employee geEmpByMap(Map<String,Integer> map);
    
    <select id="geEmpByMap" resultType="com.syq.mybatis02.bean.Employee">
        select id, emp_name as name, age, emp_salary as salary from t_emp where id = #{id}
    </select>
    
(2)多参数 – @Param
List<Employee> getEmpAaa(
    @Param("id")Integer id,
    @Param("name") List<String> names,
    @Param("e") Employee employee
);
<select id="getEmpAaa" resultType="com.syq.mybatis02.bean.Employee">
    select id, emp_name as name, age, emp_salary as salary from t_emp where id = #{id} or emp_name = #{name} or emp_salary = #{e.salary}
</select>
  • 多参数时必须在mapper接口使用@Param指定参数名,xml中sql引入参数时也必须使用对应的参数名
(3)总结
  1. 最佳实践:

无论是一个参数还是多个参数,都使用@Param 标识

  1. 区别:
传参形式示例取值方式
单个参数 - 普通类型getEmploy(Long id)#{变量名}
单个参数 - List类型getEmploy(List id)#{变量名[0]}
单个参数 - 对象类型addEmploy(Employ e)#{对象中属性名}
单个参数 - Map类型addEmploy(Map<String,Object> m)#{map中属性名}
多个参数 - 无@ParamgetEmploy(Long id,String name)#{变量名} //新版兼容
多个参数 - 有@ParamgetEmploy(@Param(“id”)Long id, @Param(“name”)String name)#{param指定的名}
扩展:getEmploy(@Param(“id”)Long id, @Param(“ext”)Map<String,Object> m, @Param(“ids”)List ids, @Param(“emp”)Employ e)#{id}、 #{ext.name}、#{ext.age}, #{ids[0]}、#{ids[1]}, #{e.email}、#{e.age}

四、查询结果返回–结果封装

4.1 @ResultType 一般返回

本质上:
使用MyBatis的默认映射规则,把查询的内容封装到指定类型中

注意点:

  1. 返回对象、基本数据类型时:@ResultType=“全类名”
    • 虽然对于java自带的一些类型可以简写,但是不推荐
  2. 返回Map、List.集合时:@ResultType=“全类名”
    • 有时对于返回Map<,>封装结果的方法,插件生成的@ResultType使用了Map的全类名,此时虽然也得到了Map集合,但是其中的内容(Employee等自定义对象)也都变成了Map集合,这样不符合业务逻辑无法使用get方法,不建议使用
4.2 @ResultMap 使用自定义映射规则

有时就算开启了驼峰转换,或者是其他情况,此时我们可以自定义映射的规则,确保代码正常运行

例:

  • mapper.java

    Employee getEmpByName(@Param("name") String name);
    
  • mapper.xml

    <!--    自定义映射规则-->
        <resultMap id="EmployeeRM" type="com.syq.mybatis02.bean.Employee">
            <id property="id" column="id"/>
            <result property="name" column="emp_name"/>
            <result property="age" column="age"/>
            <result property="salary" column="emp_salary"/>
        </resultMap>
    <!--    使用该规则-->
    <select id="getEmpByName"  resultMap="EmployeeToResult">
        select id, emp_name , age, emp_salary  from t_emp where emp_name = #{name}
    </select>
    
    • id标签:主键
    • result标签:普通字段
4.3 总结,最佳实践

步骤:

  1. 开启驼峰命名转换
  2. 如果无法转换,使用@ResultMap

五、自定义结果集

▽ 关联关系

  1. 一对一:

    多表联查产生一对一关系,比如一个订单对应唯一的一个下单客户

    • 此时需要保存客户与订单的关系键到其中的一个表中
  2. 一对多:

    多表查询的是一对多的关系,比如一个客户的购物车中有多个订单

    • 此时把对应关系存到为多的那一方的表中
  3. 多对多:

    查询的对应关系不存在一方为一时,比如一个客户的对应商家有多个、一个商家又服务多个客户

    • 此时要新建一个中间表记录客户和商家的关系

5.1 关联查询

我们要获取具有关联性的数据,可以sql的表关联 (a join b on a.id=b.id),而要如何接收sql的查询结果,则是我们要考虑的

(1) 一对一 【association】

因为使用关联查询,我们在sql查询器中查询得到的是一排数据,为了将其中各个表的数据分开存储,我们在自定义映射规则中引入一种把部分属性封装到一个对象中的标签【association】

使用例:
Order 与Customer 两个javaBean有关联

<?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.syq.mybatis03.mappers.OrderMapper">
<!--    映射规则	-->
    <resultMap id="OrderRM" type="com.syq.mybatis03.bean.Order">
        <id column="id" property="id"></id>
        <result column="address" property="address"></result>
        <result column="amount" property="amount"></result>
        <association property="customer" javaType="com.syq.mybatis03.bean.Customer"><!-- 重点 -->
            <id column="customer_id" property="id"></id>
            <result column="customer_name" property="customerName"></result>
            <result column="phone" property="phone"></result>
        </association>
    </resultMap>

<!--    sql代码	-->
    <select id="getOrderByIdWithCustomer" resultMap="OrderRM">
        select o.*,
               c.id  customer_id,
               c.customer_name customer_name,
               c.phone
        from t_order o
                 left join t_customer c on o.customer_id =c.id
        where c.id=#{id};
    </select>


</mapper>
  • 我们发现在association 标签中除了property属性 指定封装属性,还要使用javaType属性 指定对象的类型
(2) 一对多【collection】

当面对一对多关系时,查询结果有时为多行,此时我们不仅要将部分数据封装,而且要对于多行进行处理,处理成一个List集合,我们在自定义映射规则中引入一种把部分属性封装到一个List<对象>中的标签【collection】

使用例:

<?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" >
<!--suppress ALL -->
<mapper namespace="com.syq.mybatis03.mappers.CustomerMapper">
<!--    映射规则-->
    <resultMap id="CustomerRM" type="com.syq.mybatis03.bean.Customer">
        <id column="c_id" property="id"></id>
        <result column="customer_name" property="customerName"></result>
        <result column="phone" property="phone"></result>

        <collection property="orders" ofType="com.syq.mybatis03.bean.Order">
            <id column="id" property="id"></id>
            <result column="address" property="address"></result>
            <result column="amount" property="amount"></result>
            <result column="c_id" property="customerId"></result>
        </collection>
    </resultMap>

<!--    sql语句-->
    <select id="getCustomerById" resultMap="CustomerRM">
        select c.id c_id,
               c.phone phone,
               c.customer_name customer_name,
               o.*
        from t_customer c
                 left join t_order o on c.id =o.customer_id
        where c.id=${id};
    </select>
</mapper>
  • 我们发现在collection 标签中除了property属性 指定封装属性,还要使用ofType属性 指定集合元素的类型association则是javaType

5.2 分布查询

关联查询适用于有关联关系的查询(本质上是对通过关联关系查询一次得到的容器进行分层封装),而还有一种“分布查询”的方式,除了关联关系对于没有关联关系的查询也能得到结果(本质上是对于头一次sql得不到的数据,进行再次查询,依次类推直到数据全部得到并将其封装完毕)

(1)原生分布查询

通过方法手动调用多次Dao方法,如何把结果封装并返回

(2)MyBatis自动分布查询

分布查询的本质是对于头一次sql得不到的数据,进行多次查询,也就是多个sql语句,同理我们在xml中配置多个< select > 标签并整合到resultMap 的 collection或者association的select中就能做到分布查询

基础sql:

<select id="getOrderByIdWithCustomer2" resultMap="OrderRM2">
    select *
    from t_order
    where id = #{id};
</select>
  • 使用的是resultMap,不是resultType

resultMap:

<!--    分布查询,获取Order-->
    <resultMap id="OrderRM2" type="com.syq.mybatis03.bean.Order">
        <id column="id" property="id"></id>
        <result column="address" property="address"></result>
        <result column="amount" property="amount"></result>
        <collection property="customer"<!-- 重点 -->
                    select="com.syq.mybatis03.mappers.OrderMapper.getCustomerByOrderId"<!-- 重点 -->
                    column="{id=customer_id}">
        </collection>
    </resultMap>
  • 这里使用的是collection标签处理一对多关系,同理association也可以
  • select=" "指定的最好是全类名
  • column=" “指定的是sql方法对应的参数,写成KeyV形式,有多个时:”{K=V,K=V,K=V}"

内层sql:

<select id="getCustomerByOrderId" resultType="com.syq.mybatis03.bean.Customer">
        select *
        from t_customer
        where id=#{id};
</select>
  • 这里使用的是resultType 而不是Map,以此类推:
    • 如果是Map属性:则表示仍要继续
    • 如果是Type属性:表示不用继续了,已经到结尾了

注意点:
要小心查询,避免出现无限重复的查询代码,引发栈溢出等问题
要规避的话可以:注意分布查询存在有resultType的select标签,注意每次使用resultMap所指向的封装规则

(3)延迟查询(延迟加载)

当使用的Dao方法会引发大量的分布查询时,我们可以通过延迟查询机制(类似于懒加载的延迟),只加载到方法所需要的数据的那一步,从而减少损耗

对应的两行配置

•mybatis.configuration.lazy-loading-enabled=true

•mybatis.configuration.aggressive-lazy-loading=false

mybatis.configuration.lazy-loading-enabled=true

mybatis.configuration.aggressive-lazy-loading=false

六、动态SQL语句

先前我们学习MyBatis的查询,但是对于sql只能拼接处理,不能在sql语句的层次进行分支、条件……的判断,而MyBatis可以通过一系列标签来做到这种动态变化

6.1 < if >标签

和JavaSE中的if类似,但是判断条件是其中的 test属性

以根据id 或者age 获取Emp为例子

  1. .java文件:
@Mapper
public interface EmpMapper {
    // 根据id或者age获取Emp
    List<Emp> getEmps1(@Param("id") Integer id,
                       @Param("age") Integer age);
}
  1. .xml文件:
<?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.syq.mybatis03.mappers.EmpMapper">
    <select id="getEmps1" resultType="com.syq.mybatis03.bean.Emp">
        select * from t_emp
        where
        <if test="id!=null"><!-- 这里,判断不为空 -->
            id=#{id}
        </if>
        <if test="age!=null"><!-- 这里,判断不为空 -->
            and age=#{age};
        </if>
    </select>
</mapper>
  • 但是仅仅如此解决满足需求如果只传入一个age 属性,那么程序会出错(原因是拼接的是 and age=#{age},其中and 违反了sql的语法规范)

6.2 < where >标签

对于6.1中的实现方式,如果只传入一个age 属性,那么程序会出错
于是,通过where 标签,MyBatis 会将where标签中的and、or 等语法错误纠正,这样就能满足需求了

  1. .java文件(和上面一样):
@Mapper
public interface EmpMapper {
    // 根据id或者age获取Emp
    List<Emp> getEmps2(@Param("id") Integer id,
                       @Param("age") Integer age);
}
  1. .xml文件:
<?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.syq.mybatis03.mappers.EmpMapper">
    <select id="getEmps1" resultType="com.syq.mybatis03.bean.Emp">
        select * from t_emp
        <where><!-- 这里,设置了where标签 -->
        <if test="id!=null"><!-- 这里,判断不为空 -->
            id=#{id}
        </if>
        <if test="age!=null"><!-- 这里,判断不为空 -->
            and age=#{age};
        </if>
        </where>
    </select>
</mapper>

6.3 < set >标签

类似于where标签,但是是针对更新数据时使用

对于sql中的where我们有了处理的方法,但是举个例子:

update t_emp set 
	<if test="salary!=null">
        emp_salary=${salary} ,
	</if>
	<if test="id!=null">
		age=${age} where id=${id};
	</if>

对于这种情况,如果我们只用salary查询,那么会报错,因为结尾存在一个","号,此时,我们就要使用< set >标签

  1. .java文件:
void setEmp1(Emp emp);
  1. .xml文件:
<update id="setEmp1" parameterType="com.syq.mybatis03.bean.Emp">
    update t_emp
    <set>
        <if test="empSalary != null">
            emp_salary = #{empSalary},
        </if>
        <if test="age != null">
            age = #{age},
        </if>
        <if test="empName != null">
            emp_name = #{empName}
        </if>
    </set>
    where id = #{id};
</update>
  • **注意:**虽然不知道什么原因,只有一个对象参数使用@Param后居然无法调用其属性,自己写代码时要小心,或者之后搞明白原因

6.4 < trim >标签(自定义截串规则)

trim标签能替代where或者set标签,本质是:where和set都是对于if 中语句的特定前后缀进行判断修改,而trim 标签则是将修改的内容交给程序员(比如and为前缀,或是","为后缀)

(1)内部属性
属性用途
prefix设置前缀
suffix设置后缀
prefixOverrides前缀覆盖
suffixOverrides后缀覆盖
  • **注意:**这里的设置前后缀,只有当trim 中存在内容时才会推荐;如果没有内容,就没有前后缀
(2)对where标签的替换:

whre标签的本质是对于其内以and、or开头的字符串用 空 覆盖and、or

  1. .java文件:
// 根据id或者age获取Emp--2
List<Emp> getEmps2(@Param("id") Integer id,
                   @Param("age") Integer age);
  1. .xml文件:
<select id="getEmps2" resultType="com.syq.mybatis03.bean.Emp">
    select * from t_emp
    <trim prefix="where" prefixOverrides="and"><!-- 如果标签中包含的字符串以and开头,则用空的内容覆盖掉and -->
        <if test="id!=null">
            id=#{id}
        </if>
        <if test="age!=null">
            and age=#{age};
        </if>
    </trim>
</select>
  • **原理:**如果标签中包含的字符串以and开头,则用空的内容覆盖掉and
(3)对于set标签的替换

set标签的本质是对以“,”结尾的字符串用 空 替换

  1. .java文件:
// 更新Emp--2
void setEmp2(Emp emp);
  1. .xml文件:
<update id="setEmp2">
    update t_emp
    <trim prefix="set" suffixOverrides=","><!-- 如果标签中包含的字符串以“,”结尾,则用 空 的内容覆盖掉 -->
        <if test="empSalary != null">
            emp_salary = #{empSalary},
        </if>
        <if test="age != null">
            age = #{age},
        </if>
        <if test="empName != null">
            emp_name = #{empName}
        </if>
    </trim>
    where id = #{id};
</update>
  • **原理:**如果标签中包含的字符串以“,”结尾,则用 空 的内容覆盖掉

6.5 choose–when–otherwise 标签

就是javaSE中的switch–case–default 的翻版

仍然以根据id或者age获取Emp为例

  1. .java文件
// 根据id或者age获取Emp--3--这个方法只能使用一个参数
List<Emp> getEmps3(@Param("id") Integer id,
                   @Param("age") Integer age);
  1. .xml文件
<select id="getEmps3" resultType="com.syq.mybatis03.bean.Emp">
    select * from t_emp
    <where>
        <choose>
            <when test="id!=null">
                id=#{id}
            </when>
            <when test="age!=null">
                 age=#{age}
            </when>
            <otherwise>
                id=2
            </otherwise>
        </choose>
    </where>
</select>

6.6 < foreach > 标签

前面我们学了这么多但是当对于集合,我们无法确定集合中有多少参数,此时不妨使用foreach 来遍历集合,并拼接到sql语句中

(1)内部属性
属性作用
collection确定遍历的集合的名字
item指定集合的元素对应的实例名
separator指定每次遍历的分隔符
open整个遍历开始前的前缀
(2)基本使用:遍历插入/查询

其实也可以在查询时使用,不过这里直接以插入为代表

  1. java文件:
// 添加一堆Emp
void addEmp1(List<Emp> emps);
  1. xml文件:
<insert id="addEmp1">
    insert into t_emp(emp_name, age, emp_salary)
    values
    <foreach collection="emps" item="emp" separator=",">
        (#{emp.empName},#{emp.age},#{emp.empSalary})
    </foreach>
</insert>
(3)基本使用:遍历更新/删除

更新与删除在sql层面每一次都需要一个新的sql语句(以“;”结尾算是一条语句),所以需要用“;”号分隔遍历,而默认情况下MyBatis不支持一次Dao带有多条sql语句需要在配置数据库的代码后添加/mybatis-example?allowMultiQueries=true

spring.datasource.url=jdbc:mysql://localhost:3306/mybatis-example?allowMultiQueries=true#在这里
spring.datasource.username=root
spring.datasource.password=syq8257507
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

以遍历更新为例:

  1. .java文件:
// 更新一堆Emp
void setEmp3(List<Emp> emps);
  1. .xml文件:
<update id="setEmp3">
    update t_emp
    <foreach collection="emps" item="emp" separator=";">
        <set>
        <if test="emp.empSalary != null">
            emp_salary = #{emp.empSalary},
        </if>
        <if test="emp.age != null">
            age = #{emp.age},
        </if>
        <if test="emp.empName != null">
            emp_name = #{emp.empName}
        </if>
        </set>
    </foreach>
</update>

注意:

  • 如果不使用MyBatis遍历,而是在java代码中遍历,那么后者的效率远不如前者,因为后者的交互次数多,而前者只有一次
▽ 是否使用多sql一起发送

前面我们在遍历更新时使用了这种功能,虽然能大幅度提高效率,但是在一些情况下这种功能并不一定好

  • 当我们需要对每个sql做好事务回滚:如果使用这种功能,那么一旦后面有错,那么前面的数据也一块回滚了
  • 在分布式框架中不能使用:具体原因未知,等后续学到

▽ 可重复字段

当有些内容过长而且经常出现,此时可以用可重复字段代替

  1. 创建复用字段:
<sql id="user_name">
	id,emp_name empName,age
</sql>
  1. 使用:
    <select id="getEmp" resultType="com.syq.mybatis03.bean.Emp">
        select <include refid="user_name"></include><!-- 这里使用 -->
        from t_emp
        <where>
            <choose>
                <when test="id!=null">
                    id=#{id}
                </when>
                <when test="age!=null">
                     age=#{age}
                </when>
                <otherwise>
                    id=2
                </otherwise>
            </choose>
        </where>
    </select>

6.7 总结

动态SQL语句的实现原理是MyBatis的自动拼串机制,我们所谓的sql动态化其本质就是对该机制的应用。又由于MyBatis在xml文件中,所以我们使用了一系列似曾相识的标签来规范这种操作

▽ XML文件的转义字符

我们的MyBatis使用xml配置,所以一些字符要以xml的规则,写成转义字符

原始字符转义字符
&&
<<
>>
""
'

七、缓存机制

6.1 什么是缓存机制

字面意思,暂缓存储:通过N级的缓存,调节运算速度和存储空间,从而改善处理装置和存储装置之间交互效率

**例:**计算机的cpu和其硬盘

6.2 MyBatis的缓存机制

MyBatis作为服务器与数据源的交互处,为了效率考虑也建立了缓存的机制

  • 了解即可,以后有专门的工具

MyBatis 拥有二级缓存机制:

  • **一级缓存:**默认开启;

    • 事务级别:当前事务共享
  • **二级缓存:**手动配置开启

    • 开启方式:在mapper.xml中标识< cache/ >标签
    • 事务级别:所有事务共享
  • 缓存中有就不用查数据库;

八、插件机制

为了应对不同的情况,MyBatis 也能组载插件

  • 底层原理不用细究,过于复杂,大部分时间会用别人写的插件即可

8.1 插件拦截

MyBatis 底层使用 拦截器机制提供插件功能,方便用户在SQL执行前后进行拦截增强。

  • 拦截器:Interceptor

  • 拦截器可以拦截 四大对象 的执行

    • ParameterHandler:处理SQL的参数对象
    • ResultSetHandler:处理SQL的返回结果集
    • StatementHandler:数据库的处理对象,用于执行SQL语句
    • Executor:MyBatis的执行器,用于执行增删改查操作

8.2 应用:PageHelper 分页插件

虽然我们可以在sql语句中配置分页操作,但是实际业务比较麻烦,可以通过分页插件较为简单并统一地给前端发数据,在减少后端代码量的同时便于前端接收并使用分页数据

(1)基本使用
  1. maven配置:

    <dependency>
        <groupId>com.github.pagehelper</groupId>
        <artifactId>pagehelper</artifactId>
        <version>最新版本号</version>
    </dependency>
    
  2. 配置类配置:

    package com.syq.mybatis03.config;
    
    
    import com.github.pagehelper.PageInterceptor;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import java.util.Properties;
    
    @Configuration
    public class MyBatisConfig {
        @Bean
        PageInterceptor myBatisConfigBean(){
            // 创建分页插件
            PageInterceptor pageInterceptor = new PageInterceptor();
            // 设置,以分页合理化为例
            Properties properties = new Properties();// 创建设置项
            properties.setProperty("reasonable","true");// 修改设置项
            pageInterceptor.setProperties(properties);// 添加设置项
            // 完成插件配置
            return pageInterceptor;
        }
    }
    
    • **什么是分页合理化:**超过最大按最大页数算,少于1按1算
  3. 实际使用:

    @SpringBootTest
    public class PageTest {
        @Autowired
        EmpMapper empMapper;
        @Test
        public void test1() {
            PageHelper.startPage(1,3);// 在每次需要用到分页时使用:(当前页数,一页记录数)
            for (Emp emp : empMapper.getEmpAll()) {
                System.out.println(emp);
            }
    
        }
    }
    
  4. 注意:

    • 调用PageHelper.startPage( , ) 方法,只能对其后第一个方法调用有效,原理是后一个方法获取的是插件产生的共享数据,当方法获取完毕,该数据自动销毁
(2)进阶使用(前后端请求响应交互)

前面我们只是在后端进行了分页,但是对于真正开发来说,前端会需要更详细的信息,比如:总页数、当前是第几页、页面内容的大小。

此时我们使用PageInfo< Object > Info 对象,为前端获取这些信息

例:

@Test
public void test2() {
    PageHelper.startPage(1,3);
    List<Emp> empAll = empMapper.getEmpAll();
    System.out.println("--------------------************************");
    PageInfo<Emp> empPageInfo = new PageInfo<>(empAll);
    // 获取信息
    System.out.println("每页显示的条数:"+empPageInfo.getPageSize());// 每页显示的条数
    System.out.println("总页数:"+empPageInfo.getPages());// 总页数
    System.out.println("总记录数:"+empPageInfo.getTotal());// 总记录数

    System.out.println("查询结果:"+empPageInfo.getList());// 查询结果

}

效果:

在这里插入图片描述

  • **注:**实际使用时会放在Controller方法的返回值里(作为JSON串发给前端),info中的list属性对应的就是分页查询的结果,其他属性对应别的参数

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2286558.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

万字长文总结前端开发知识---JavaScriptVue3Axios

JavaScript学习目录 一、JavaScript1. 引入方式1.1 内部脚本 (Inline Script)1.2 外部脚本 (External Script) 2. 基础语法2.1 声明变量2.2 声明常量2.3 输出信息 3. 数据类型3.1 基本数据类型3.2 模板字符串 4. 函数4.1 具名函数 (Named Function)4.2 匿名函数 (Anonymous Fun…

Flutter android debug 编译报错问题。插件编译报错

下面相关内容 都以 Mac 电脑为例子。 一、问题 起因&#xff1a;&#xff08;更新 Android studio 2024.2.2.13、 Flutter SDK 3.27.2&#xff09; 最近 2025年 1 月 左右&#xff0c;我更新了 Android studio 和 Flutter SDK 再运行就会出现下面的问题。当然 下面的提示只是其…

【Proteus仿真】【51单片机】简易计算器系统设计

目录 一、主要功能 二、使用步骤 三、硬件资源 四、软件设计 五、实验现象 联系作者 一、主要功能 1、LCD1602液晶显示 2、矩阵按键​ 3、可以进行简单的加减乘除运算 4、最大 9999*9999 二、使用步骤 系统运行后&#xff0c;LCD1602显示数据&#xff0c;通过矩阵按键…

JavaScript函数中this的指向

总结&#xff1a;谁调用我&#xff0c;我就指向谁&#xff08;es6箭头函数不算&#xff09; 一、ES6之前 每一个函数内部都有一个关键字是 this &#xff0c;可以直接使用 重点&#xff1a; 函数内部的 this 只和函数的调用方式有关系&#xff0c;和函数的定义方式没有关系 …

51单片机入门_01_单片机(MCU)概述(使用STC89C52芯片;使用到的硬件及课程安排)

文章目录 1. 什么是单片机1.1 微型计算机的组成1.2 微型计算机的应用形态1.3 单板微型计算机1.4 单片机(MCU)1.4.1 单片机内部结构1.4.2 单片机应用系统的组成 1.5 80C51单片机系列1.5.1 STC公司的51单片机1.5.1 STC公司单片机的命名规则 2. 单片机的特点及应用领域2.1 单片机的…

51单片机入门_02_C语言基础0102

C语言基础部分可以参考我之前写的专栏C语言基础入门48篇 以及《从入门到就业C全栈班》中的C语言部分&#xff0c;本篇将会结合51单片机讲差异部分。 课程主要按照以下目录进行介绍。 文章目录 1. 进制转换2. C语言简介3. C语言中基本数据类型4. 标识符与关键字5. 变量与常量6.…

时间轮:XXL-JOB 高效、精准定时任务调度实现思路分析

大家好&#xff0c;我是此林。 定时任务是我们项目中经常会遇到的一个场景。那么如果让我们手动来实现一个定时任务框架&#xff0c;我们会怎么做呢&#xff1f; 1. 基础实现&#xff1a;简单的线程池时间轮询 最直接的方式是创建一个定时任务线程池&#xff0c;用户每提交一…

人工智能如何驱动SEO关键词优化策略的转型与效果提升

内容概要 随着数字化时代的到来&#xff0c;人工智能&#xff08;AI&#xff09;技术对各行各业的影响日益显著&#xff0c;在搜索引擎优化&#xff08;SEO&#xff09;领域尤为如此。AI的应用不仅改变了关键词研究的方法&#xff0c;而且提升了内容生成和搜索优化的效率&…

【NLP251】NLP RNN 系列网络

NLP251 系列主要记录从NLP基础网络结构到知识图谱的学习 &#xff11;.原理及网络结构 &#xff11;.&#xff11;&#xff32;&#xff2e;&#xff2e; 在Yoshua Bengio论文中( http://proceedings.mlr.press/v28/pascanu13.pdf )证明了梯度求导的一部分环节是一个指数模型…

【越学学糊涂的Linux系统】Linux指令篇(二)

一、pwd指令&#xff1a; 00x0:打印该用户当前目录下所属的文件路径 看指令框可以看出我用的是一个叫sw的用户&#xff0c;我们的路径就是在一个home目录下的sw目录下的class113文件路径。 也可以说是指出当前所处的工作目录 补充&#xff1a;&#x1f386;​​​​​​​Wi…

【AI论文】Omni-RGPT:通过标记令牌统一图像和视频的区域级理解

摘要&#xff1a;我们提出了Omni-RGPT&#xff0c;这是一个多模态大型语言模型&#xff0c;旨在促进图像和视频的区域级理解。为了在时空维度上实现一致的区域表示&#xff0c;我们引入了Token Mark&#xff0c;这是一组在视觉特征空间中突出目标区域的标记。这些标记通过使用区…

Java面试题2025-并发编程基础(多线程、锁、阻塞队列)

并发编程 一、线程的基础概念 一、基础概念 1.1 进程与线程A 什么是进程&#xff1f; 进程是指运行中的程序。 比如我们使用钉钉&#xff0c;浏览器&#xff0c;需要启动这个程序&#xff0c;操作系统会给这个程序分配一定的资源&#xff08;占用内存资源&#xff09;。 …

Three城市引擎地图插件Geo-3d

一、简介 基于Three开发&#xff0c;为Three 3D场景提供GIS能力和城市底座渲染能力。支持Web墨卡托、WGS84、GCJ02等坐标系&#xff0c;支持坐标转换&#xff0c;支持影像、地形、geojson建筑、道路&#xff0c;植被等渲染。支持自定义主题。 二、效果 三、代码 //插件初始化…

MySQL的复制

一、概述 1.复制解决的问题是让一台服务器的数据与其他服务器保持同步&#xff0c;即主库的数据可以同步到多台备库上&#xff0c;备库也可以配置成另外一台服务器的主库。这种操作一般不会增加主库的开销&#xff0c;主要是启用二进制日志带来的开销。 2.两种复制方式&#xf…

【后端开发】字节跳动青训营Cloudwego脚手架

Cloudwego脚手架使用 cwgo脚手架 cwgo脚手架 安装的命令&#xff1a; GOPROXYhttps://goproxy.cn/,direct go install github.com/cloudwego/cwgolatest依赖thriftgo的安装&#xff1a; go install github.com/cloudwego/thriftgolatest编辑echo.thrift文件用于生成项目&…

ArcGIS10.2 许可License点击始终启动无响应的解决办法及正常启动的前提

1、问题描述 在ArcGIS License Administrator中&#xff0c;手动点击“启动”无响应&#xff1b;且在计算机管理-服务中&#xff0c;无ArcGIS License 或者License的启动、停止、禁止等均为灰色&#xff0c;无法操作。 2、解决方法 ①通过cmd对service.txt进行手动服务的启动…

Cyber Security 101-Build Your Cyber Security Career-Security Principles(安全原则)

了解安全三元组以及常见的安全模型和原则。 任务1&#xff1a;介绍 安全已成为一个流行词;每家公司都想声称其产品或服务是安全的。但事实真的如此吗&#xff1f; 在我们开始讨论不同的安全原则之前&#xff0c;了解我们正在保护资产的对手至关重要。您是否试图阻止蹒跚学步…

NLP模型大对比:Transformer > RNN > n-gram

结论 Transformer 大于 RNN 大于 传统的n-gram n-gram VS Transformer 我们可以用一个 图书馆查询 的类比来解释它们的差异&#xff1a; 一、核心差异对比 维度n-gram 模型Transformer工作方式固定窗口的"近视观察员"全局关联的"侦探"依赖距离只能看前…

【Rust自学】14.5. cargo工作空间(Workspace)

喜欢的话别忘了点赞、收藏加关注哦&#xff0c;对接下来的教程有兴趣的可以关注专栏。谢谢喵&#xff01;(&#xff65;ω&#xff65;) 14.4.1. 为什么需要cargo workspace 假如说我们构建了一个二进制crate&#xff0c;里面既有library又有库。随着项目规模不断增长&#…

[权限提升] Windows 提权 — 系统内核溢出漏洞提权

关注这个框架的其他相关笔记&#xff1a;[内网安全] 内网渗透 - 学习手册-CSDN博客 0x01&#xff1a;系统内核溢出漏洞提权介绍 注意&#xff1a;提权很容易让电脑蓝屏&#xff0c;所以如果是测试的话&#xff0c;提权前最好做好系统备份。 溢出漏洞就像是往杯子里装水 —— 如…