• 6、MyBatis的SQL映射(mapper)文件


    学习资源:动力节点《2020最新MyBatis教程【IDEA版】-MyBatis从入门到精通》



    MyBatis 的真正强大在于它的映射语句,也是它的魔力所在。由于它的异常强大,映射器的 XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。MyBatis 就是针对 SQL 构建的,并且比普通的方法做的更好。

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

    • cache:给定命名空间的缓存配置。
    • cache-ref:其他命名空间缓存配置的引用。
    • resultMap – 是最复杂也是最强大的元素,用来描述如何从数据库结果集中来加载对象。
    • parameterMap已废弃!老式风格的参数映射。
    • sql:可被其他语句引用的可重用语句块。详见:点击跳转
    • insert:映射插入语句
    • update:映射更新语句
    • delete:映射删除语句
    • select:映射查询语

    1、指定约束文件

    mybatis-3-mapper.dtd 是约束文件的名称,扩展名是 dtd。

    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    

    约束文件作用:限制和检查在当前文件中出现的标签,属性必须符合 MyBatis 的要求。


    2、Mapper标签

    <mapper> 标签是 SQL 映射文件的根标签,对应 dao 包下的一个接口,在这个标签内部使用 sql 对应实现 dao 接口中的方法即可。

    <mapper namespace="">
    	CRUD操作
    <mapper>
    

    namespace

    意为命名空间,是<mapper>标签的一个属性,用于绑定连接 SQL 映射文件和 dao 接口

    一个 dao 接口对应一个 SQL 映射文件,所以为了区分不同的 dao 接口,命名空间要求是唯一的,推荐使用 dao 接口的全限定名称(可以自定义)。

    <mapper namespace="com.chen.dao.StudentDao">
    	CRUD操作
    <mapper>
    

    3、CRUD

    MyBatis 默认不自动提交事务,所以 update、insert、delete 执行后需要手动 commit

    在 SQL 映射文件中,只需要使用对应的标签语句写出我们要实现的 dao 接口的方法,之后再借助 MyBatis的 dao 动态代理,即可在 Java 代码中执行 dao 接口的方法。

    CRUD 操作在 MyBatis 的帮助下,是很容易实现。但是 MyBatis 的 CRUD 语句也有一些复杂的部分:传参返回值


    3.1、select

    简单的 select 语句:

    <select id="selectStudents" resultType="com.bjpowernode.domain.Student">
        select id,name,email,age from student
    </select>
    

    Java 代码执行:

    // 查询所有学生信息
    // 返回值问题有些复杂
    List<Student> = dao.selectStudents();
    

    3.2、insert

    简单的 insert 语句:

    <insert id="insertStudent">
            insert into student values (#{id},#{name},#{email},#{age})
    </insert>
    

    Java 代码执行:

    // 插入一条学生信息
    // 返回值表示执行 insert 后,影响数据库数据的行数
    int rows = dao.insertStudent(new Student(1006, "lisa", "5981956@163.com", 24));
    

    3.3、update

    简单的 update 语句:

    <update id="updateStudent">
        update student set age = #{age} where id=#{id}
    </update>
    

    Java 代码执行:

    // 更新 id 是 1001 的学生年龄为 37
    // 返回值表示执行 update 后,影响数据库数据的行数
    int rows = dao.updateStudent(1001, 37);
    

    3.4、delete

    简单的 delete 语句:

    <delete id="deleteStudent">
        delete from student where id=#{studentId}
    </delete>
    

    Java 代码执行:

    // 删除 id 是 1002 的学生
    // 返回值表示执行 insert 后,影响数据库数据的行数
    int rows = dao.deleteStudent(1002);
    

    4、# 和 $

    4.1、#

    #:占位符,告诉 mybatis 使用实际的参数值代替,并使用 PrepareStatement 对象执行 sql 语句, #{…} 代替 sql 语句的“?”。 这样做更安全、更迅速,通常也是首选做法。

    mapper文件:

    <select id="selectStudentById" resultType="com.bjpowernode.domain.Student">
    	select id,name, email,age from student where id=#{studentId}
    <select> 	
    

    mapper 文件转为 MyBatis 的执行是:

    String sql="select id,name,email,age from student where id=?";
    PreparedStatement ps = conn.prepareStatement(sql);
    ps.setInt(1,1005);
    

    解释:

    where id=? 就是 where id=#{studentId}
           ps.setInt(1,1005) , 1005 会替换掉 #{studentId}


    4.2、$

    $:字符串替换, 告诉 mybatis 使用 ${} 包含的“字符串”替换所在位置,使用 Statement 把 sql 语句和 ${} 的内容连接起来。

    $ 主要用在替换表名,列名,不同列排序等操作。

    使用实例1:分别使用 id , email 列查询 Student

    1. 接口方法
    Student findById(int id);
    
    1. mapper文件
    <select id="findById" resultType="com.bjpowernode.domain.Student">
    	select * from student where id=${studentId}
    </select>
    
    1. mapper 文件转为 MyBatis 的执行是:
    String sql="select id,name, email,age from student where id"+"1001";
    Statement st = conn.statement(sql);
    

    使用实例2:通用方法,使用不同列作为查询条件

    1. 接口方法
    Student findByDiffField(@Param("col") String colunName,@Param("cval") Object value);
    
    1. mapper 文件:
    <select id="findByDiffField" resultType="com.bjpowernode.domain.Student">
    	select * from student where ${col} = #{cval}
    </select>
    

    4.3、对比

    • # 使用 ?在sql语句中做站位的, 使用 PreparedStatement 执行 sql,效率高
    • $ 不是占位符方式,而是字符串连接得方式,使用 Statement 对象执行 sql,效率低
    • # 能够避免 sql 注入,更安全;$ 有sql注入的风险,缺乏安全性
    • # 一般用来占位 sql 语句中 = 后面字段的值;$一般用来替换 sql 语句中的表名或者列名

    5、传参问题

    从 Java 代码中把接口的方法参数传递到 mapper.xml 文件,需要遵守的规范

    5.1、parameterType 属性

    parameterType 是 CRUD 标签语句的一个属性,它对应 dao 接口中方法参数的类型,对应方法参数的类型不同,parameterType 也有不同的值对应,一般赋值为方法参数类型的全限定名或别名。如:

    <select id="selectStudentById" parameterType="java.lang.Integer" resultTytpe="com.bjpowernode.domain.Student">
        select id, name, email, age from student where id = #{id}
    </select>
    

    MyBatis 默认支持的别名(MyBatis 内部定义):

    别名 映射类型
    _byte byte
    _long long
    _short short
    _int int
    _integer int
    _double double
    _float float
    _boolean boolean
    string String
    byte Byte
    long Long
    short Short
    int Integer
    integer Integer
    double Double
    float Float
    boolean Boolean
    date Date
    decimal BigDecimal
    bigdecimal BigDecimal
    map Map/HashMap

    但是对于大多数简单的使用场景,MyBatis 通过反射机制都能够发现接口方法的参数的类型,所以 parameterType 属性一般也不需要赋值。


    5.2、传递一个简单类型的参数

    Dao 接口中方法的参数是一个简单类型的参数(Java 基本类型和 String),那么在 mapper.xml 文件中要获取这个参数,只需要使用占位符 #{ 任意字符 }即可获取,括号中的内容和方法的参数名无关。如:

    接口方法:

    Student selectById(int id);
    

    mapper文件:

    mapper 文件:
    <select id="selectById" resultType="com.bjpowernode.domain.Student">
        <!-- #{studentId} , studentId 是自定义的变量名称,和方法参数名无关 -->
    	select id,name,email,age from student where id=#{studentId}
    </select>
    

    使用 #{} 之后, MyBatis 执行 sql 是使用 JDBC 中的 PreparedStatement 对象

    由 MyBatis 执行下面的代码:

    1. MyBatis 创建 ConnectionPreparedStatement 对象
    String sql="select id,name, email,age from student where id=?";
    PreparedStatement pst = conn.preparedStatement(sql);
    pst.setInt(1,1001);
    
    1. 执行 sql 封装为 resultType="com.bjpowernode.domain.Student" 这个对象 ResultSet rs = ps.executeQuery()
    Student student = null;
    while(rs.next()){
       // 从数据库取表的一行数据, 存到一个java对象属性中
       student = new Student();
       student.setId(rs.getInt("id));
       student.setName(rs.getString("name"));
       student.setEmail(rs.getString("email"));
       student.setAge(rs.getInt("age"));
    }
    
    return student;  // 给了dao方法调用的返回值
    

    所以说,MyBatis 是封装的 JDBC 操作。

    5.3、传递多个参数,使用 @Param

    当 Dao 接口方法有多个参数,则需要通过名称使用参数。 在方法的所有形参前面加入 @Param(“自定义参数名”) ,那么在 mapper 文件使用 #{自定义参数名},mapper.xml 文件就可以获取到这个参数。

    使用实例:

    1. 接口方法:
    public List<Student> selectStudentsByMulitParam(@Param("stuName"), String name, @Param("stuAge") Integer age);
    
    1. mapper文件:
    <select id="selectStudentByMulitParam" resultTtpe="com.bjpowernode.domain.Student">
        select * from student where name=#{stuName} or age=#{stuAge}
    </select>
    

    5.4、传递多个参数,使用对象

    使用 java 对象传递参数,java 的属性值就是 sql 需要的参数值,每一个属性就是一个参数,语法格式:#{ property,javaType=java 中数据类型名,jdbcType=数据类型名称 }

    image-20200830084647389

    因为 javaType、jdbcType 的类型 MyBatis 都可以检测出来,所以一般不需要设置。 简化后的格式为: #{对象的属性}

    可传递的的 Java 对象参数是灵活的,只需要 sql 映射语句的参数和对象的属性名一致即可。

    使用实例:

    1. 创建保存参数值的对象 QueryParam :
    public class QueryParam {
        private String queryName;
    	private int queryAge;
    	//set , get 方法
    }  
    
    1. 接口方法:
    List<Student> selectMultiObject(QueryParam queryParam);  
    
    1. mapper 文件:
     <select id="selectMultiObject" resultType="com.bjpowernode.domain.Student">
        select id,name,email,age from student
        where name=#{queryName,javaType=string,jdbcType=VARCHAR}
        or age =#{queryAge,javaType=int,jdbcType=INTEGER}
    </select>
    <!-- 简化写法 -->
    <select id="selectMultiObject" resultType="com.bjpowernode.domain.Student">
        select id,name,email,age from student where name=#{queryName} or age=#{queryAge}
    </select>
    

    5.5、传递多个参数,按位置

    接口方法的参数位置从 0 开始, 引用参数语法 #{ arg 位置 } , 第一个参数是#{arg0},第二个是#{arg1}。

    注意: mybatis3.3 版本和之前的版本使用 #{0} ,#{1} 方式, 从 mybatis3.4 开始使用 #{arg0} 方式。

    但是传递多个参数的话,还是推荐使用 @Param 或 使用对象传参。

    使用实例:

    1. 接口方法:
    List<Student> selectByNameAndAge(String name,int age);
    
    1. mapper 文件
    <select id="selectByNameAndAge" resultType="com.bjpowernode.domain.Student">
    	select id,name,email,age from student where name=#{arg0} or age =#{arg1}
    </select>
    

    5.6、传递多个参数,使用 Map

    Map 集合可以存储多个值,使用 Map 可以向 mapper 文件一次传入任意多个参数。

    Map 集合使用 String的 <key,Object> 类型的值存储参数。

    mapper 文件使用 #{key} 引用 Map 中的数据。

    使用实例:

    1. 定义一个 Map
    Map<String,Object> data = new HashMap<String,Object>();
    data.put(“myname”,”李力”);
    data.put(“myage”,20);
    
    1. 接口方法
    List<Student> selectMultiMap(Map<String,Object> map);
    
    1. mapper文件
    <select id="selectMultiMap" resultType="com.bjpowernode.domain.Student">
    	select id,name,email,age from student where name=#{myname} or age =#{myage}
    </select>
    

    不推荐使用 Map 传参的原因:

    • Map 的 key 定义具有随意性,key 的改变会引起占位符的改变
    • 方法的参数是一个 Map ,无法直接确定该方法的参数个数、参数的类型,可读性很差

    6、封装 MyBatis 的输出结果(查询结果)

    在 MyBatis 中执行了 sql 语句,都会有返回一个 Java 对象: select 返回一个复杂的 Java 对象, update、 insert、 delete 返回一个整型值。

    那么我们应该如何为 MyBatis 指定返回值类型呢?

    6.1、resultType

    reslutType 是 <select> 标签的一个属性,只有 <select> 标签具有这个属性,并且必须要为这个属性赋值。

    reslutType 是用于指定执行 sql 后得到的 ResultSet 转换为 Java 对象,这个 Java 对象的类型,指定类型使用全限定名类型的别名

    reslutType 对返回结果的处理方式

    1. MyBatis 执行 sql 语句,然后调用实体类的无参构造方法,创建一个该类的空对象
    2. MyBatis 把 ResultSet 的列值通过 setter 赋给对象的同名属性

    image-20200830105318224


    6.1.1、返回简单类型

    简单类型:Java 基本类型和 String。

    别名 映射类型
    _byte byte
    _long long
    _short short
    _int int
    _integer int
    _double double
    _float float
    _boolean boolean
    string String
    byte Byte
    long Long
    short Short
    int Integer
    integer Integer
    double Double
    float Float
    boolean Boolean
    date Date
    decimal BigDecimal
    bigdecimal BigDecimal
    map Map/HashMap
    1. 接口方法:
    int countStudent();  
    
    1. mapper文件
    <select id="countStudent" resultType="int">
    	select count(*) from student
    </select>
    
    1. 测试方法
    @Test
    public void testRetunInt(){
        int count = dao.countStudent();
        System.out.println("学生总人数: "+ count);
    }
    

    6.1.2、返回一个对象类型

    这里说的对象类型,通常是根据业务自定义的实体类,但也可以不是实体类(qaq)。

    MyBatis 框架的处理:使用类的无参构造器创建一个空对象,然后调用 setXXX() 方法给同名属性赋值,所以类中一定要创建 setter

    1. 接口方法
    Student selectById(int id);
    
    1. mapper 文件
    <select id="selectById" resultType="com.bjpowernode.domain.Student">
    	select id,name,email,age from student where id=#{studentId}
    </select>
    
    1. 测试方法
    @Test
    public void testSelectById(){
        Student studet = dao.selectById(1001);
        System.out.println(studet);
    }
    
    sql 语句查询的字段 java 对象方法
    id setId( rs.getInt(“id”) )
    name setName( rs.getString(“name”) )
    email setEmail( rs.getString(“email”) )
    age setAge( rs.getInt(“age”) )

    6.1.3、返回一个列表

    如果返回的是集合,那应该设置为集合中元素的类型,而不是集合本身。

    1. 接口方法
    List<Student> selectStudents();
    
    1. mapper文件
    <select id="selectStudents" reslutType="com.bjpowernode.domain.Student">
        select * from student
    </select>
    
    1. 测试方法
    @Test
    public void testSelectList(){
        List<Student> studets = dao.selectStudents();
    }
    

    6.1.4、返回一个 Map

    MyBatis 可以将查询的结果数据封装为一个 Map,字段名作为 key,字段值作为 value 。

    使用实例:

    1. 接口方法
    Map<Object,Object> selectReturnMap(int id);
    
    1. mapper文件
    <select id="selectReturnMap" resultType="java.util.HashMap">
    	select name,email from student where id = #{studentId}
    </select>
    
    1. 测试方法
    @Test
    public void testReturnMap(){
        Map<Object,Object> retMap = studentDao.selectReturnMap(1002);
        System.out.println("查询结果是 Map:"+retMap);
    }
    

    注意:Map 作为接口返回值, sql 语句的查询结果最多只能有一条记录,大于一条记录会产生错误。


    6.2、resultMap

    看官方文档吧,好复杂的样子~

    resultMap 可以自定义 sql 查询结果和 Java 对象属性的映射关系,可以更灵活的把列值赋值给指定属性。

    属性 描述
    id 当前命名空间中的一个唯一标识,用于标识一个结果映射。
    type 类的完全限定名, 或者一个类型别名(关于内置的类型别名,可以参考上面的表格)。
    autoMapping 如果设置这个属性,MyBatis 将会为本结果映射开启或者关闭自动映射。 这个属性会覆盖全局的属性 autoMappingBehavior。默认值:未设置(unset)。

    resultMap 使用场景:

    • 表字段名和 Java 对象属性名不一样
    • 一对多,多对一查询

    因为 MyBatis 在输出结果时,会将查询到的字段赋给同名的属性,但是由于数据库字段名和 Java 属性的命名规范是不相同的,极有可能出现字段名和属性名不一致的情况,这种情况下,如果不使用 resultMap 进行对应关系的重映射,可以查出数据但是无法输出数据。

    一个 mapper 文件中可以有多个 resultMap ,只需要赋予不同的 id 即可区分;同时 resultMap 是可以复用的,可同时被多个 <select> 引用。

    注意:resultMap和resultType不要一起用,二选一。


    6.2.1、字段名-属性名关系重映射

    使用实例:

    1. 实体类
    public class Student {
        //属性名和列名不一致
        private Integer sId;
        private String sName;
        private String sEmail;
        private Integer sAge;
        // set ,get , toString
    }
    
    1. 接口方法
    List<Student> selectUseResultMap();
    
    1. mapper文件
    <!--使用resultMap
    	resultMap也是 <select> 标签的一个属性
    	只需要赋给已定义的 <resultMap> 的id,即可引用新的映射关系
    -->
    <select id="selectUseResultMap" resultMap="studentMap">
        select id,name,email,age from student where name=#{queryName} or age=#{queryAge}
    </select>
    
    <!-- 创建 resultMap -->
    <resultMap id="studentMap" type="com.bjpowernode.domain.Student">
        <!-- 
    		新的,字段和属性的映射关系
    		column:字段名
    		property:属性名
    	-->
        <!-- 主键字段使用 id -->
        <id column="id" property="sId" />
        <!--非主键字段使用 result-->
        <result column="name" property="sName"/>
        <result column="email" property="sEmail" />
        <result column="age" property="sAge" />
    </resultMap>
    
    <resultMap id="xxx" type="xxx.xxx.xxx.xxx">
        ......
    </resultMap>
    

    6.2.2、多对一查询

    7、为自定义类型起别名

    MyBatis 内部默认支持一些别名,如 java.lang.Integer 的别名为 int,因此 resultType="java.lang.Integer" 就可以简写为resultType="int"

    使用别名的意义在于减少完全限定名的冗余,方便引用某个类。

    起过别名后,在主配置文件的其他任何需要用到全限定名称的位置,都可以使用该类的别名替换了。

    MyBatis 支持为自定义类型定义别名,可以使用以下几种方式:

    1. 为一个类设置一个自定义的别名
    <typeAliases>
        <!-- 
    		type:自定义类型的全限定名称
    		alias:别名(推荐使用短小、容易记忆的)
    	-->
        <typeAlias type="com.chen.pojo.Student" alias="student"/>
        <typeAlias type="com.chen.pojo.User" alias="vipUser"/>
    </typeAliases>
    
    1. 批量定义别名,扫描整个包下的类,别名为类名(首字母大写或小写都可以)
    <typeAliases>
        <package name="com.bjpowernode.domain"/>
        <package name="...其他包"/>
    </typeAliases>
    
    1. 在自定义类上使用注解 @Alias("自定义别名")
    @Alias("stu")
    public class Student {
        ......
    }
    

    推荐使用全限定名称


    8、解决字段名和属性名不一致

    推荐使用 resultMap

    8.1、使用 resultMap 进行重映射

    8.2、列别名

    resultMap 的处理原则是将同名的字段值赋给同名的属性,所以可以给字段起别名 $-->$ Java 对象的属性名。

    使用实例:

    1. 实体类
    public class Student {
        //属性名和列名不一致
        private Integer sId;
        private String sName;
        private String sEmail;
        private Integer sAge;
        // set ,get , toString
    }
    
    1. 接口方法
    List<Student> selectDiffColProperty();
    
    1. mapper文件
    <select id="selectDiffColProperty" resultType="">
    	select id as sId, name sName, email sEmail, age sAge from student
    </select>
    

    9、模糊查询

    在 SQL 中有如下这种模糊查询语句:

    -- 查询所有姓李的学生的信息
    select * from student where name like "李%"
    

    那么 MyBatis 是如何实现模糊查询的呢?

    MyBatis 模糊查询的实现有两种方式:

    • 在 Java 代码中给查询数据加上"%"
    • 在 mapper 文件 sql 语句的条件位置加上"%"

    推荐使用方式1


    9.1、Java 代码指定 like 的内容

    使用实例:

    1. 接口方法:
    List<Student> selectLikeFirst(String name);  
    
    1. mapper文件
    <select id="selectLike_1" resultType="com.bjpowernode.domain.Student">
        select id,name,email,age from student
        where name like #{studentName}
    </select>
    

    3.测试方法

    @Test
    public void testSelectLike_1(){
        
        String name="李%";
        List<Student> stuList = studentDao.selectLike_1(name);
        stuList.forEach( stu -> System.out.println(stu));
    }
    

    9.2、mapper 文件中拼接 like 的内容

    注意:"%" #{xxx} "%" ,前后双引号和占位符之间是有一个空格的。

    使用实例:

    1. mapper 文件
    <select id="selectLike_2" resultType="com.bjpowernode.domain.Student">
        select id,name,email,age from student
        where name like #{studentName} "%"
    </select>
    
    1. 测试方法
    @Test
    public void testSelectLike_2(){
        String name="李";
        List<Student> stuList = studentDao.selectLikeSecond(name);
        stuList.forEach( stu -> System.out.println(stu));
    }
    
  • 相关阅读:
    VUE Class动态绑定
    SPA 单页面
    pdf.js使用记录
    vue 采坑 pdfjsdist/es5/build/pdf.js
    搜索引擎搜索技巧
    Service Broker初创建
    SQL Server 2008 R2 SP1升级到SQL Server 2012后的注意事项
    怎么都无法解决的 "Cannot Generate SSPI Context"
    Service Broker 应用示例
    接口测试工具 Jmeter使用笔记(一:编写一个http请求)
  • 原文地址:https://www.cnblogs.com/sout-ch233/p/13608337.html
Copyright © 2020-2023  润新知