zoukankan      html  css  js  c++  java
  • 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));
    }
    
  • 相关阅读:
    数据结构-树与二叉树-思维导图
    The last packet successfully received from the server was 2,272 milliseconds ago. The last packet sent successfully to the server was 2,258 milliseconds ago.
    idea连接mysql报错Server returns invalid timezone. Go to 'Advanced' tab and set 'serverTimezone' property
    redis学习笔记
    AJAX校验注册用户名是否存在
    AJAX学习笔记
    JSON学习笔记
    JQuery基础知识学习笔记
    Filter、Listener学习笔记
    三层架构学习笔记
  • 原文地址:https://www.cnblogs.com/sout-ch233/p/13608337.html
Copyright © 2011-2022 走看看