Mybatis映射文件

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();

}

}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 228,923评论 6 535
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 98,740评论 3 420
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 176,856评论 0 380
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 63,175评论 1 315
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 71,931评论 6 410
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 55,321评论 1 324
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 43,383评论 3 443
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 42,533评论 0 289
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 49,082评论 1 335
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 40,891评论 3 356
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 43,067评论 1 371
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 38,618评论 5 362
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 44,319评论 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 34,732评论 0 27
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 35,987评论 1 289
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 51,794评论 3 394
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 48,076评论 2 375

推荐阅读更多精彩内容