zoukankan      html  css  js  c++  java
  • 深入理解MyBatis(二)--Mapper

    GitHub:https://github.com/JDawnF

    一、Mapper接口的工作原理

    Mapper 接口,对应的关系如下:

    • 接口的全限名,就是映射文件中的 "namespace" 的值。

    • 接口的方法名,就是映射文件中 MappedStatement 的 "id" 值。

    • 接口方法内的参数,就是传递给 SQL 的参数。

    Mapper 接口是没有实现类的,当调用接口方法时,接口全限名 + 方法名拼接字符串作为 key 值,可唯一定位一个对应的 MappedStatement 。举例:com.mybatis3.mappers.StudentDao.findStudentById ,可以唯一找到 namespace 为 com.mybatis3.mappers.StudentDao 下面 id为 findStudentById 的 MappedStatement 。

    总结来说,在 Mybatis 中,每一个 <select /><insert /><update /><delete /> 标签,都会被解析为一个 MappedStatement 对象。

    Mapper 接口的实现类,通过 MyBatis 使用 JDK Proxy 自动生成其代理对象 Proxy ,而代理对象 Proxy 会拦截接口方法,从而“调用”对应的 MappedStatement 方法,最终执行 SQL ,返回执行结果。整体流程如下图:

    其中,SqlSession 在调用 Executor 之前,会获得对应的 MappedStatement 方法。例如:DefaultSqlSession#select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) 方法,代码如下:

     // DefaultSqlSession.java
     @Override
     public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
         try {
             // 获得 MappedStatement 对象
             MappedStatement ms = configuration.getMappedStatement(statement);
             // 执行查询
             executor.query(ms, wrapCollection(parameter), rowBounds, handler);
         } catch (Exception e) {
             throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
         } finally {
             ErrorContext.instance().reset();
         }
     }

    Mapper 接口里的方法,是不能重载的,因为是全限名 + 方法名的保存和寻找策略。

    二、Mapper接口绑定的实现方式

    接口绑定有三种实现方式:

    第一种,通过 XML Mapper 里面写 SQL 来绑定。在这种情况下,要指定 XML 映射文件里面的 "namespace" 必须为接口的全路径名。

    如:<mapper namespace="com.taotao.mapper.TbContentCategoryMapper" >

    第二种,通过注解绑定,就是在接口的方法上面加上 @Select@Update@Insert@Delete 注解,里面包含 SQL 语句来绑定。

    第三种,是第二种的特例,也是通过注解绑定,在接口的方法上面加上 @SelectProvider@UpdateProvider@InsertProvider@DeleteProvider 注解,通过 Java 代码,生成对应的动态 SQL 。

    获取自动生成的(主)键值

    不同的数据库,获取自动生成的(主)键值的方式是不同的。

    MySQL 有两种方式,但是自增主键,代码如下:

     <!--方式一,使用 useGeneratedKeys + keyProperty 属性-->
     <insert id="insert" parameterType="Person" useGeneratedKeys="true" keyProperty="id">
         INSERT INTO person(name, pswd)
         VALUE (#{name}, #{pswd})
     </insert>
         
     <!--方式二,使用 <selectKey /> 标签,返回主键-->
     <insert id="insert" parameterType="Person" useGeneratedKeys="true" keyProperty="id">
         <selectKey keyProperty="id" resultType="long" order="AFTER">
             SELECT LAST_INSERT_ID()
         </selectKey>
         INSERT INTO person(name, pswd)
         VALUE (#{name}, #{pswd})
     </insert>

    SELECT LAST_INSERT_ID()也可以换成select @@identity

    • resultType:指出获取的主键的类型。

    • keyProperty:指出主键在 Java 类中对应的属性名。此处会将获取的主键值直接封装到被插入的实体类对象中。

    • order:指出 id 的生成相对于 insert 语句的执行是在前还是在后。MySql 数据库表 中的 id,均是先执行 insert 语句,而后生成 id,所以需要设置为 AFTER;Oracle 数据库表中 的 id,则是在 insert 执行之前先生成,所以需要设置为 BEFORE。当前的 MyBatis 版本,不指 定 order 属性,则会根据所用 DBMS,自动选择其值。

    无论插入操作是提交还是回滚,DB 均会为 insert 的记录分配 id,即使发生回滚,这个 id 也已经被使用。后面再插入并提交的记录数据,此 id 已经不能再使用,被分配的 id 是跳 过此 id 后的 id。

    另外,从前面<selectKey/>中 order 属性值的设置讲解可知,MySql 在 insert 语句执行后 会自动生成该新插入记录的主键值。主键值的生成只与 insert 语句是否执行有关,而与最终 是否提交无关。

    Oracle 有两种方式,序列触发器。基于序列,根据 <selectKey /> 执行的时机,也有两种方式,代码如下:

    <!--这个是创建表的自增序列
    CREATE SEQUENCE student_sequence
    INCREMENT BY 1
    NOMAXVALUE
    NOCYCLE
    CACHE 10;
    -->
    <!--方式一,使用 `<selectKey />` 标签 + BEFORE-->
    <insert id="add" parameterType="Student">
      <selectKey keyProperty="student_id" resultType="int" order="BEFORE">
          select student_sequence.nextval FROM dual
        </selectKey>
        
         INSERT INTO student(student_id, student_name, student_age)
         VALUES (#{student_id},#{student_name},#{student_age})
    </insert>
    
    <!--方式二,使用 `<selectKey />` 标签 + AFTER-->
    <insert id="save" parameterType="com.threeti.to.ZoneTO" >
        <selectKey resultType="java.lang.Long" keyProperty="id" order="AFTER" >
          SELECT SEQ_ZONE.CURRVAL AS id FROM dual
        </selectKey>
        
        INSERT INTO TBL_ZONE (ID, NAME ) 
        VALUES (SEQ_ZONE.NEXTVAL, #{name,jdbcType=VARCHAR})
    </insert>
    

    三、在 Mapper 中如何传递多个参数

    第一种,直接使用 Map 集合封装,装载多个参数进行传递。代码如下:

    // 调用方法
    Map<String, Object> map = new HashMap();
    map.put("start", start);
    map.put("end", end);
    return studentMapper.selectStudents(map);
    
    // Mapper 接口
    List<Student> selectStudents(Map<String, Object> map);
    
    // Mapper XML 代码
    <select id="selectStudents" parameterType="Map" resultType="Student">
        SELECT * 
        FROM students 
        LIMIT #{start}, #{end}
    </select>
    

    第二种,保持传递多个参数,使用 @Param 注解。(推荐使用)代码如下:

     // 调用方法
     return studentMapper.selectStudents(0, 10);
     ​
     // Mapper 接口
     List<Student> selectStudents(@Param("start") Integer start, @Param("end") Integer end);
     ​
     // Mapper XML 代码
     <select id="selectStudents" resultType="Student">
         SELECT * 
         FROM students 
         LIMIT #{start}, #{end}
     </select>

    第三种,保持传递多个参数,不使用 @Param 注解。代码如下:

     // 调用方法
     return studentMapper.selectStudents(0, 10);
     ​
     // Mapper 接口
     List<Student> selectStudents(Integer start, Integer end);
     ​
     // Mapper XML 代码
     <select id="selectStudents" resultType="Student">
         SELECT * 
         FROM students 
         LIMIT #{param1}, #{param2}
     </select>

    其中,按照参数在方法方法中的位置,从 1 开始,逐个为 #{param1}#{param2}#{param3} 不断向下排列,即通过#{param1}对应Mapper接口方法中的参数位置。

  • 相关阅读:
    之所以菜鸟依旧
    单点登陆
    让entityframework.extend库同时支持mysql,sqlsever
    背包算法
    JS中实现继承
    Altium Designer 生成 Mach3 G代码的程序
    test博客嵌入pbi
    testPBI报表
    html中隐藏title属性方法
    Spring mvc 中有关 Shiro 1.2.3 配置问题
  • 原文地址:https://www.cnblogs.com/baichendongyang/p/13235437.html
Copyright © 2011-2022 走看看