Mybatis 的中高级特性使用

news2025/1/13 11:51:18

简介:

MyBatis 的真正强大在于它的语句映射,这是它的魔力所在。由于它的异常强大,映射器的 XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。MyBatis 致力于减少使用成本,让用户能更专注于 SQL 代码。

SQL 映射文件只有很少的几个顶级元素(按照应被定义的顺序列出):

关于每个顶级元素 具体属性的详解,请移步官网: mybatis – MyBatis 3 | XML 映射器

本文只是针对新手开发中常见的几个问题,如何处理进行讲解:

1、自定义映射resultMap

1.1 resultMap处理字段和属性的映射关系

若字段名和实体类中的属性名不一致,则可以通过resultMap设置自定义映射

官方介绍中说,resultMap属性是对 对外部 resultMap 的命名引用。结果映射是 MyBatis 最强大的特性,如果你对其理解透彻,许多复杂的映射问题都能迎刃而解。 resultType 和 resultMap 之间只能同时使用一个

resultMap 是Select标签中一个重要的属性之一:如下图

<select
  id="selectPerson"
  parameterType="int"
  parameterMap="deprecated"
  resultType="hashmap"
  resultMap="personResultMap"
  flushCache="false"
  useCache="true"
  timeout="10"
  fetchSize="256"
  statementType="PREPARED"
  resultSetType="FORWARD_ONLY">

之前你已经见过简单映射语句的示例,它们没有显式指定 resultMap。比如:

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

或者,我们用一个实例对象 来接收数据库的查询结果

<select id="selectUsers" resultType="User">
  select *
  from t_user
  where id = #{id}
</select>

但是,上述条件是 数据库 表的列名 与 实例对象的属性名是一致的情况。如果二者不一致呢?该如何去做?

我们面试的时候,经常会遇到有面试官会问,数据库表的字段名和 实体对象的属性名不一致,我们如何操作,

其实解决这个问题有很多办法。

比如 利用SQL语法里的 as

<select id="selectUsers" resultType="User">
  select
    user_id             as "id",
    user_name           as "userName",
    user_password       as "password"
  from some_table
  where id = #{id}
</select>

我们还可以显示的配置一个外部resultMap  这也是解决列名不匹配的另外一种方式。

<!--
resultMap:设置自定义映射
属性:
id:表示自定义映射的唯一标识
type:查询的数据要映射的实体类的类型
子标签:
id:设置主键的映射关系
result:设置普通字段的映射关系
association:设置多对一的映射关系
collection:设置一对多的映射关系
属性:
property:设置映射关系中实体类中的属性名
column:设置映射关系中表中的字段名
-->

    <resultMap id="userMap" type="user">
        <id property="id" column="id"></id>
        <result property="userName" column="username"></result>
        <result property="passWord" column="password"></result>
        <result property="age" column="age"></result>
        <result property="gender" column="gender"></result>
    </resultMap>

    <select id="getMohu" resultMap="userMap"  >
        <!-- select * from t_user where gender like '%${keyword}%'-->
        <!--select * from t_user where username like concat('%',#{keyword},'%')-->
        select * from t_user where email like "%"#{keyword}"%"
    </select>

若字段名和实体类中的属性名不一致,但是字段名符合数据库的规则(使用_),实体类中的属性 名符合Java的规则(使用驼峰)

此时也可通过以下两种方式处理字段名和实体类中的属性的映射关系:

1、可以通过为字段起别名的方式,保证和实体类中的属性名保持一致

2、可以在MyBatis的核心配置文件中设置一个全局配置信息mapUnderscoreToCamelCase,可 以在查询表中数据时,自动将_类型的字段名转换为驼峰

例如:字段名user_name,设置了mapUnderscoreToCamelCase,此时字段名就会转换为 userName

1.2、多对一映射处理

场景模拟: 查询员工信息以及员工所对应的部门信息

1.2.1 级联方式处理映射信息

我们先看一下 用原生Sql语句实现的效果:

SELECT emp.* ,dept.* FROM t_emp emp LEFT JOIN t_dept dept ON emp.`dept_id`=dept.`dept_id` WHERE emp.`emp_id`=1;

 

代码实现:


public class Emp{

    private Integer eid;
    private String ename;
    private Integer age;
    private String gender;
    private Dept dept;

    @Override
    public String toString() {
        return "Emp{" +
                "eid=" + eid +
                ", ename='" + ename + '\'' +
                ", age=" + age +
                ", dept=" + dept +
                ", gender='" + gender + '\'' +
                '}';
    }



    public Integer getEid() {
        return eid;
    }

    public void setEid(Integer eid) {
        this.eid = eid;
    }

    public String getEname() {
        return ename;
    }

    public void setEname(String ename) {
        this.ename = ename;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }



}


public class Dept {
    private Integer did;
    private String dname;

    public String getDname() {
        return dname;
    }

    public void setDname(String dname) {
        this.dname = dname;
    }

    public Integer getDid() {
        return did;
    }

    public void setDid(Integer did) {
        this.did = did;
    }

    @Override
    public String toString() {
        return "dept{" +
                "did=" + did +
                ", dname='" + dname + '\'' +
                '}';
    }
}

接口添加方法:

  Emp getEmpAndDeptByEid(@Param("id")int id);

    <resultMap id="empDeptMap" type="Emp">
        <id column="emp_id" property="eid"></id>
        <result column="emp_name" property="ename"></result>
        <result column="age" property="age"></result>
        <result column="gender" property="gender"></result>
        <result column="dept_id" property="dept.did"></result>
        <result column="dept_name" property="dept.dname"></result>

    </resultMap>

<!--    getEmpAndDeptByEid-->

    <select id="getEmpAndDeptByEid" resultMap="empDeptMap">
        SELECT emp.* ,dept.* FROM t_emp emp LEFT JOIN t_dept dept ON emp.`dept_id`=dept.`dept_id` WHERE emp.`emp_id`=#{id}
    </select>

测试输出:

输出:

 Emp{eid=1, ename='小黑', age=20, dept=dept{did=1, dname='A'}, gender='女'}

1.2.2、使用association处理映射关系

<resultMap id="empDeptMap" type="Emp">
        <id column="emp_id" property="eid"></id>
        <result column="emp_name" property="ename"></result>
        <result column="age" property="age"></result>
        <result column="gender" property="gender"></result>
      <association property="dept" javaType="Dept">
          <id column="dept_id" property="did"></id>
          <result column="dept_name" property="dname"></result>
      </association>
    </resultMap>

1.2.3 分步查询

建立两个实体 Emp 和 Dept 分别代表 员工和 部门。

建立对应的mapper接口。

创建对应的接口映射文件

此处我作为简化,把分步查询放在了同一个接口 和映射文件中 (EmpMapper.java 和 EmpMapper.xml)

①查询员工信息

public interface EmpMapper {

    /**
     * 通过分步查询查询员工信息
     *     分步查询:查询员工以及所在部门信息的第一步
     * @param eid
     * @return
     */
    Emp getEmpByStep1(@Param("eid") int eid);

    /**
     * 分步查询的第二步: 根据员工所对应的dept_id 查询部门信息
     * @param did
     * @return
     */
    Dept getEmpDeptByStep2(@Param("did") int did);


}
<resultMap id="empDeptStepMap" type="Emp">
    <id column="emp_id" property="eid"></id>
    <result column="emp_name" property="ename"></result>
    <result column="age" property="age"></result>
    <result column="gender" property="gender"></result>
    <!--
    select:设置分步查询,查询某个属性的值的sql的标识(namespace.sqlId)
    column:将sql以及查询结果中的某个字段设置为分步查询的条件
    -->
        <association property="dept" fetchType="eager"
                     select="com.alex.mybatis.mapper.EmpMapper.getEmpDeptByStep2" column="dept_id" ></association>
    </resultMap>

    <!--Emp getEmpByStep1(@Param("eid") int eid);-->
    <select id="getEmpByStep1" resultMap="empDeptStepMap">
        select * from t_emp where emp_id = #{eid}
    </select>

    <!--Dept getEmpDeptByStep2(@Param("did") int did);-->
    <resultMap id="deptMap" type="Dept">
        <id column="dept_id" property="did"></id>
        <result column="dept_name" property="dname"></result>
    </resultMap>
    <select id="getEmpDeptByStep2" resultMap="deptMap" >
        select * from t_dept where dept_id = #{did}
    </select>

可以这样理解:

t_emp和t_dept 是外键包含关系,传统意义上来说,我们查询的时候,要进行外连接。

我们现在如果想得到员工的个人信息,以及所在部门的个人信息(以实体对象的形式获得查询结果),可以分步进行。

1)我们根据 eid 获取 从 t_emp 表中查询到的 员工数据 执行 step1方法,构建  员工实体对象 emp

2) 由于Emp实体对象里,包含一个属性 Dept 类型的 dept对象, 而表数据,只能给到我们 具体的部门编号did。( did 是Dept对象的属性之一)

3)所以,我们要拿着这个did 去部门表t_dept 里查询部门信息。此时 就利用了 association 标签里的 select 去执行 step2方法 (column =dept_id 填写的是 分步查询条件的数据库字段名,property 填写的是 Emp对象 当前要被分步查询的属性名)

4)Emp实例对象构建完成,返回最终执行结果

 

 Emp{eid=1, ename='小黑', age=20, dept=Dept{did=1, dname='A'}, gender='女'}

注意事项: 一定要 注意,对象属性名是否与表字段名一一对应,否则很有可能会报nullPoint 或查不到数据,

1.3 一对多映射处理

修改 Dept 如下



import java.util.List;

public class Dept {
    private Integer did;
    private String dname;

    private List<Emp> emps;  //多对一映射   一个部门可以有多个员工

    @Override
    public String toString() {
        return "Dept{" +
                "did=" + did +
                ", dname='" + dname + '\'' +
                ", emps=" + emps +
                '}';
    }

    public List<Emp> getEmps() {
        return emps;
    }

    public void setEmps(List<Emp> emps) {
        this.emps = emps;
    }



    public String getDname() {
        return dname;
    }

    public void setDname(String dname) {
        this.dname = dname;
    }

    public Integer getDid() {
        return did;
    }

    public void setDid(Integer did) {
        this.did = did;
    }

}

1.3.1、collection

public interface DeptMapper {

    /**
     *  根据部门id查新部门以及部门中的员工信息
     * * @param did
     * * @return
     */

    Dept getDeptEmpById(@Param("did") int did);
}
<?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.alex.mybatis.mapper.DeptMapper">


    <resultMap id="DeptMap" type="Dept">
        <id property="did" column="dept_id"></id>
        <result property="dname" column="dept_name"></result>
        <!--
        ofType:设置collection标签所处理的集合属性中存储数据的类型
        -->
        <collection property="emps" ofType="Emp">
            <id property="eid" column="emp_id"></id>
            <result property="ename" column="emp_name"></result>
            <result property="age" column="age"></result>
            <result property="gender" column="gender"></result>
<!--            <result property="dept.did" column="dept_id"></result>-->
<!--            <result property="dept.dname" column="dept_name"></result>-->
        </collection>
    </resultMap>

    <select id="getDeptEmpById" resultMap="DeptMap">
        select dept.*,emp.* from t_dept dept left join t_emp emp on dept.dept_id = emp.dept_id where dept.dept_id =#{did}
    </select>


</mapper>

输出结果


Dept{did=1, dname='A', emps=[Emp{eid=1, ename='小黑', age=20, dept=null, gender='女'}, Emp{eid=4, ename='赵六', age=26, dept=null, gender='男'}]}

1.3.2 分步查询

我们捋一捋思路:

现在 两个关系中,谁是多,谁是1?

根据部门编号,查询该部门全部员工,显然 部门是1,员工是多。

而且我们在Dept类中也表现出来了:

这种分步查询,其实效果类似子查询,更具体的说是一种相关子查询。

t_dept 的数据要代入到 t_emp中查询结果,所以 t_emp是被驱动表,t_dept 是驱动表。 小表驱动大表

 那么,我们分步查询的步骤就是这样:

1)先查询t_dept 的 部门信息

2)再将dept_id的值带入 t_emp 查询员工信息。

DeptMapper 接口

/**
     * 分步查询1: 根据did 查询部门信息
     */

    Dept getDeptEmpStep1(@Param("did") int did);

EmpMapper 接口

 /**
     * 
     * 通过分步查询 部门的具体信息(该部门的所有信息包括员工信息)
     */
    Emp getDeptEmpStep2(@Param("did") int did);

DeptMapper.xml

!--    分步查询 查询部门全部信息-->
    <resultMap id="DeptEmpMap" type="Dept">
        <id property="did" column="dept_id"></id>
        <result property="dname" column="dept_name"></result>

        <collection property="emps" fetchType="eager"
                    select="com.alex.mybatis.mapper.EmpMapper.getDeptEmpStep2" column="dept_id"></collection>
    </resultMap>
    <select id="getDeptEmpStep1" resultMap="DeptEmpMap">
        select * from t_dept where dept_id =#{did}
    </select>

EmpMapper.xml

 <!-- 2分步查询 查询部门全部信息-->
    <resultMap id="empDeptMap" type="Emp">
        <id column="emp_id" property="eid"></id>
        <result column="emp_name" property="ename"></result>
        <result column="age" property="age"></result>
        <result column="gender" property="gender"></result>
        <association property="dept" javaType="Dept">
            <id column="dept_id" property="did"></id>
            <result column="dept_name" property="dname"></result>
        </association>
    </resultMap>
    <select id="getDeptEmpStep2" resultMap="empDeptMap">
        select * from t_emp where dept_id =#{did}
    </select>

测试输出:

 Dept{did=1, dname='A',

emps=[Emp{eid=1, ename='小黑', age=20, dept=Dept{did=1, dname='null', emps=null}, gender='女'},

            Emp{eid=4, ename='赵六', age=26, dept=Dept{did=1, dname='null', emps=null}, gender='男'}]}

代码仓库:https://github.com/Chai-Feng/git-demo01.git

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

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

相关文章

小学生编程一些log

家里孩子也逐渐开始学习编程了&#xff0c;这里是一些经验积累&#xff0c;log下&#xff0c;希望能节省一些大家的探索时间&#xff1b; 方块编程 开始起手是一些scratch类的编程&#xff0c;使用方块类的&#xff0c;网课就可以&#xff0c;我这里尝试的是核桃编程&#xff…

蓝桥杯2020省赛python

蓝桥杯2020省赛Python 第一题&#xff1a;门牌制作 用了一个时间复杂度是n的算法&#xff0c;反正是填空题也不会出现TLE的事&#xff0c;只要别太离谱就行了。 利用python 的count函数 res 0 for i in range(1,2021):i str(i)res i.count(2) print(res)答案是2 第二题&…

【目标检测论文解读复现NO.26】基于改进YOLOv5s网络的实时输液监测

前言 此前出了目标改进算法专栏&#xff0c;但是对于应用于什么场景&#xff0c;需要什么改进方法对应与自己的应用场景有效果&#xff0c;并且多少改进点能发什么水平的文章&#xff0c;为解决大家的困惑&#xff0c;此系列文章旨在给大家解读最新目标检测算法论文&#xff0c…

115、【回溯算法】leetcode ——216.组合总和III:回溯法+剪枝优化(C++版本)

题目描述 原题链接&#xff1a;216.组合总和III 解题思路 整体回溯法思路类似于 77. 组合&#xff08;回溯法剪枝优化&#xff09;&#xff0c;与之不同的是&#xff0c;需要多一个相加和为n的判定条件&#xff0c;可以让每次传入数的时候进行n - i&#xff0c;直至找到n 0时…

CAPBase理论

一、CAP 理论CAP 理论指出对于一个分布式计算系统来说&#xff0c;不可能同时满足以下三点&#xff1a;一致性&#xff1a;在分布式环境中&#xff0c;一致性是指数据在多个副本之间是否能够保持一致的特性&#xff0c;等同于所有节点访问同一份最新的数据副本。在一致性的需求…

英飞凌TC3xx系列介绍01-GTM系统介绍

本文框架1. 本系列前言2. GTM模块系统介绍2.1 GTM模块常用缩略语3. 系列介绍规划1. 本系列前言 英飞凌TC3xx是英飞凌AURIX 2G系列单片机&#xff0c;该系列单片机是32位微控制器&#xff0c;具有多个TriCore CPU、程序及数据存储器、总线、中断系统、DMA及功能强大的外围设备。…

linux基本功系列之su命令

文章目录一. su命令介绍二. 语法格式及常用选项三. 参考案例3.1 切换到root用户3.2 切换到指定用户3.3 不切换成root&#xff0c;但执行root才能执行的命令总结前言&#x1f680;&#x1f680;&#x1f680; 想要学好Linux&#xff0c;命令是基本功&#xff0c;企业中常用的命令…

【MySQL】第十部分 常用的聚合函数

【MySQL】第十部分 常用的聚合函数 文章目录【MySQL】第十部分 常用的聚合函数10. 常用的聚合函数10.1 MIN和MAX函数10.2 COUNT函数10.3 AVG和SUM函数10.4 GROUP BY10.5 HAVING总结10. 常用的聚合函数 定义: 对一组数据进行汇总的函数&#xff0c;输入的是一组数据的集合&…

英语学习打卡day5

2023.1.25 1.aqua n.水;溶液;浅绿色 The construction of underground aqua storage tank 地下水储罐的建设 2.do sth for dear life 拼命做某事 If you do something for dear life, you do it with as much effort as possible, usually to avoid danger. 3. 4.swoop …

vue-grid-layout数据可视化图表面板优化过程所遇问题汇总

对于drag事件不熟悉的&#xff0c;请先阅读&#xff1a;《drag事件详解&#xff1a;html5鼠标拖动排序及resize实现方案分析及实践》之前老项目grafana面板&#xff0c;如下图所示&#xff08;GEM添加图表是直接到图表编辑&#xff0c;编辑完成后自动插入到面板最后&#xff09…

Flume集群安装部署、Kafka集群安装部署以及Maxwell安装部署实战

1、Flume集群安装部署 1.1、安装地址 Flume官网地址&#xff1a;http://flume.apache.org/文档查看地址&#xff1a;http://flume.apache.org/FlumeUserGuide.html下载地址&#xff1a;http://archive.apache.org/dist/flume/ 1.2、安装部署 将apache-flume-1.9.0-bin.tar.…

Redis实现笔记点赞排行榜 | 黑马点评-达人探店

一、发布探店笔记 探店笔记类似点评网站的评价&#xff0c;往往是图文结合。对应的表有两个 探店笔记表&#xff08;主键、商户id、用户id、标题、文字、图片、探店文字描述、点赞数量、评论数量&#xff09;评价表&#xff08;笔记的评价&#xff09;先上传图片请求一次保存…

【Java基础】--Java排序

【Java基础】--Java排序1、选择排序(Select Sort)2、冒泡排序(Bubble Sort)3、插入排序(Insert Sort)4、希尔排序(Shell Sort)5、归并排序(Merge Sort)6、快速排序(Quick Sort)7、堆排序(Heap Sort)小结待排序的元素需要实现 Java 的 Comparable 接口&#xff0c;该接口有 com…

Facebook SEO中参与度的重要性

参与度&#xff0c;也就是大家所说的浏览量&#xff0c;在 Facebook SEO中也叫做页面访问者参与度。一般来说&#xff0c; Facebook的用户在上面停留的时间越长代表着它在用户心目中的形象越好&#xff0c;这也是为什么 Facebook上的访客愿意打开自己的 Facebook页面让好友帮忙…

使用vite构建vue3项目详细介绍(ts+pinia+sass+vue-router+axios+element-plus)

使用vite构建vue3项目详细介绍(tspiniasassvue-routeraxioselement-plus) 1. 创建项目 npm init vitelatest 2. 配置 vite.config.ts path需要安装--npm install types/node --save-dev import vue from vitejs/plugin-vue; import { resolve } from path; import { defineC…

Jupyter的安装与默认目录的切换

下载与安装 清华大学开源软件镜像站 使用国内镜像下载更快&#xff0c;官网下载很慢 下载msi镜像文件&#xff0c;打开安装&#xff1a; 安装完成后得到4个文件 Reset Spyder...和Anaconda Powershell....都是相应的配置&#xff0c;其中后者是Jupyter和anaconda的dos命令窗口…

Coolify系列02-从0到1超详细手把手教你上手Coolify

重启 如果由于某种原因&#xff0c;你的实例崩溃了&#xff0c;你可以用下面的命令重新启动它: wget -q https://get.coollabs.io/coolify/install.sh \ -O install.sh; sudo bash ./install.sh -r防火墙设置 您需要在防火墙中允许以下端口 Coolify: 3000 (required)Revers…

【Mysql】Mysql的存储引擎

【Mysql】Mysql的存储引擎 文章目录【Mysql】Mysql的存储引擎1.概述2. 特点2.1 InnoDB2.2 MyISAM2.3 Memory2.4 区别3. 选择1.概述 **存储引擎&#xff1a;存储引擎就是存储数据、建立索引、更新/查询数据等技术的实现方式。**存储引擎是基于表的&#xff0c;而不是 基于库的&…

HTTPS工作原理详解加密(TLS握手)过程

HTTPS概念 HTTPS就是一个有安全保障的HTTP通信&#xff0c;我们都知道&#xff0c;http是明文传输的&#xff0c;http报文是人肉眼就可识别的ASCII码&#xff0c;在通信过程中&#xff0c;http报文很容易被黑客窃听、篡改、伪造&#xff0c;而在互联网交易中&#xff0c;我们必…

【1】初识Linux

学习笔记目录 学习教程&#xff1a;B站 “黑马程序员” 初识Linux--入门Linux基础命令--会用Linux权限管控--懂权限Linux实用操作--熟练实战软件部署--深入掌握脚本&自动化--用的更强项目实战--学到经验云平台技术--紧跟潮流 操作系统概述 1.计算机由硬件和软件两个主要…