映射文件指导着MyBatis如何进行数据库增删改查,有着非常重要的意义

  1. cache —— 命名空间的二级缓存设置
  2. cache-ref —— 其他命名空间缓存配置的引用。
  3. resultMap —— 自定义结果集映射。
  4. parameterMap —— 已废弃!老式风格的参数映射
  5. sql —— 抽取可重用的语句块
  6. insert —— 映射插入语句
  7. update —— 映射更新语句
  8. delete —— 映射删除语句
  9. select —— 映射查询语句

https://mybatis.org/mybatis-3/zh/sqlmap-xml.html#

insert、update、delete元素

参数 意义
id 命名空间中的唯一标识符
parameterType 要将传入语句的参数的完全限定类名或别名。这个属性是可选的,因为MyBatis可以通过TypeHandler推断出具体传入语句的参数类型,默认值为unset
flushCache 将其设置为true,任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空,默认值:true(对应插入、更新和删除语句)。
timeout 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的描述。默认值为unset(依赖驱动)
statementType STATEMENT,PREPARED或CALLABLE的一个。这会让MyBatis分别使用Statement,PreparedStatement或CallableStatement,默认值:PREPARED.
useGeneratedKeys (仅对insert和update有用)这会令MyBatis使用JDBC的getGeneratedKeys方法来去除由数据库内部生成的主键(比如:像MySQL和SQL Server这样的关系数据库管理系统的自动递增字段),默认值为:false
keyProperty (仅对insert和update有用)唯一标记一个属性。MyBatis会通过getGeneratedKeys的返回值或者通过insert语句的selectKey子元素设置他的主键,默认unset
keyColumn (仅对insert和update有用)通过生成的键值设置表中的列名,这个设置尽在某些数据库(像PostgreSQL)是必须的,当主键列不是表中的第一列的时候需要设置。如果希望得到多个生成的列。也可以是逗号分隔的属性名称列表。
databaseId 如果配置了databaseIdPrvoider,MyBatis会加载所有的不带databaseId或匹配当前databaseId的语句;如果带或者不带的语句都有,则不带的会被忽略。

主键生成方法

  1. 若数据库支持自动生成主键的字段(比如MySQL和SQL Server),则可以设置useGeneratedKeys="true",然后再把keyProperty设置到目标属性属性上。

    1. <insert id="insertCustomer" databaseId="mysql" userGeneratedKeys="true" keyProperty="id">
    2. insert into coutomers2 (last_name, email, age) values (#{lastName}, #{email}, #{age})
    3. </insert>
  2. 而对于不支持自增主键的数据库(例如Oracle),则可以使用selectKey子元素:selectKey元素将会首先运行,id会被设置,然后插入语句会被调用。

    <insert id="insertCustomer" databaseId="oracle" parameterType="customer">
     <selectKey order="BEFORE" keyProperty="id" resultType="_int">
         select crm_seq.nextval from dual
     </selectKey>
     insert into customer2 (id, last_name, email, age) values (#{id}, #{lastName}, #{email}, #{age})
    </insert>
    

selectKey

属性 意义
keyProperty selectKey语句结果应该被设置的目标属性
keyColumn 匹配属性的返回结果集中的列名称
resultType 结果的类型,MyBatis通常可以推算出来,但是为了更加确定写上也不会有什么问题。MyBatis允许任何简单类型用作主键的类型,包括字符串。
order 可以被设置为BEFORE或AFTER。如果设置为BEFORE,name他会首先选择主键,设置keyProperty然后执行插入语句。如果设置为AFTER,name限制性插入语句,然以是selectKey元素
statementType 与前面相同,MyBatis支持STATEMENT,PREPARED和CALLABLE语句的映射类型,分别代表PreparedStatement和CallableStatement类型

参数(Parameters)传递

单个参数

可以接受基本类型,对象类型,集合类型的值。这种情况下MyBatis可以直接使用这个参数,不需要经过任何处理。

{参数名};取出参数值

多个参数

任意多个参数,都会被MyBatis重新包装成一个Map传入。Map的key是param1,param2,0,1.。。,值就是参数的值。

多个参数会被封装成一个map,    
    key:param1...paramN,或者参数的索引也可以    
    value:传入的参数值
#{}就是从map中获取指定的key的值;

异常:    
    org.apache.ibatis.binding.BindingException:    
    Parameter 'id' not found.    
    Available parameters are [arg1, arg0, param1, param2]
操作:    
    方法:public Employee getEmpByIdAndLastName(Integer id, String lastName);    
    取值:#{id}, #{lastName}

命名参数

为参数使用@Param起一个名字,MyBatis就会将这些参数封装进map中,key就是我们自己指定的名字。

明确指定封装参数时,map的key;@Param("id")
多个参数会被封装成一个map,
    key:使用@Param注解指定的值
    value:参数值
#{指定的key}取出对应的参数值

POJO

当这些参数属于我们业务POJO时,我们直接传递POJO。

{属性名}:取出传入pojo的属性值

Map

我们也可以封装多个参数为map,直接传递

{key}:取出map中对应的值

参数处理

参数也可以制定一个特殊的数据类型

{ prperty, javaType = int, jdbcType = NUMERIC }

{ height, javaType = double, jdbcType = NUMBERIC, numericScale = 2 }

  1. javaType 通常可以从参数对象中去确定。
  2. 如果null被当做值来传递,对于所有可能为空的列,jdbcType需要被设置。
  3. 对于数值类型,还可以设置小数点后保留的位数。
  4. mode属性允许IN,OUT或INOUT参数。如果参数为OUT或INOUT,参数对象属性的正式值就会被改变,就像在获取输出参数时所期望的那样。

参数位置支持的属性

javaType、jdbcType、mode、numericScale、resultMap、typeHandler、jdbcTypeName

实际上通常被设置的是

可能为空的列名制定jdbcType

#{key}

获取参数的值,预编译到SQL中。安全。

${key}

获取参数的值,并接到SQL中,有SQL注入问题。ORDER BY ${name}

select元素

  1. Select元素来定义查询操作。
  2. Id:唯一标识符
    1. 用来引用这条语句,需要和接口的方法名一致
  3. parameterType:参数类型。
    1. 可以不传,MyBatis会根据TypeHandler自动推断
  4. resultType:返回值类型。
    1. 别名或者全类名,如果返回的是结合,定义集合中元素的类型。不能喝resultMap同时使用。| | 参数 | 意义 | | —- | —- | | parameterType | 将会传入这条语句的参数类完全限定名或别名。这个属性是可选的,因为MyBatis可以提供过TypeHandler推断出具体传入语句的参数,默认值为unset | | resultType | 从这条语句中返回的期望类型的类的完全限定名或别名。注意如果是集合,那应该是是集合可以包含的类型,而不能是集合本身。改属性和resultMap,不能同时使用。 | | resultMap | 外部resultMap的命名引用。和resultType属性不能同时使用。 | | flushCache | 将其设置为true,任何时候只要被调用,都会导致本地缓存和二级缓存,默认值:false | | useCache | 将其设置为true,将会导致本条语句的结果被二级缓存,默认值:对select元素为true | | timeout | 这个设置是在抛出异常之前,驱动程序等待数据库返回结果的秒数。默认值为unset(依赖驱动) | | fetchSize | 影响驱动程序每次批量返回的结果行数,默认值为unset(依赖驱动)。 | | statementType | STATEMENT,PREPARED或CALLABLE的一个。这会让MyBatis分别使用Statement,PreparedStatement或CallableStatement,默认值:PREPARED, | | resultSetType | FOREWARD_ONLY,SCROLL_SENSITIVE或SCROLL_INSENSITIVE中的一个,默认值为unset(依赖驱动) | | databaseId | 如果配置了databaseIdProvider,MyBatis会加载所有的不带databaseId或匹配当前databaseId的语句;如果带或者不带语句都有,则不带的会被忽略。 | | resultOrdered | 这个设置仅对嵌套结果select语句适用:如果为true,就假设包含了嵌套结果集或是分组,这样当返回一个主结果行,就不会发生有对前面结果集引用的情况。这就是的在获取嵌套的结果集的时候不至于导致内存不够用。默认值为:false | | resultSets | 这个设置仅对结果集的情况适用,他讲列出语句执行后返回的结果集并每隔结果集给一个名称,名称是逗号分割的。 |

自动映射

  1. 全局setting设置
    1. autoMappingBehavior默认是PARTIAL,开启自动映射的功能。唯一的要求是列名和javaBean属性名一致。
    2. 如果autoMappingBehavior设置为null则会取消自动映射。
    3. 数据库字段命名规范,POJO属性符合驼峰命名法,如A_COLUMN —> AColumn,我们可以开启自动驼峰命名规则映射功能,mapUnderscoreToCamelCase = true
  2. 自定义resultMap,实现高级结果集映射。

reseultMap

  1. constructor
    1. 类在实例化时,用来注入结果到构造方法中
    2. idArg —— ID 参数:标记结果作为ID可以帮助提高整体效能。
  2. id —— 一个ID结果;标记结果作为ID可以帮助提高整体效能。
  3. result —— 注入到字段或JavaBean属性的普通结果
  4. association —— 一个复杂的类型关联;许多结果将包成这种类型
    1. 嵌入结果映射——结果映射自身的关联,或者参考一个
  5. collection —— 复杂类型的集
    1. 嵌入结果映射 —— 结果映射自身的集,或者参考一个
  6. discriminator —— 使用结果值来决定使用哪个结果映射。
    1. case——基于某些值的结果映射
      • 嵌入结果映射——这种情形结果也映射它本身,因此可以包含很多相同的元素,或者他可以参照一个外部的结果映射。

id & result

id和result映射一个单独列的值到简单数据类型(字符串、整形、双精度浮点数、日期等)的属性或字段。

元素 参数
property 映射到列结果的字段或属性。例如:”username” 或 “address.street.number”。
column 数据表的列名。通常和resultSet.getString(columnName)的返回值一致。
javaType 一个Java类的完全限定名,或一个类型别名。如果映射到一个JavaBean,MyBatis通常可以断定类型
jdbcType JDBC类型是仅仅需要对插入,更新和删除操作可能为空的列进行处理。
typeHandler 类型处理器,使用这个属性,可以覆盖默认的类型处理器。这个属性值是类的完全限定类名或者是一个类型处理器的实现,或者是类型别名

association

  1. 复杂对象映射
  2. POJO中的属性可能会是一个对象
  3. 我们可以使用联合查询。并以吉利啊属性的方式封装对象。

    <resultMap type="com.zh.bean.lock" id="myLock">
     <id column="id" property="id"/>
     <result column="lockName" property="lockName"/>
     <result column="key_id" property="key.id"/>
     <result column="keyName" property="key.keyName"/>
    </resultMap>
    
  4. 使用association标签定义对象的封装规则。

association-嵌套结果集

<resultMap type="com.zh.bean.Lock" id="myLock2">
    <id column="id" property="id"/>
    <result column="lockName" property="lockName"/>
    <association property="key" javaType="com.zh.bean.Key">
        <id column="key_id" property="id"/>
        <result column="keyName" property="keyName"/>
    </association>
</resultMap>

association-分段查询

<resultMap type="com.zh.bean.Lock" id="myLock3">
    <id column="id" property="id"/>
    <result column="lockName" property="lockName"/>
    <association property="key" select="com.zh.dao.KeyMapper.getKeyById" column="key_id">        </association>
</resultMap>
  1. select:调用目标的方法查询当前属性的值
  2. column:将指定列的值传入目标方法

association-分段查询&延迟加载

开启延迟加载和属性按需加载

<settings>
    <setting name="lazyLoadingEnabled" value="true"/>
    <setting name="aggressiveLazyLoading" value="false"/>
</settings>

旧版本的MyBatis需要额外的支持包。

  1. asm-3.3.1.jar
  2. cglib-2.2.2.jar

Collection-集合类型&嵌套结果集

<select id="getDeptById" resultMap="MyDept">
    select d.id d_id, d.dept_name d_deptName, e.id e_id, e.last_name e_lastName, e.email email, e.gender e_gender, e.dept_id e_deptId from department d left join employee e on e.'dept_id' = d.'id' where d.'id' = #{id}
</select>
<resultMap type="com.zh.bean.Department" id="MyDept">
    <id column="d_id" property="id"/>
    <result column="d_department" property="deptName"/>
    <collection property="emps" ofType="com.zh.bean.Employee" columnPrefix="e_">
        <id column="id" property="id"/>
        <result column="lastName" property="lastName"/>
        <result column="email" property="email"/>
        <result column="gender" property="gender"/>
    </collection>
</resultMap>

Collection-分布查询&延迟加载

<resultMap type="com.zh.bean.Department" id="MyDeptStep">
    <id cloumn="id" property="id"/>
    <result column="dept_name" property="deptName"/>
    <collection property="emps" select="com.zh.dao.Employee.getEmpsByDeptId" column="id">
        </collection>
</resultMap>

扩展-多列值封装map传递

  1. 分布查询的时候通过column指定,将对应的列的数据传递过去,我们有时需要传递多列数据。
  2. 使用{key1=column1, key2=column2...}的形式

    <resultMap type="com.zh.bean.Department" id="MyDeptStep">
     <id column="id" property="id"/>
     <result column="dept_name" property="deptName"/>
     <collection property="emps" select="com.zh.dao.EmployeeMapper.getEmpsByDeptId" column="{deptId=id}">
     </collection>
    </resultMap>
    
  3. association或者collect标签的fetchType=eager/lazy可以覆盖全局的延迟加载策略。指定立即加载(eager)或者延迟加载(lazy)

参数取值(#{} & ${})

#{}:可以获取map中的值或者pojo对象属性的值;
${}:可以获取map中的值或者pojo对象属性的值。

select * from tbl_employee where id=${id} and last_name=#{lastName}
Preparing: select * tbl_employee where id=2 and last_name=?
    区别:
        #{}:它是以预编译的形式,将参数设置到sql语句中;PreparedStatement;防止sql注入
        ${}:取出的值直接拼装在sql语句中;会有安全问题
        大多情况下,我们取参数的值都应该取使用#{};

        原生jdbc不支持占位符的地方就可以使用${}进行取值
        比如分表、排序:按照年份分表拆分
            select * from ${year}_salary where xxx;
            select * from tbl_employee order by ${f_name} ${order}

#{}:更丰富的用法:
    可以规定参数的规则:
    javaType、jdbcType、mode(存储过程)、numericScale(数字精度)、resultMap、typeHandler、jdbcTypeName、expression(未来准备支持的)

    jdbcType通常需要在某种特定的条件下被设置:
        在我们数据为null的时候,有些数据库可能不能识别mybatis对null的默认处理。比如Oracle(报错);
        JdbcType OTHER:无效的类型;因为mybatis对所有的null都映射的是原生Jdbc的OTHER类型,oracle不能正确处理。

        由于全局配置中:jdbcTypeForNull=OTHER;oracle不支持;两种办法都行
        1. #{email,jdbc=NULL};
        2. jdbcTypeForNull=NULL

鉴别器

<discriminator javaType=""></discriminator>
鉴别器:myBatis可以使用discriminator判断某列的值,然后根据某列的值改变封装行为
封装employee:
    如果查出的是女生,就把部门信息查询出来,否则不查询
    如果是男生,把last_name这一列的值,赋值给email;
<resultMap id="MyEmpDis" type="com.zh.mybatis.bean.Employee">
    <id column="id" property="id"/>
    <result column="last_name" property="lastName"/>
    <result column="email" property="email"/>
    <result column="gender" property="gender"/>

    <!--
        column:指定判断的列名
        javaType:列值对应的java类型
    -->
    <discriminator javaType="string" column="gender">
        <!--女生 resultType:指定封装的结果类型:不能缺少。resultMap和resultType二选其一-->
        <case value="0" resultType="com.zh.mybatis.bean.Employee">
            <association property="department"
                         select="com.zh.mybatis.dao.DepartmentMapper.getDeptById"
                         column="d_id">
            </association>
        </case>
        <!--男生 -->
        <case value="1" resultType="com.zh.mybatis.bean.Employee">
            <id column="id" property="id"/>
            <result column="last_name" property="lastName"/>
            <result column="last_name" property="email"/>
            <result column="gender" property="gender"/>
        </case>
    </discriminator>

</resultMap>

分步查询

<!--
    使用association进行分步查询
    1. 先按照员工id查出员工信息
    2. 根据查询员工信息中的d_id值去部门表查出部门信息
    3. 部门设置到员工中;
-->
<resultMap id="MyEmpByStep" type="com.zh.mybatis.bean.Employee">
    <id column="id" property="id"/>
    <result column="last_Name" property="lastName"/>
    <result column="email" property="email"/>
    <result column="gender" property="gender"/>
    <!--
        association定义相关对象的封装规则
        select :表明当前属性是调用select指定的方法查出的结果
        column:指定将哪一列的值传递给这个方法

        流程:使用select 指定的方法(传入column指定的这列参数的值)查出对象,并封装给property指定的属性
    -->
    <association property="department"
                 select="com.zh.mybatis.dao.DepartmentMapper.getDeptById"
                 column="d_id">

    </association>
</resultMap>