美文网首页
Mybatis映射文件

Mybatis映射文件

作者: 紫雨杰 | 来源:发表于2018-07-31 22:22 被阅读0次

    mybatis二级缓存在的问题?

    mybatis的二级缓存是基于namespace级别的。
    
    由于关联查询,导致不同命名空间下的缓存不能及时更新的情况。
    
    mybatis给出的建议是:如果涉及到关联查询,尽量不要使用它自带的二级缓存。
    

    1、Mybatis 映射文件之增删改:

    (1)、Mybatis 允许增删改直接定义以下类型的返回值:
    
    Integer、Long、Boolean、void (或其基本数据类型)
    
    (2)、Mybatis 默认为手动提交(sqlSession.commit();)
    
    *   sqlSessionFactory.openSession();选择无参的,则需要手动提交
    *   sqlSessionFactory.openSession(boolean autoCommit); 选择有参的,
    
    如果填入true,则会自动提交,默认为false,即手动提交
    
    (3)、Mybatis 映射文件中的 parameterType 可以省略
    
    (4)、Mybatis模糊查询的方法:
    
      * 不要在Java代码中添加"%",影响效率
    
    ①、方式一:使用CONCAT()函数进行连接
    
        <!--PC端分页查询活动报名人员列表-->
        <select id="listApplicationPage" resultType="com.zhengtoon.ghtoon.activity.entity.Application" >
            select
            <include refid="applicationColumns" />
            from t_application
            <where>
                <if test="actId != null and actId != ''">
                   AND act_id = #{actId}
                </if>
                <if test="userName != null and userName != ''">
                    AND user_name LIKE CONCAT('%', #{userName}, '%')
               或者
                    AND user_name LIKE  "%"#{userName}"%"
                </if>
                <if test="telephone != null and telephone != ''">
                    AND telephone LIKE CONCAT('%', #{telephone}, '%')
               或者
                    AND telephone LIKE  "%"#{telephone}"%"
                </if>
            </where>
            ORDER BY create_time DESC
        </select>
    

    2、Mybatis 映射文件之 Insert 获取自增主键的值:

    (1)、原生的JDBC 采用 statement对象的getGeneratedKeys()方法可以获取到
    
    (2)、mysql 支持自增主键,自增主键值的获取,Mybatis 也是利用 statement的 getGeneratedKeys()方法来获取
    
    *   需要在 insert 标签中配置 useGeneratedKeys="true",表示使用自增主键获取主键值策略,默认值为FALSE
    *   还需要在 insert 标签中配置 keyProperty属性,指定对应的主键属性,也就是 Mybatis 获取到主键值以后,
        将这个值封装给 javaBean的哪个属性
    
    例:
    
    keyProperty="id" , 即将这个值封装给javaBean的 id 属性
    
    <insert id="addEmp" parameterType="com.zlj.mybatis.bean.Employee" useGeneratedKeys="true" 
     keyProperty="id">
        insert into tbl_employee(last_name,email, gender) values(#{lastName}, #{email}, #{gender} )
    </insert>
    

    3、Mybatis 映射文件之 Insert 获取非自增主键的值:

    (1)、Oracle 不支持自增,Oracle 使用序列来模拟自增,
    
    每次插入的数据的主键是从序列中拿到的值,如何获取到这个值?
    
    ------ 先从selectKey 标签中查询出序列的下一个值封装到JavaBean的id属性中,
    
    然后再把查询出来的id 值插入到数据表中
    
    例:
    
    <insert id="addEmp" parameterType="com.zlj.mybatis.bean.Employee" databaseId="oracle">
    
    /*
    
    ①、keyProperty:查出的主键值封装给 JavaBean 的哪个属性
    
    ②、order属性,取值有两种 :BEFORE 和 AFTER
    
            BEFORE :当前 sql 在插入 sql 之前执行
    
            AFTER :当前 sql 在插入 sql 之后执行
    
    ③、resultType : 查出的数据的返回值类型
    
    ④、BEFORE 运行顺序:
    
        *   先运行 selectKey 查询 id 的sql, 查出 id值封装给 JavaBean的id 属性
        *   再运行插入的sql,就可以取出id 属性对应的值
    
    ⑤、AFTER 运行顺序:
    
        *   先运行插入的 sql (从序列中取出新值作为 id);
        *   运行 selectKey 查询 id 的 sql ,然后封装到JavaBean的id属性中
    
    ⑥、因为 AFTER 有些问题,如果一次插入好几条数据,那么使用selectKey 查询出的 id,为最后插入sql 的最后一个序列值, 
       所以推荐使用 BEFORE
    */
    
    /*
    BEFORE :
    */
    
    <selectKey keyProperty="id" order="BEFORE" resultType="Integer">
            /* 编写查询主键的 sql 语句 */
            select EMPLOYEE_SEQ.nextval from dual
    </selectKey>
    
    /* 插入时的主键是从序列中拿到的,即id为上面查出来后进行封装后的值 */
    insert into tbl_employee(id, last_name,email, gender)
    values(#{id}, #{lastName}, #{email}, #{gender} )
    
    /*
    AFTER:
    */
    
    <!--
    
    <selectKey keyProperty="id" order="AFTER" resultType="Integer">
            /* 再从系列中查找出当前系列值,封装到JavaBean对象的id属性中 */
            select EMPLOYEE_SEQ.currval from dual
    </selectKey>
    
    <!-- 先执行插入,插入时插入的 id 值直接是从系列中取出下一个序列的值插入 -->
    insert into tbl_employee(id, last_name,email, gender)
    values(EMPLOYEE_SEQ.nextval, #{lastName}, #{email}, #{gender} )
    
    -->
    </insert>
    

    4、Mybatis映射文件参数处理(单个参数/多个参数/命名参数):

    (1)、单个参数:Mybatis 不会做任何特殊处理
    
            #{参数名}:取出参数值, #{}括号里面的参数名可以随意取值
    
    (2)、多个参数:Mybatis 会做特殊处理
    
    ①、多个参数会被封装成一个 map,其中:
    
            key : param1 ... paramN 或者参数的索引也可以,从0开始
            value : 传入的参数值
    
    ②、#{}就是从 map 中获取指定的key 的值
    
            即 #{param1} and #{param2} 或者 #{0} and #{1}
    
    操作:
    
    *   方法:Employee getEmpByIdAndLastName(Integer id, String lastName);
    *   取值:#{id} and #{lastName}
    *   抛异常:
    
    org.apache.ibatis.binding.BindingException: Parameter 'id' not found. Available parameters are
     [0, 1, param1, param2]
    
    *   正确取值方式:
    
    #{param1} and #{param2} 或者 #{0} and #{1}
    
    (3)、命名参数:多个参数的情况下,可以明确指定封装参数时map 的key,多个参数会被封装成一个map
    
    例如:Employee getEmpByIdAndLastName(@Param("id") Integer id, @Param("lastName") String lastName);
    
    key:@Param 注解指定的值
    
    value : 参数值
    
    #{指定的key} 取出对应的参数值,即可以使用id = #{id} and last_name=#{lastName}
    
    <!--
    <select id="getEmpByIdAndLastName" resultType="emp">
            select * from tbl_employee where id = #{0} and last_name=#{1}
    </select>
    
    <select id="getEmpByIdAndLastName" resultType="emp">
            select * from tbl_employee where id = #{param1} and last_name=#{param2}
    </select>
    -->
    
    //使用 @Param() 注解后
    
    <select id="getEmpByIdAndLastName" resultType="emp">
            select * from tbl_employee where id = #{id} and last_name=#{lastName}
    </select>
    

    5、Mybatis 参数处理(POJO、Map、TO)

    (1)、POJO:如果多个参数正好是业务逻辑的数据模型,可以直接传入 POJO
    
            #{属性名} : 取出传入的 POJO 的属性值
    
    (2)、Map: 如果多个参数不是业务模型中的数据,没有对应的POJO,不经常使用,为了方便,也可以传入Map
    
            #{key}: 取出 map 中对应的值
    
    (3)、TO:如果多个参数不是业务模型中的数据,但是经常要使用,推荐来编写一个To(Transfer Object)数据传输对象
    

    6、Mybatis 参数处理 -> 参数封装扩展思考:

    (1)、public Employee getEmp(@Param("id") Integer id, String lastName);
    
            取值: id ==> #{id / param1 / 0} lastName ==> #{param2 / 1}
    
    (2)、public Employee getEmp(Integer id, @Param("e")Employee emp);
    
            取值:id ==> #{param1} lastName ==> #{param2.lastName / e.lastName}
    
    (3)、特别注意,如果是 Collection (List、Set) 类型或者是数组,Mybatis也会进行特殊处理。
                 也是把传入的list或者数组封装在map中
    
            key : Collection (collection) , 如果是 List 还可以使用List封装的key(list)
    
            数组(array)
    
            public Employee getEmpById(List<Integer> ids);
    
            取值:取出第一个id的值:#{list[0]} 或者 #{collection[0]}
    

    7、Mybatis 参数处理 -> # 与 $ 取值的区别:

    (1)、#{} :可以获取 map 中的值或者 POJO 对象属性的值
         ${} : 可以获取 map 中的值或者 POJO 对象属性的值
    
    (2)、区别:
    
            #{} : 是以预编译的形式,将参数设置到sql语句中,占位符,就像原生JDBC中的PreparedStatement, 
                   可以防止sql注入问题
    
            ${} : 取出的值直接拼装在sql语句中,会有安全问题;
    
                例如: select * from tbl_employee where id=${id} and last_name=#{lastName}
    
                      Preparing : select * from tbl_employee where id=1 and last_name = ?
    
    (3)、大多情况下,我们取参数的值都应该去使用 #{}
    
    (4)、原生JDBC 不支持占位符的地方就可以使用${}进行取值
    
    例如 : 分表、排序.....
    
    按照年份分表拆分
    
    select * from ${year}_salary where xxx;
    
    select * from tbl_employee order by ${last_name} ${order} 等
    
    8、Mybatis 参数处理 -> # 取值时指定参数相关规则:
    
    (1)、规定参数的一些规则:
    
    javaType、jdbcType、mode(存储过程)、numericScale、 resultMap、typeHandler、jdbcTypeName、
    
    expression(未来准备支持的功能)
    
    (2)、jdbcType 通常需要在某种特定的条件下被设置:
    
    在执行插入操作,当数据为 null 的时候,有些数据库可能不能识别 Mybatis 对null的默认处理,比如 Oracle(报错)
    
    jdbcType OTHER : 无效的类型,因为 Mybatis 对所有的null都映射的是原生的 jdbc 的 OTHER 类型,
    
    Oracle不能正确处理!
    
    (3)、由于全局配置中,jdbcTypeForNull 默认值为 OTHER, Oracle 不支持,有两种解决办法:
    
    (1)、#{email,jdbcType=NULL}
    
    (2)、在全局配置文件中配置jdbcTypeForNull = NULL
    
    <setting name="jdbcTypeForNull" value="NULL"></setting>
    
    9、Mybatis 映射文件 - select 返回 List:
    
    resultType: 如果返回的是一个集合,要写集合中元素的类型
    
    10、Mybatis映射文件 - select 返回 Map:
    
    (1)、返回一条记录的 map,key 就是列名,值就是对应的值,resultType="map"
    
    (2)、返回多条记录封装一个map:Map<Integer, Employee> 键是这条记录的主键,值时记录封装后的JavaBean对象
    
    *   @MapKey("") 注解,标注在方法上,告诉Mybatis 封装这个map 的时候使用哪个属性作为map的key
    
    比如: @MapKey("id"),就是使用id 作为map 的key
    
    *   resultType 为 map中元素的类型
    
    11、Mybatis映射文件 - select - resultMap 自定义结果映射规则:
    
    (1)、resultMap: 自定义结果集映射规则,resultMap和resultType 只能选择一个
    
    (2)、自定义结果集映射规则
    
    *   自定义某个 JavaBean 的封装规则
    
    type : 自定义规则的java类型, 可以使用设置的别名,也可以使用全类名
    
    id : 唯一id , 方便后面引用
    
    <resultMap id="myEmp" type="com.zlj.mybatis.bean.Employee">
    
    <!--
    
    指定主键列的封装规则
    
    id 标签专门用来定义主键列,底层会有一定的优化
    
    column :指定哪一列
    
    property: 指定对应的 JavaBean 属性
    
    -->
    
    <id column="id" property="id"></id>
    
    <!-- result 标签定义普通列封装规则 -->
    
    <result column="last_name" property="lastName"></result>
    
    <!-- 不指定的列会自动封装,我们只要写 resultMap 就把全部的映射规则都写上 -->
    
    <result column="email" property="email"></result>
    
    <result column="gender" property="gender"></result>
    
    </resultMap>
    
    <!-- resultMap: 自定义结果集映射规则 -->
    
    <select id="getEmpById" resultMap="myEmp">
    
    select * from tbl_employee where id = #{id}
    
    </select>
    
    12、Mybatis 映射文件 - select - resultMap 关联查询_级联属性封装结果:
    
    例:
    
    <!-- 联合查询:级联属性封装结果集 -->
    
    <resultMap id="myDifEmp" type="com.zlj.mybatis.bean.Employee">
    
    <id column="id" property="id"></id>
    
    <result column="last_name" property="lastName"></result>
    
    <result column="gender" property="gender"></result>
    
    <result column="email" property="email"></result>
    
    <result column="did" property="dept.id"></result>
    
    <result column="dept_name" property="dept.departmentName"></result>
    
    </resultMap>
    
    <select id="getEmpAndDept" resultMap="myDifEmp">
    
    SELECT e.id id, e.last_name last_name, e.gender gender, e.email email, e.d_id d_id,d.id did,
    
    d.dept_name dept_name FROM tbl_employee e,tbl_dept d WHERE e.d_id = d.id AND e.id = #{id}
    
    </select>
    
    13、Mybatis 映射文件 - select - resultMap 关联查询_association定义关联对象封装规则:
    
    例:
    
    <!-- 使用 association 定义关联的单个对象的封装规则 -->
    
    <resultMap id="MyDifEmp2" type="com.zlj.mybatis.bean.Employee">
    
    <id column="id" property="id"></id>
    
    <result column="last_name" property="lastName"></result>
    
    <result column="gender" property="gender"></result>
    
    <result column="email" property="email"></result>
    
    <!--
    
    association 可以指定联合的 JavaBean 对象
    
    property=“dept” : 指定哪个属性是联合的对象
    
    javaType : 指定这个属性对象的类型[不能省略]
    
    -->
    
    <association property="dept" javaType="com.zlj.mybatis.bean.Department">
    
    //注意:column 中不能有值重复的,否则会出错,但是property 中可以,因为属于不用的java类
    
    <id column="did" property="id"></id>
    
    <result column="dept_name" property="departmentName"></result>
    
    </association>
    
    </resultMap>
    
    14、Mybatis 映射文件 - select - resultMap 关联查询_association分步查询:
    
    例:
    
    <!--
    
    使用 association 进行分步查询:
    
    1、先按照员工的 id 查询员工的信息;
    
    2、根据查询员工的信息中的 d_id 值去部门表查出部门信息
    
    3、部门信息设置到员工信息中;
    
    -->
    
    <resultMap id="myEmpStep" type="com.zlj.mybatis.bean.Employee">
    
    <id column="id" property="id"></id>
    
    <result column="last_name" property="lastName"></result>
    
    <result column="gender" property="gender"></result>
    
    <result column="email" property="email"></result>
    
    <!--
    
    association: 定义关联对象的封装规则
    
    select :表明当前属性是调用 select 指定的方法查出的结果
    
    column : 指定将哪一列的值传给这个方法
    
    流程:使用select 指定的方法(传入column指定的这列参数的值)查出对象,并封装给property 指定的属性
    
    -->
    
    <association property="dept" javaType="com.zlj.mybatis.bean.Department"
    
    select="com.zlj.mybatis.bean.dao.DepartmentMapper.getDeptById" column="d_id"></association>
    
    </resultMap>
    
    <select id="getEmpByIdStep" resultMap="myEmpStep">
    
    select * from tbl_employee where id = #{id}
    
    </select>
    
    15、Mybatis 映射文件 - select - resultMap 关联查询_association分步查询 & 延迟加载:
    
    (1)、在分步查询的基础上,可以使用延迟加载(懒加载 或 按需加载):
    
    Employee ==> Dept
    
    每次查询Employee 对象的时候,都将Dept一起查询出来,想让部门信息在使用的时候再去查询。
    
    解决办法:在分段查询的基础之上在全局配置文件中加两个配置即可。
    
    <!-- 开启延迟加载 -->
    
    <!-- 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。默认false -->
    
    <setting name="lazyLoadingEnabled" value="true"></setting>
    
    <!-- 当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载,默认false -->
    
    <setting name="aggressiveLazyLoading" value="false"></setting>
    
    16、Mybatis 映射文件 - select - resultMap 关联查询_collection定义关联集合封装规则:
    
    例:
    
    <!-- collection 嵌套结果集的方式,定义关联的集合类型元素的封装规则 -->
    
    <resultMap id="myDept" type="com.zlj.mybatis.bean.Department">
    
    <id column="did" property="id"></id>
    
    <result column="dept_name" property="departmentName"></result>
    
    <!--
    
    collection :定义关联集合类型的属性的封装规则
    
    ofType :指定集合里面元素的类型
    
    -->
    
    <collection property="emps" ofType="com.zlj.mybatis.bean.Employee">
    
    <!-- 定义这个集合中元素的封装规则 -->
    
    <id column="eid" property="id"></id>
    
    <result column="last_name" property="lastName"></result>
    
    <result column="email" property="email"></result>
    
    <result column="gender" property="gender"></result>
    
    </collection>
    
    </resultMap>
    
    <select id="getDeptAndEmpsById" resultMap="myDept">
    
    SELECT d.id did, d.dept_name dept_name, e.id eid, e.last_name last_name, e.email email,
    
    e.gender gender,e.d_id d_id
    
    FROM tbl_dept d LEFT JOIN tbl_employee e ON e.d_id = d.id WHERE d.id = 1
    
    </select>
    
    16、Mybatis 映射文件 - select - resultMap 关联查询_collection分步查询 & 延迟加载:
    
    (与 association 的分步查询和延迟加载一样 )
    
    <!--
    
    resultMap - 关联查询 - collection 分步查询 & 延迟加载
    
    -->
    
    <resultMap id="myDeptStep" type="com.zlj.mybatis.bean.Department">
    
    <id column="id" property="id"></id>
    
    <result column="dept_name" property="departmentName"></result>
    
    <collection property="emps" select="com.zlj.mybatis.bean.dao.EmployeeMapper2.getEmpsByIdStep"
    
    column="id">
    
    </collection>
    
    </resultMap>
    
    <select id="getDeptByStep" resultMap="myDeptStep">
    
    select id, dept_name from tbl_dept where id = #{id}
    
    </select>
    
    <select id="getEmpsByIdStep" resultType="com.zlj.mybatis.bean.Employee">
    
    select * from tbl_employee where d_id = #{deptId}
    
    </select>
    
    17、Mybatis 映射文件 - select - resultMap 分步查询传递多列值 & fetchType:
    
    (1)、resultMap - 分步查询传递多列值 & fetchType
    
    扩展:将多列的值封装 map 传递 column = "{key1 = column1, key2 = column2}"
    
    (2)、fetchType = "lazy" : 表示使用延迟加载
    
    取值有:lazy - 延迟加载
    
    eager - 立即加载
    
    <resultMap id="myDeptStep" type="com.zlj.mybatis.bean.Department">
    
    <id column="id" property="id"></id>
    
    <result column="dept_name" property="departmentName"></result>
    
    <collection property="emps" select="com.zlj.mybatis.bean.dao.EmployeeMapper2.getEmpsByIdStep"
    
    column="{deptId = id}"  fetchType="eager"></collection>
    
    </resultMap>
    
    18、Mybatis 映射文件 - select - resultMap - discriminator 鉴别器:
    
    (1)、<discriminator javaType=""></discriminator>
    
    鉴别器:Mybatis 可以使用 discriminator 判断某列的值,然后根据某列的值改变封装行为
    
    例:
    
    <!--
    
    例:封装 Employee
    
    如果查出的是女生,就把部门信息查询出来,否则不查询
    
    如果查出的是男生,把last_name 这一列的值赋值给email
    
    -->
    
    <resultMap id="MyEmpDis" type="com.zlj.mybatis.bean.Employee">
    
    <id column="id" property="id"></id>
    
    <result column="last_name" property="lastName"></result>
    
    <result column="gender" property="gender"></result>
    
    <result column="email" property="email"></result>
    
    <!--
    
    column : 指定判断的列名
    
    javaType : 列值对应的java 类型,可以使用别名,比如:String 的别名 Mybatis自动封装为 string
    
    -->
    
    <discriminator javaType="string" column="gender">
    
    <!-- value="0" 表示女生,resultType / resultMap : 指定封装的结果类型,不能缺少 -->
    
    <case value="0" resultType="com.zlj.mybatis.bean.Employee">
    
    <association property="dept" javaType="com.zlj.mybatis.bean.Department"
    
    select="com.zlj.mybatis.bean.dao.DepartmentMapper.getDeptById" column="d_id">
    
    </association>
    
    </case>
    
    <!-- value="1" 表示男生 -->
    
    <case value="1" resultType="com.zlj.mybatis.bean.Employee">
    
    <id column="id" property="id"></id>
    
    <result column="last_name" property="lastName"></result>
    
    <result column="gender" property="gender"></result>
    
    <result column="last_name" property="email"></result>
    
    </case>
    
    </discriminator>
    
    </resultMap>
    
    五、Mybatis 动态 sql
    
    1、Mybatis 动态SQL - if 判断 & OGNL:
    
    (1)、动态SQL是Mybatis 强大特性之一,极大的简化了我们拼装SQL的操作。
    
    (2) 、Mybatis 采用功能强大的基于 OGNL 的表达式来简化操作。
    
    *   if
    *   choose(when, otherwise)
    *   trim(where, set)
    *   foreach
    
    (3)、OGNL(Object Graph Navigation Language) 对象图导航语言,这是一种强大的表达式语言,通过它可以
    
    非常方便的来操作对象属性。类似于EL、SpEL等.
    
    *   访问对象属性:person.name
    *   调用方法:person.getName()
    *   调用静态属性/方法: @java.lang.Math@PI
    
    @java.util.UUID@random.UUID()
    
    *   调用构造方法: new com.zlj.bean.Person("admin").name
    *   运算符: +、-、*、/、%
    *   逻辑运算符: in、not、in、>、>=、<、<=、==、!=
    
    注意:xml 中特殊符号如 "、> 、 < 等这些都需要使用转义字符
    
    *   访问集合伪属性:
    
    类型 伪属性 伪属性对应的 java 方法
    
    List、Set、Map size、isEmpty List/Set/Map.size(),List/Set/Map.isEmpty()
    
    List、Set iterator List/Set.iterator
    
    Map keys、values Map.keySet(),Map.values()
    
    Iterator next、hasNext Iterator.next(),Iterator.hasNext()
    
    (4)、查询的时候如果某些条件没带可能sql拼装会有问题
    
    *   给 where 后面加上 1 = 1, 以后的条件都 and xxx
    *   Mybatis 使用 where 标签来将所有的查询条件包括在内。Mybatis就会将where 标签中拼装的sql,多出来的 and或者or 去掉。
    
    注意:where 标签只会去掉第一个多出来的 and 或者 or
    
    例:
    
    <!-- 查询员工,要求携带了哪个字段查询条件就带上这个字段的值 -->
    
    <select id="getEmpsByConditionIf" resultType="com.zlj.mybatis.bean.Employee">
    
    select * from tbl_employee
    
    <!--
    
    1、 test : 判断表达式,使用的是 OGNL
    
    2、OGNL 查看官方文档
    
    3、从参数中取值进行判断
    
    4、遇见特殊符号应该去写转义字符,例如:双引号"" --> &quot;&quot; 、 and --> &amp;&amp;
    
    -->
    
    <where>
    
    <if test="id != null">
    
    and id = #{id}
    
    </if>
    
    <if test="lastName != null &amp;&amp; lastName != &quot;&quot;">
    
    and last_name like #{lastName}
    
    </if>
    
    <if test="email != null &amp;&amp; email.trim() != &quot;&quot;">
    
    and email = #{email}
    
    </if>
    
    <!-- OGNL会进行字符串与数字的转换判断 "0" == 0 -->
    
    <if test="gender == 0 or gender == 1">
    
    and gender = #{gender}
    
    </if>
    
    </where>
    
    </select>
    
    2、Mybatis 动态SQL - trim 自定义字符串截取:
    
    (1)、trim标签:后面多出的and或者or,where标签不能解决,所以可以使用trim标签来解决
    
    *   prefix="" 前缀,trim标签体中是整个字符串拼串后的结果
    
    prefix 给拼串后的整个字符串加一个前缀
    
    *   prefixOverrides="" 前缀覆盖,去掉整个字符串前面多余的字符
    *   suffixOverrides="" 后缀覆盖,去掉整个字符串后面多余的字符
    *   suffix="" 后缀,suffix给拼串后的整个字符串加一个后缀
    
    例:
    
    <select id="getEmpsByConditionTrim" resultType="com.zlj.mybatis.bean.Employee">
    
    select * from tbl_employee
    
    <!-- 自定义字符串的截取规则 -->
    
    <trim prefix="where" suffixOverrides="and">
    
    <if test="id != null">
    
    id = #{id} and
    
    </if>
    
    <if test="lastName != null &amp;&amp; lastName != &quot;&quot;">
    
    last_name like #{lastName} and
    
    </if>
    
    <if test="email != null &amp;&amp; email.trim() != &quot;&quot;">
    
    email = #{email} and
    
    </if>
    
    <!-- OGNL会进行字符串与数字的转换判断 "0" == 0 -->
    
    <if test="gender == 0 or gender == 1">
    
    gender = #{gender}
    
    </if>
    
    </trim>
    
    </select>
    
    3、Mybatis 动态SQL - choose 分支选择:
    
    例:
    
    <select id="getEmpsByConditionChoose" resultType="com.zlj.mybatis.bean.Employee">
    
    select * from tbl_employee
    
    <where>
    
    <choose>
    
    <when test="id != null" >
    
    id = #{id}
    
    </when>
    
    <when test="lastName != null">
    
    last_name like #{lastName}
    
    </when>
    
    <when test="email != null">
    
    email = #{email}
    
    </when>
    
    <otherwise>
    
    gender = 0
    
    </otherwise>
    
    </choose>
    
    </where>
    
    </select>
    
    4、Mybatis 动态SQL - set 与 if 结合的动态更新 :
    
    例:
    
    <update id="updateEmployee">
    
    <!-- set 标签的使用 -->
    
    update tbl_employee
    
    <set>
    
    <if test="lastName != null">
    
    last_name = #{lastName},
    
    </if>
    
    <if test="email != null">
    
    email != #{email},
    
    </if>
    
    <if test="gender != null">
    
    gender = #{gender}
    
    </if>
    
    </set>
    
    where id = #{id}
    
    <!-- Trim 更新拼串
    
    update tbl_employee
    
    <trim prefix="set" suffixOverrides=",">
    
    <if test="lastName != null">
    
    last_name = #{lastName},
    
    </if>
    
    <if test="email != null">
    
    email != #{email},
    
    </if>
    
    <if test="gender != null">
    
    gender = #{gender}
    
    </if>
    
    </trim>
    
    where id = #{id}
    
    -->
    
    </update>
    
    5、Mybatis 动态SQL - foreach 遍历集合 :
    
    *   collection :指定要遍历的集合。 list类型的参数会特殊处理封装在map中,map的key 就叫list
    
    也可以是使用@Param("")在方法的入参上指定别名,则collection="指定的别名"
    
    例:List<Employee> getEmpsByConditionForeach(@Param("ids")List<Integer> ids);则collection="ids"
    
    *   item :将当前遍历出的元素赋值给指定的变量
    *   separator: 每个元素之间的分隔符
    *   open :遍历出所有结果拼接一个开始的字符
    *   close :遍历出所有的结果拼接一个结束的字符
    *   index :索引。遍历list的时候index 就是索引,item 就是当前值
    
    遍历map的时候index 表示的就是map的key,item就是map的值
    
    *   #{变量名} 就能取出变量的值,也就是当前遍历出的元素
    
    例:
    
    <select id="getEmpsByConditionForeach" resultType="com.zlj.mybatis.bean.Employee">
    
    select * from tbl_employee
    
    <foreach collection="list" item="id" separator=", " open="where id in (" close=")">
    
    #{id}
    
    </foreach>
    
    </select>
    
    6、Mybatis 动态SQL - foreach - mysql 下 foreach批量插入的两种方式 :
    
    (1)、方式一:
    
    MYSQL下的批量保存,可以 foreach遍历,mysql 支持 insert into 表名 values(), (), () 等语法
    
    <insert id="addEmps">
    
    insert into tbl_employee(last_name, email, gender, d_id) values
    
    <foreach collection="emps" item="emp" separator=",">
    
    (#{emp.lastName}, #{emp.email}, #{emp.gender}, #{emp.dept.id})
    
    </foreach>
    
    </insert>
    
    (2)、方式二: 循环遍历整条insert语句,每条insert语句之间用;隔开
    
    这种分号分隔多个sql可以用于其他批量操作(删除,修改等)
    
    注意:mysql 默认不支持用;分隔多条语句,所以需要设置数据库连接属性allowMultiQueries=true
    
    <insert id="addEmps">
    
    <foreach collection="emps" item="emp" separator=";">
    
    insert into tbl_employee(last_name, email, gender, d_id)
    
    values (#{emp.lastName}, #{emp.email}, #{emp.gender}, #{emp.dept.id})
    
    </foreach>
    
    </insert>
    
    7、Mybatis 动态SQL - foreach - Oracle下 foreach批量保存的两种方式 :
    
    (1)、Oracle 不支持 values(), (), () 这种语法
    
    (2)、Oracle 支持的批量方式:
    
    方式一:多个insert放在 begin - end 里面
    
    begin
    
    insert into employees(employee_id,last_name,email,gender)
    
    values(employees_seq.nextval,"aa","aa@163.com","1");
    
    insert into tbl_employee(employee_id,last_name,email,gender)
    
    values(employees_seq.nextval,"aa","aa@163.com","1");
    
    end;
    
    方式二:利用中间表
    
    insert into employees(employee_id,last_name,email,gender)
    
    select employees_seq.nextval,lastName,email,gender from (
    
    select "aa" lastName,"aa@163.com" email,"1" gender) from dual
    
    union
    
    select "bb" lastName,"bb@163.com" email,"1" gender) from dual
    
    )
    
    例:
    
    <insert id="addEmps">
    
    <!-- Oracle 第一种批量方式
    
    <foreach collection="emps" item="emp" open="begin" close="end;">
    
    insert into employees(employee_id,last_name,email,gender)
    
    values(employees_seq.nextval,#{emp.lastName},#{emp.email},#{emp.gender});
    
    </foreach>
    
    -->
    
    <!-- Oracle 第二种批量方式 -->
    
    insert into employees(employee_id,last_name,email,gender)
    
    <foreach collection="emps" item="emp" open=" select employees_seq.nextval,lastName,email,gender from ("
    
    close=")" separator="union">
    
    select #{emp.lastName} lastName,#{emp.email} email,#{emp.gender} gender) from dual
    
    </foreach>
    
    </insert>
    
    8、Mybatis 动态SQL - 内置参数 _parameter & _databaseId :
    
    不只是方法传递过来的参数可以被用来判断,取值。Mybatis默认还有两个内置参数:
    
    (1)、_parameter:代表整个参数
    
    单个参数:_parameter 就是这个参数
    
    多个参数:参数会被封装为一个map, _parameter就是代表这个map
    
    (2)、_databaseId : 如果配置了 databaseIdProvider 标签,_databaseId就是代表当前数据库的别名 oracle/ mysql
    
    例:
    
    <select id="getEmpsTestInnerParameter" resultType="com.zlj.mybatis.bean.Employee">
    
    <if test="_databaseId=='mysql'">
    
    select * from tbl_employee
    
    <!-- _parameter 代表的是 getEmpsTestInnerParameter 方法参数中的 Employee 对象 -->
    
    <if test="_parameter!=null">
    
    where last_name = #{_parameter.lastName}
    
    </if>
    
    </if>
    
    <if test="_databaseId=='oracle'">
    
    select * from employees
    
    <if test="_parameter != null">
    
    where last_name = #{_parameter.lastName}
    
    </if>
    
    </if>
    
    </select>
    
    9、Mybatis 动态SQL - bind 绑定 :
    
    (1)、bind : 可以将OGNL表达式的值绑定到一个变量中,方便后来引用这个变量的值
    
    例:
    
    <select id="getEmpsTestInnerParameter" resultType="com.zlj.mybatis.bean.Employee">
    
    <!-- 给 lastName 左右添加 %,然后把值赋给 _lastName , 即把Value 的值 赋给 name -->
    
    <bind name="_lastName" value="'%' + lastName + '%'"></bind>
    
    <if test="_databaseId=='mysql'">
    
    select * from tbl_employee
    
    <!-- _parameter 代表的是 getEmpsTestInnerParameter 方法参数中的 Employee 对象 -->
    
    <if test="_parameter!=null">
    
    where last_name like #{_lastName}
    
    </if>
    
    </if>
    
    </select>
    
    10、Mybatis 动态SQL - sql标签 抽取可重用的sql片段 :
    
    抽取可重用的sql片段,方便后面引用。
    
    (1)、sql标签抽取:经常将要查询的列名,或者插入用的列名抽取出来方便引用
    
    (2)、include 标签来引用已经抽取的sql
    
    (3)、include还可以自定义一些 property,sql标签内部就能使用自定义的属性
    
    取值的正确方式:${prop}
    
    #{} 不能使用这种方式取值
    
    例 :
    
    <sql id="insertColumn">
    
    <if test="_databaseId=='mysql'">
    
    last_name, email, gender, d_id
    
    <!-- last_name, email, gender, d_id, ${testColumn} -->
    
    </if>
    
    <if test="_databaseId=='oracle'">
    
    last_name, email, gender
    
    </if>
    
    </sql>
    
    <!-- 使用sql标签抽取 -->
    
    <insert id="addEmps">
    
    insert into tbl_employee(
    
    <include refid="insertColumn">
    
    <!-- <property name="testCplumn" value="abc"></property> -->
    
    </include>
    
    ) values
    
    <foreach collection="emps" item="emp" separator=",">
    
    (#{emp.lastName}, #{emp.email}, #{emp.gender}, #{emp.dept.id})
    
    </foreach>
    
    </insert>
    
    六、Mybatis两级缓存
    
    (1)、一级缓存(本地缓存):sqlSession 级别的缓存。一级缓存是一直开启的;
    
    Mybatis 一级缓存其实是sqlSession级别的一个Map.
    
    *   与数据库同一次会话期间查询到的数据会放在本地缓存中。
    *   以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库。
    
    (2)、Mybatis 一级缓存失效的四种情况(没有使用到当前一级缓存的情况,效果就是还需要再次向数据库发送sql查询)
    
    1、sqlSession 不同,导致一级缓存失效;
    
    2、sqlSession相同,但是查询条件不同,导致一级缓存失效。(当前一级缓存中还没有这个数据)
    
    3、sqlSession相同,但是两次查询之间执行了增删改操作,导致一级缓存失效。(这次增删改可能对当前数据有影响)
    
    4、sqlSession相同,但是手动清除了一级缓存(缓存清空),也会导致一级缓存失效。
    
    注意:一级缓存(本地缓存)不能被关闭,但可以调用clearCache()来清空本地缓存,或者改变缓存的作用域。
    
    (3)、二级缓存(全局缓存):基于namespace级别的缓存,一个namespace对应一个二级缓存。
    
    工作机制:
    
    1、一个会话,查询一条数据,这个数据就会被放在当前会话的一级缓存中;
    
    2、如果会话关闭,一级缓存中的数据会被保存到二级缓存中,新的会话查询信息,就可以参照二级缓存中的内容
    
    3、sqlSession ===> EmployeeMapper ===> Employee
    
    DepartmentMapper ===> Department
    
    不同的namespace 查出的数据会放在自己对应的缓存中(即map中,因为Mybatis的缓存就是一个map)
    
    (4)、二级缓存的使用步骤:
    
    1、开启全局二级缓存,在全局配置文件中配置:
    
    <!-- 开启二级缓存, 默认为true -->
    
    <setting name="cacheEnabled" value="true"></setting>
    
    2、去mapper.xml 中配置使用二级缓存:
    
    <cache eviction="FIFO" flushInterval="60000" readOnly="false" size="1024"></cache>
    
    3、为了安全,可能会用到序列化和反序列化,所以POJO需要实现序列化接口
    
    4、二级缓存,全局作用域缓存,默认不开启,需要手动配置。
    
    注意:查出的数据都会被默认先放在一级缓存中,只有会话提交或者关闭之后,一级缓存中的数据才会转移到二级缓存中 即 :二级缓存在SqlSession 关闭或者提交之后才会生效。
    
    (5)、缓存有关的设置以及属性:
    
    1、 <setting name="cacheEnabled" value="true"></setting>中的cacheEnabled,默认为true,开启缓存。
    
    如果设置为false,则关闭缓存,这里关闭的是二级缓存,一级缓存还可以一直使用。
    
    2、每个select 标签都有 useCache 属性,默认为true,使用缓存。
    
    如果设置为false,则表示不使用缓存(一级缓存还可以使用,二级缓存不能使用)
    
    3、每个增删改标签都有一个 flushCache 属性,默认为true,清除缓存,这里一级缓存,二级缓存都被清除。
    
    增删改执行完成后都会清除缓存,一二级缓存都被清除。
    
    *   查询标签也有 flushCache 属性,默认为false,不清除缓存。
    
    如果设置为true,则每次查询之后都会清空缓存,一二级缓存都不能使用
    
    4、sqlSession.clearCache(); 只是清除当前session的一级缓存。对二级缓存没有影响
    
    5、localCacheScope : 本地缓存作用域(Mybatis3.3版本之后出现)。可以影响一级缓存,不影响二级缓存。
    
    默认值为 SESSION,一级缓存session,这种情况下会缓存一个会话中执行的所有查询。
    
    若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlSession 的不同调用将不会共享数据。
    
    (可以禁用一级缓存)
    
    (6)、缓存原理图:
    
    [图片上传失败...(image-68ad87-1531473301917)]
    
    *   缓存的使用顺序:
    
    先去查找二级缓存,如果二级缓存没有数据,再去一级缓存中查询,如果还没有,才去数据库中查
    
    (7)、Mybatis 整个第三方缓存(Ehcache):
    
    1、导入第三方缓存整合的jar包;
    
    2、导入与第三方缓存整合的适配包,可以去官当查找,官当有好多与第三方缓存整合的适配包;
    
    3、Ehcache 要想正常运行,需要在配置文件中加入cache.xml配置文件;
    
    4、mapper.xml 中使用自定义的缓存,type中引入自定义缓存的全类名
    
    <cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>
    
    5、如果其他mapper.xml也使用和该mapper.xml 中一样的第三方缓存,则可以如下配置:
    
    <!-- 引用EmployeeMapper.xml中使用的自定义第三方缓存 Ehcache
    
    namespace : 指定和哪个名称空间下的缓存一样
    
    -->
    
    <cache-ref namespace="com.zlj.mybatis.dao.EmployeeMapper"></cache-ref>
    
    七、Mybatis 批量 BatchExecutor & 与Spring整合时配置批量SqlSession :
    
    注意:批量操作只有执行 sqlSession.commit();才会发送sql语句到数据库执行
    
    (1)、Mybatis 批量 BatchExecutor :
    
    /*
    
    测试 Mybatis 的批量操作
    
    */
    
    public class BatchTest {
    
    public SqlSessionFactory getSqlSessionFactory() throws IOException {
    
    String source = "mybatis-config.xml";
    
    InputStream stream = Resources.getResourceAsStream(source);
    
    return new SqlSessionFactoryBuilder().build(stream);
    
    }
    
    @Test
    
    public void test() throws IOException {
    
    SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    
    /*
    
    使用 openSession(ExecutorType execType) 这个带ExecutorType参数的方法,
    
    传入ExecutorType.BATCH参数,获取到的sqlSession就可以执行批量操作
    
    */
    
    SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
    
    try {
    
    EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
    
    long start = System.currentTimeMillis();
    
    for(int i = 0; i < 1000; i++) {
    
    mapper.addEmp(new Employee("java", "java@qq.com", "1"));
    
    }
    
    sqlSession.commit();
    
    long end = System.currentTimeMillis();
    
    /*
    
    批量操作:(预编译sql 执行一次) ==> (设置参数执行 1000次) ==> 执行sql(一次) 用时 1190 毫秒
    
    Preparing: insert into tbl_employee(last_name,email, gender) values( ?, ?, ? ) java:145
    
    Parameters: java(String), java@qq.com(String), 1(String)
    
    Parameters: java(String), java@qq.com(String), 1(String)
    
    ....
    
    非批量操作:每次执行都是 预编译sql ==> 设置参数执行 ==> 执行sql,循环执行1000次 用时 2865 毫秒
    
    Preparing: insert into tbl_employee(last_name,email, gender) values( ?, ?, ? )
    
    Parameters: java(String), java@qq.com(String), 1(String) java:145
    
    Updates: 1 java:145
    
    */
    
    System.out.println("批量操作一共用时 : " + (end - start));
    
    } finally {
    
    sqlSession.close();
    
    }
    
    }
    
    }
    
    (2)、Mybatis与Spring整合时配置批量SqlSession :
    
    *   在ApplicationContext.xml配置文件中配置:
    
    <!--配置一个可以进行批量执行的sqlSession -->
    
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
    
    <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactoryBean"></constructor-arg>
    
    <constructor-arg name="executorType" value="BATCH"></constructor-arg>
    
    </bean>
    
    *   然后在service 中通过@Autowired注入可以执行批量操作的SqlSession ,使用该SqlSession 就可以执行批量操作
    
    七、Mybatis自定义类型转换器
    
    1、自定义类型转换器,需要实现 TypeHandler 接口,或者继承 BaseTypeHandler
    
    2、配置自定义的类型转换器(两种方式):
    
    *   在全局配置文件中进行配置;
    *   在指定的字段上进行配置
    
    <typeHandlers>
    
    <!-- 1、配置自定义的 TypeHandler -->
    
    <typeHandler handler="com.zlj.mybatis.typeHandler_enum.MyEnumEmpStatusTypeHandler" javaType="com.zlj.mybatis.bean.EmpStatus"></typeHandler>
    
    <!--
    
    2、也可以在处理某个字段的时候告诉Mybatis用什么类型的处理器
    
    保存时:在传入参数时,做如下操作: #{empStatus, typeHandler = “自定义类型处理器的全类名”}
    
    查询时:定义一个 resultMap ,在resultMap 的指定字段中指定即可
    
    <resultMap type="com.zlj.mybatis.bean.Employee" id="PageEmp">
    
    <id column="id" property="id"/>
    
    <result column="last_name" property="lastName"/>
    
    <result column="email" property="email"/>
    
    <result column="empStatus" property="empStatus" typeHandler="自定义类型处理器的全类名"/>
    
    </resultMap>
    
    -->
    
    <!-- 使用Mybatis自带的类型转换器 -->
    
    <!-- <typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler" javaType="com.zlj.mybatis.bean.EmpStatus"></typeHandler>-->
    
    </typeHandlers>
    
    3、测试
    
    /*
    
    *   Mybatis 默认在处理枚举对象的时候,保存的是枚举的名字,使用的是 EnumTypeHandler处理
    *   如果想改变这个设置,可以使用 EnumOrdinalTypeHandler 来处理,这样Mybatis在处理枚举对象的时候,保存的是枚举的索引
    *   需要在全局配置文件中配置:
    
    <typeHandlers>
    
    <!-- 自定义枚举处理器 -->
    
    <typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler" javaType="com.zlj.mybatis.bean.EmpStatus"></typeHandler>
    
    </typeHandlers>
    
    */
    
    @Test
    
    public void test() throws IOException {
    
    SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    
    SqlSession sqlSession = sqlSessionFactory.openSession();
    
    try {
    
    EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
    
    Employee employee = new Employee("lastName_enum", "enum@qq.com","0");
    
    mapper.addEmp(employee);
    
    sqlSession.commit();
    
    System.out.println("保存成功 : " + employee.getId());
    
    } finally {
    
    sqlSession.close();
    
    }
    
    }

    相关文章

      网友评论

          本文标题:Mybatis映射文件

          本文链接:https://www.haomeiwen.com/subject/rnrspftx.html