一、MyBatis入门 + 接口式编程(idea实现)

1.maven工程

  1. <dependencies>
  2. <dependency>
  3. <groupId>org.mybatis</groupId>
  4. <artifactId>mybatis</artifactId>
  5. <version>3.4.6</version>
  6. </dependency>
  7. <dependency>
  8. <groupId>mysql</groupId>
  9. <artifactId>mysql-connector-java</artifactId>
  10. <version>8.0.21</version>
  11. </dependency>
  12. <dependency>
  13. <groupId>log4j</groupId>
  14. <artifactId>log4j</artifactId>
  15. <version>1.2.17</version>
  16. </dependency>
  17. <dependency>
  18. <groupId>junit</groupId>
  19. <artifactId>junit</artifactId>
  20. <version>4.13</version>
  21. <scope>test</scope>
  22. </dependency>
  23. </dependencies>

2.项目目录

03.mybatis - 图1

3.SQL文件

CREATE DATABASE `mybatis`;

USE `mybatis`;

CREATE TABLE `user`(
 `id` INT(20) NOT NULL PRIMARY KEY,
  `name` VARCHAR(30) DEFAULT NULL,
  `pwd` VARCHAR(30) DEFAULT NULL
)ENGINE=INNODB DEFAULT CHARSET=utf8;

INSERT INTO `user` VALUES(1,'manster','123456'),(2,'taotao','123456'),(3,'feifei','123456');

4.javaBean

package com.manster.bean;

public class Employee {

   private Integer id;
   private String lastName;
   private String email;
   private String gender;


   public Integer getId() {
      return id;
   }
   public void setId(Integer id) {
      this.id = id;
   }
   public String getLastName() {
      return lastName;
   }
   public void setLastName(String lastName) {
      this.lastName = lastName;
   }
   public String getEmail() {
      return email;
   }
   public void setEmail(String email) {
      this.email = email;
   }
   public String getGender() {
      return gender;
   }
   public void setGender(String gender) {
      this.gender = gender;
   }
   @Override
   public String toString() {
      return "Employee [id=" + id + ", lastName=" + lastName + ", email="
            + email + ", gender=" + gender + "]";
   }
}

5.mybatis的全局配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <properties resource="db.properties"/>

    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>

    <typeAliases>
        <package name="com.manster.bean"/>
    </typeAliases>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC" />
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver" />
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true" />
                <property name="username" value="root" />
                <property name="password" value="123456" />
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <package name="com.manster.mapper" />
    </mappers>
</configuration>

6.sql映射文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.manster.mapper.EmployeeMapper">

    <select id="getEmpById" resultType="employee">
      select id,last_name,email,gender from tbl_employee where id = #{id}
   </select>
</mapper>

7.log4j配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/doc-files/log4j.dtd">

<log4j:configuration>

    <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
        <param name="Encoding" value="UTF-8"/>
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss,SSS} %m  (%F:%L) \n"/>
        </layout>
    </appender>
    <logger name="java.sql">
        <level value="debug"/>
    </logger>
    <logger name="org.apache.ibatis">
        <level value="info"/>
    </logger>
    <root>
        <level value="debug"/>
        <appender-ref ref="STDOUT"/>
    </root>
</log4j:configuration>

8.mapper 接口

package com.manster.mapper;

import com.manster.bean.Employee;

public interface EmployeeMapper {

    Employee getEmpById(int id);

    Employee getEmpAndDept(Integer id);
}

9.测试代码

public class MybatisTest {

    public SqlSessionFactory getSqlSessionFactory() throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        return sqlSessionFactory;
    }


    @Test
    public void test() throws IOException {
        SqlSession sqlSession = getSqlSessionFactory().openSession();
        EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
        Employee empById = mapper.getEmpById(1);
        System.out.println(empById);
    }
}

二、全局配置文件详解

1、peoperties

mybatis可以使用properties来引入外部properties配置文件的内容

  • resource:引入类路径下的资源

  • url:引入网络或者磁盘路径下的资源

db.properties:

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true
jdbc.username=root
jdbc.password=123456

2、settings

这是mybatis中极为重要的调整设置,他们会改变mybatis的运行时行为

<!-- 设置转驼峰
settings:所有的设置项
setting :每个设置项
-->
<settings>
   <setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

3、typeAliases 取别名

<!--别名处理器 typeAliases 为某个类型起别名  别名不区分大小写-->
<typeAliases>
   <!--
   typeAlias : 为某个java类型取别名
      type: 指定要娶别名的类型的全类名,默认别名时类名小写employee
      alias: 指定新的别名
   -->
   <!--<typeAlias type="com.mansterbean.Employee" alias="emp"></typeAlias>-->

   <!--批量起别名
      name :指定包名(为当前包已经下面所有子包的所有类起一个默认别名,类名小写)
   -->
   <package name="com.manster.bean"/>
</typeAliases>

4、typeHandlers_类型处理器

5、plugins_插件简介

- Executor(update,query,flushStatements,commit,rollback,getTransaction,close,isClosed)
- ParameterHandler(getParameterObject, setParameters)
- ResultSetHandler(handleResultSets,handleOutputParameters)
- StatementHandler(prepare,parameterize,batch,update,query)

6、environments

<!--
   mybatis 可以配置多种环境  default指定使用某种环境
   environment :配置具体的环境信息,必须有两个标签:transactionManager dataSource
      id: 是环境的唯一标识
      transactionManager: 事务管理器
         type : JDBC|MANAGED 事务管理器的类型

      dataSource: 数据源
         type: UNPOOLED|POOLED|JNDI


-->
<environments default="development">
   <environment id="development">
      <transactionManager type="JDBC" />
      <dataSource type="POOLED">
         <property name="driver" value="${jdbc.driver}" />
         <property name="url" value="${jdbc.url}" />
         <property name="username" value="${jdbc.username}" />
         <property name="password" value="${jdbc.password}" />
      </dataSource>
   </environment>
</environments>

7、databaseIdProvider_多数据库支持

<!--
   databaseIdProvider : 支持多数据库厂商

   mybatis 根据数据库厂商的标识来执行不同的sql

-->
<databaseIdProvider type="DB_VENDOR">
   <property name="MySQL" value="mysql"/> //支持mysql环境
   <property name="Oracle" value="oracle"/> //支持oracle环境
</databaseIdProvider>

databaseId:告诉mybatis 这条语句是在什么环境下执行的

<select id="getEmpById" resultType="employee" databaseId="mysql">
   select id,last_name,email,gender from tbl_employee where id = #{id}
</select>

8、mappers

<!-- 将我们写好的sql映射文件(EmployeeMapper.xml)
一定要注册到全局配置文件(mybatis-config.xml)中 -->
<!--
   将sql映射注册到 mybatis 全局配置文件中
   mapper: 注册一个sql映射
      resource:引用类路径下的sql映射文件
      url : 引用网络路径或者磁盘路径下的sql映射文件

      class : 引用接口
         1、映射文件名和接口同名,并且与接口放在同一路径下
         2、没有sql映射文件,所有的sql都是利用注解写在接口上

         推荐:
         比较重要的,复杂的dao接口,我们来写sql映射文件
         不重要,简单的sql文件我们利用注解写在接口上

      package: 批量注册
      映射文件名和接口同名,
      并且与接口放在同一路径下

-->
<mappers>
   <!--<mapper resource="EmployeeMapper.xml" />-->
   <!--<mapper class="com.manster.mapper.EmployeeMapper"></mapper>-->
   <package name="com.manster.mapper"/>
</mappers>

9、注意

如果出现了以下错误:

org.apache.ibatis.binding.BindingException: Invalid bound statement
 (not found): com.manster.mapper.EmployeeMapper.getEmpById

而且是在idea中使用maven工程进行构建的项目,可以在pom.xml中加入以下配置来避免不编译

<build>
    <resources>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.xml</include>
            </includes>
        </resource>
    </resources>
</build>

然后使 maven 进行 reload 运行即可

三、Mybatis- 映射文件

1、insert_获取自增主键的值

<!-- 增加
    mybatis 也是利用 statement.getGeneratedKeys()来获取自增主键值
    useGeneratedKeys ="true" 使用自增主键获取主键值策略
    keyProperty : mybatis获取到主键值后,将其封装到java bean的哪个属性
-->
<insert id="addEmp" parameterType="com.manster.bean.Employee" useGeneratedKeys="true"
   keyProperty="id">
   insert into tbl_employee(last_name,email,gender)
   values(#{lastName},#{email},#{gender})
</insert>

2、insert:获取非自增主键的值selectKey

<!--
      oracle 不支持自增: Oracle 使用序列来模拟自增
      每次插入的数据的主键是从序列中拿到的值,那么如何获取到这个值
  -->
  <insert id="addEmp" databaseId="oracle">
      /*
      keyProperty: 查出的主键值封装到javaBean的哪个属性
      order = "BEFORE" 当前sql在插入sql之前运行
              "AFTER"  当前sql在插入sql之后运行
      resultType : 查出的数据的返回值类型
      */
      <selectKey keyProperty="id" order="BEFORE" resultType="Integer">
          /*编写查询主键的sql语句*/
          select employees_seq.nextval from dual;
      </selectKey>
      /*插入的主键是从序列中拿到的*/
      insert into employees(id,last_name,email,gender)
values(#{lastName},#{email},#{gender})
  </insert>

3、mybatis参数处理

  • 单个参数:

    • mybatis不会做特殊处理,#{参数名} 取出参数
  • 多个参数

    • mybatis会做特殊处理
    • 多个参数会被封装成一个map

      • key:param1…paramN
      • value: 对应的按照顺序传入的参数
      • {param1} 就是从map中获取指定的值

  • 命名参数:

    • 明确指定封装参数时map的key ==》接口定义时:

    • 多个参数会被封装成一个map

      • key:使用@param注解指定的值
      • value:参数值
    • {key} 就是从map中获取指定的值

接口中的方法:

public Employee getEmpByIdAndLastName(@Param("id") int id, @Param("lastName") String lastName);

mapper.xml中

<select id="getEmpByIdAndLastName" resultType="employee">
   select id,last_name,email,gender from tbl_employee 
   where id = #{id} and last_name =#{lastName}
</select>

POJO:

如果多个参数正好是我们业务逻辑的数据原型,可以之间传入pojo

{属性名} 取出对应的值

Map:

如果多个参数不是业务模型中的数据,没有对应的pojo,为了方便,我们也可以传入map

{key} 取出对应的值

应用示例

 public Employee GetEmp(@Param("id") Integer id,String lastName);在映射文件中怎么赋值

      取值:id-->#{id/param1} last_name-->#{param2}

 public Employee GetEmp( Integer id,Employee emp);在映射文件中怎么赋值

      取值:id-->#{id/param1}     last_name-->#{param2.lastname}

 public Employee GetEmp( Integer id,@Param("em") Employee emp);在映射文件中怎么赋值

      取值:id-->#{id/param1}     last_name-->#{param2.lastname/em.lastname}           

 特别注意,如果是Collection类型(list,set)类型或者数组。会特殊处理,会把list或者数组封装进map中
                     如果key是Collection(collection) 
                           是List(list)
                           是数组(array)

 public Employee GetEmp(List<Integer> ids);在映射文件中怎么赋值

      取值:id-->#{list[0]}

参数值的获取

{} :以预编译的形式,将参数设置到sql语句中,类似于jdbc的参数占位符,防止sql注入

${} : 取出的值直接拼装在sql语句中,会有安全问题

#{} 更丰富的用法

规定参数的一些规则:

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,jdbcType=NULL };

    2、jdbcTypeForNull=NULL

4、select 返回list和map

如果select返回list ,resultType 取得是 list 中的泛型

如果select返回map,resultType = “map”

5、select_resultMap

增加department实体类

create table tbl_dept
(
    id        int auto_increment
        primary key,
    dept_name varchar(255) null
)
-- 为employee增加外键
constraint tbl_employee_tbl_dept__fk foreign key (d_id) references tbl_dept (id)
public class Department {
    private Integer id;
    private String deptName;
public class Employee {
    private Integer id;
    private String lastName;
    private String email;
    private String gender;
    private Department dept;

自定义结果映射规则:

<resultMap id="myemp" type="com.manster.bean.Employee">
    <!--指定主键列的封装规则
    id 定义主键,底层会有优化
    column :指定哪一列
    property: 指定对应的javaBean属性
    -->
    <id column="id" property="id"></id>
    <result column="last_name" property="lastName"></result>
</resultMap>

<select id="getEmpById" resultMap="myemp">
    select * from tbl_employee where id = #{id}
</select>

级联属性封装结果:

    <resultMap id="myEmpDept" type="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.deptName"></result>

    </resultMap>
    <select id="getEmpAndDept" resultMap="myEmpDept" >
        select
        e.id id,
        e.last_name last_name,
        e.gender gender,
        e.email email,
        d.id did,
        d.dept_name dept_name
        from  tbl_employee e,tbl_dept d
        where e.d_id = d.id
        and e.d_id = #{id}
    </select>

03.mybatis - 图2

association定义关联对象封装规则(嵌套结果):

<!--
    使用 association ,封装关联的单个对象
-->
    <resultMap id="myEmpDept1" type="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 : 指定哪个属性是联合的对象
             javaType : 指定这个属性对象的类型
        -->
        <association property="dept" javaType="department">
                <result column="did" property="id"></result>
                <result column="dept_name" property="deptName"></result>
           </association>
    </resultMap>

    <select id="getEmpAndDept" resultMap="myEmpDept" >
        select
        e.id id,
        e.last_name last_name,
        e.gender gender,
        e.email email,
        d.id did,
        d.dept_name dept_name
        from  tbl_employee e,tbl_dept d
        where e.d_id = d.id
        and e.d_id = #{id}
    </select>

association分步查询(嵌套查询)

1)先准备好department对象的mapper和 xml文件

DepartmentMapper.xml

<select id="getDeptById" resultType="department">
   select id,dept_name deptName from tbl_dept where id =#{id}
</select>

EmployeeMapperPlus.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.manster.mapper.EmployeeMapperPlus">
    <resultMap id="myEmpDept2" type="com.manster.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 : 指定哪个属性是联合的对象
             select: 调用目标的方法查询当前属性的值
             column: 将sql中的哪一列传入上述调用的方法
        -->
        <association property="dept" select="com.manster.mapper.DepartmentMapper.getDeptById" column="d_id">
        </association>
    </resultMap>

    <select id="getEmpAndDept" resultMap="myEmpDept2">
        select
        e.id id,
        e.last_name last_name,
        e.gender gender,
        e.email email,
        e.d_id
        from  tbl_employee e
        where e.d_id = #{id}
    </select>
</mapper>

分步查询&延迟加载

只有在真正用到关联对象时,才会进行第二次的分布查询

1.在mybatis-config.xml增加配置

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

2.EmployeeMapperPlus.java

public interface EmployeeMapper {

    Employee getEmpById(int id);

    Employee getEmpAndDept(Integer id);
}

3.EmployeeMapperPlus.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.manster.mapper.EmployeeMapperPlus">
    <resultMap id="myEmpByStep" type="com.manster.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 property="dept" select="com.manster.mapper.DepartmentMapper.getDeptById" column="d_id">
        </association>
    </resultMap>

    <select id="getEmpByIdStep" resultMap="myEmpByStep">
        select * from tbl_employee where id=#{id}
    </select>
</mapper>

4.Test

    @Test
    public void test01() throws IOException {
        SqlSession sqlSession = getSqlSessionFactory().openSession();
        EmployeeMapperPlus mapper = sqlSession.getMapper(EmployeeMapperPlus.class);
        Employee emp = mapper.getEmpByIdStep(1);
        System.out.println(emp.getLastName());
        //System.out.println(emp.getDept());

    }

查询与dept无关的信息

03.mybatis - 图3

查询与dept有关的信息

03.mybatis - 图4

collection定义关联集合封装规则

department.java中增加private List<Employee> emps;,并添加相应的get、set方法

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.manster.mapper.DepartmentMapper">
    <select id="getDeptById" resultType="department">
        select id,dept_name deptName from tbl_dept where id =#{id}
    </select>


    <resultMap id="myDept" type="com.manster.bean.Department">
        <id column="did" property="id"></id>
        <result column="dept_name" property="deptName"></result>
        <collection property="emps" ofType="com.manster.bean.Employee">
            <id column="id" 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="getDeptByIdPlus" resultMap="myDept">
           select
           d.id did,
           d.dept_name dept_name,
           e.id id,
           e.last_name last_name,
           e.email email,
           e.gender gender
           from tbl_dept d
           left join tbl_employee e
           on d.id =e.d_id
           where d.id =#{id}
    </select>
</mapper>
@Test
public void test02() throws IOException {
    SqlSession sqlSession = getSqlSessionFactory().openSession();
    DepartmentMapper mapper = sqlSession.getMapper(DepartmentMapper.class);
    Department dept = mapper.getDeptByIdPlus(1);
    System.out.println(dept.getEmps());
}

03.mybatis - 图5

collection分步查询和延迟加载

DepartmentMapper.xml在接口类中也写上相应接口

    <resultMap id="MyDeptStep" type="com.manster.bean.Department">
        <id column="did" property="id"></id>
        <result column="dept_name" property="deptName"></result>
        <collection property="emps" select="com.manster.mapper.EmployeeMapper.getEmpsByDeptId" column="id">

        </collection>
    </resultMap>
    <select id="getDeptByIdStep" resultMap="MyDeptStep">
        select id,dept_name from tbl_dept where id=#{id}
    </select>

EmployeeMapperPlus.xml在接口类中也写上相应接口

    <select id="getEmpsByDeptId" resultType="com.manster.bean.Employee">
        select * from tbl_employee where d_id=#{deptId}
    </select>

测试

    @Test
    public void test02() throws IOException {
        SqlSession sqlSession = getSqlSessionFactory().openSession();
        DepartmentMapper mapper = sqlSession.getMapper(DepartmentMapper.class);
        Department dept = mapper.getDeptByIdStep(1);
        System.out.println(dept.getDeptName());
        //System.out.println(dept.getEmps());
    }

dept.getDeptName()

03.mybatis - 图6

dept.getEmps()

03.mybatis - 图7

扩展:分步查询select中方法,如果要传多列的值:

将多列的值封装成map传递

column = “{k1= column1}”

fetchType =”lazy”:表示延迟加载

-lazy :延迟加载

-eager: 立即查询

<collection property="emps" select="com.manster.mapper.EmployeeMapper.getEmpsByDeptId" column="id" fetchType="lazy">

</collection>

四、Mybatis- 动态sql

1、if标签

<select id="getEmpsByConditionIf" resultType="com.manster.bean.Employee">
   select * from tbl_employee
   where 1=1
   <if test="id!=null">
      and id = #{id}
   </if>
   <if test="lastName!=null and lastName !=''">
      and last_name like '%${lastName}%'
   </if>
   <if test="email!=null">
      and email like  '%${email}%'
   </if>
   <if test="gender==0 or gender == 1">
      and gender = #{gender}
   </if>
</select>

2、where标签

去除动态sql中多余的and 和 or

<select id="getEmpsByConditionIf" resultType="com.manster.bean.Employee">
   select * from tbl_employee
   <where>
      <if test="id!=null">
         and id = #{id}
      </if>
      <if test="lastName!=null and lastName !=''">
         and last_name like '%${lastName}%'
      </if>
      <if test="email!=null">
         and email like  '%${email}%'
      </if>
      <if test="gender==0 or gender == 1">
         and gender = #{gender}
      </if>
   </where>
</select>

3、trim 字符串截取

<select id="getEmpsByConditionTrim" resultType="com.manster.bean.Employee">
   select * from tbl_employee
   <!--
      后面多出的and or where 标签不能解决
      trim标签体中是整个字符串拼拼接后的结果

      prefix:给拼串后的字符串加一个前缀
      prefixOverrides="" : 前缀覆盖 去掉整个字符串前面多余的字符串
      suffixOverrides="" : 后缀覆盖 去掉整个字符串后面多余的字符串
   -->
   <trim prefix="where" suffixOverrides="and">
      <if test="id!=null">
         id = #{id} and
      </if>
      <if test="lastName!=null and lastName !=''">
         last_name like '%${lastName}%' and
      </if>
      <if test="email!=null">
         email like  '%${email}%' and
      </if>
      <if test="gender==0 or gender == 1">
         gender = #{gender} and
      </if>
   </trim>
</select>

4、choose when

<select id="getEmpsByConditionChoose" resultType="com.manster.bean.Employee">
   select * from tbl_employee
   <where>
      <choose>
         <when test="id!=null">
            and id = #{id}
         </when>
         <when test="lastName!=null and lastName !=''">
            and last_name like '%${lastName}%'
         </when>
         <otherwise>
            and gender = 1
         </otherwise>
      </choose>
   </where>
</select>

5、set

<update id="updateEmp">
    <!-- 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>

6、foreach

<select id="getEmpsByConditionForeach" resultType="com.manster.bean.Employee">
    select * from tbl_employee
    <!--
    collection:指定要遍历的集合:
     list类型的参数会特殊处理封装在map中,map的key就叫list
    item:将当前遍历出的元素赋值给指定的变量
    separator:每个元素之间的分隔符
    open:遍历出所有结果拼接一个开始的字符
    close:遍历出所有结果拼接一个结束的字符
    index:索引。遍历list的时候是index就是索引,item就是当前值
            遍历map的时候index表示的就是map的key,item就是map的值

    #{变量名}就能取出变量的值也就是当前遍历出的元素
     -->
    <foreach collection="ids" item="item_id" separator=","
             open="where id in(" close=")">
        #{item_id}
    </foreach>
</select>

mysql foreach 批量保存的两种方式:

<!-- 第一种 -->
<!--public void addEmps(@Param("emps")List<Employee> emps);  -->
<!--MySQL下批量保存:可以foreach遍历   mysql支持values(),(),()语法-->
<insert id="addEmps">
    insert into tbl_employee(last_name,email,gender,dept_id)    
    values
    <foreach collection="emps" item="emp" separator=",">
        (#{emp.lastName},#{emp.email},#{emp.gender},#{emp.dept.id})
    </foreach>
</insert>

<!-- 第二种 -->
<insert id="addEmps">
    <foreach collection="emps" item="emp" separator=";">
    insert into tbl_employee(last_name,email,gender,dept_id)
    values(#{emp.lastName},#{emp.email},#{emp.gender},#{emp.dept.id})
    </foreach>
</insert>

7、内置参数_parameter&_databaseId

两个内置参数:

     不只是方法传递过来的参数可以被用来判断,取值。。。

     mybatis默认还有两个内置参数:

     _parameter:代表整个参数

         单个参数:_parameter就是这个参数

         多个参数:参数会被封装为一个map;_parameter就是代表这个map



     _databaseId:如果配置了databaseIdProvider标签。

         _databaseId就是代表当前数据库的别名
<select id="getEmpsTestInnerParameter" resultType="com.manster.bean.Employee">
    <if test="_databaseId=='mysql'">
        select * from tbl_employee
        <if test="_parameter!=null">
            where last_name like #{lastName}
        </if>
    </if>
    <if test="_databaseId=='oracle'">
        select * from employees
        <if test="_parameter!=null">
            where last_name like #{_parameter.lastName}
        </if>
    </if>
</select>

8、bind

可以将OGNL表达式的值绑定到一个变量中,方便后来引用这个变量的值

<select id="getEmpsTestInnerParameter" resultType="com.manster.bean.Employee">
    <!-- bind:可以将OGNL表达式的值绑定到一个变量中,方便后来引用这个变量的值 -->
    <bind name="_lastName" value="'%'+lastName+'%'"/>
    select * from tbl_employee
    <if test="_parameter!=null">
        where last_name like #{_lastName}
    </if>
</select>

9、可重用sql片段

定义sql片段:

<sql id="sqlColumn">
   id,last_name,email,gender
</sql>

引用sql片段:

<select id="getEmpsByConditionChoose" resultType="com.manster.bean.Employee">
   select
   <include refid="sqlColumn"></include>
   from tbl_employee
   <where>
      <choose>
         <when test="id!=null">
            and id = #{id}
         </when>
         <when test="lastName!=null and lastName !=''">
            and last_name like '%${lastName}%'
         </when>
         <otherwise>
            and gender = 1
         </otherwise>
      </choose>
   </where>
</select>

五、Mybatis- 缓存机制

mybatis包含一个非常强大的查询缓存特性,他可以非常方便的配置和定制,缓存可以极大的提高查询效率

1、默认情况下,只有一级缓存( SqlSession级别的缓存,也称为本地缓存)开启。
2、二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
3、为了提高扩展性。 MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存

1.一级缓存

  • 一级缓存(local cache), 即本地缓存, 作用域默认为sqlSession。当 Session flush 或 close 后, 该Session 中的所有 Cache 将被清空。

  • 本地缓存不能被关闭, 但可以调用 clearCache()来清空本地缓存, 或者改变缓存的作用域.

  • 在mybatis3.1之后, 可以配置本地缓存的作用域.在 mybatis.xml 中配置

  • 同一次会话期间只要查询过的数据都会保存在当前SqlSession的一个Map中

    • key:hashCode+查询的SqlId+编写的sql查询语句+参数

一级缓存体验

@Test
public void testFirstCache() throws IOException {
    SqlSession sqlSession = getSqlSessionFactory().openSession();

    EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
    Employee emp = mapper.getEmpById(1);
    System.out.println(emp);

    Employee emp1 = mapper.getEmpById(1);
    System.out.println(emp1);
    System.out.println(emp1==emp);//true
}

失效情况

  • 不同的SqlSession对应不同的一级缓存
  • 同一个SqlSession但是查询条件不同
  • 同一个SqlSession两次查询期间执行了任何一次增删改操作
  • 同一个SqlSession两次查询期间手动清空了缓存

2.二级缓存

  • 二级缓存(second level cache),全局作用域缓存
  • 二级缓存默认不开启,需要手动配置
  • MyBatis提供二级缓存的接口以及实现,缓存实现要求POJO实现Serializable接口
  • 二级缓存在 SqlSession 关闭或提交之后才会生效

使用步骤
1、全局配置文件中开启二级缓存 <setting name="cacheEnabled" value="true"/>
2、需要使用二级缓存的映射文件处使用cache配置缓存<cache />
3、注意: POJO需要实现Serializable接口

二级缓存体验

    //注意,一定要是一个SqlSessionFactory创建出来的sqlSession
    @Test
    public void testSecondCache() throws IOException {
        SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
        SqlSession sqlSession1 = sqlSessionFactory.openSession();
        SqlSession sqlSession2 = sqlSessionFactory.openSession();

        EmployeeMapper mapper1 = sqlSession1.getMapper(EmployeeMapper.class);
        EmployeeMapper mapper2 = sqlSession2.getMapper(EmployeeMapper.class);

        Employee emp1 = mapper1.getEmpById(1);
        System.out.println(emp1);
        sqlSession1.close();

        Employee emp2 = mapper2.getEmpById(1);
        System.out.println(emp2);
        sqlSession2.close();
    }

03.mybatis - 图8

缓存相关属性

  • eviction=“FIFO”: 缓存回收策略:

    • LRU – 最近最少使用的:移除最长时间不被使用的对象。
    • FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
    • SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
    • WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
    • 默认的是 LRU。
  • flushInterval: 刷新间隔,单位毫秒

    • 默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新
  • size: 引用数目,正整数

    • 代表缓存最多可以存储多少个对象,太大容易导致内存溢出
  • readOnly: 只读, true/false

    • true:只读缓存;会给所有调用者返回缓存对象的相同实例。 因此这些对象
      不能被修改。这提供了很重要的性能优势。
    • false:读写缓存; 会返回缓存对象的拷贝(通过序列化)。这会慢一些,
      但是安全,因此默认是 false。

缓存有关设置

1、全局setting的cacheEnable:
– 配置二级缓存的开关。一级缓存一直是打开的。
2、 select标签的useCache属性:
– 配置这个select是否使用二级缓存。一级缓存一直是使用的
3、 sql标签的flushCache属性:
– 增删改默认flushCache=true。 sql执行以后,会同时清空一级和二级缓存。
查询默认flushCache=false。
4、 sqlSession.clearCache():
– 只是用来清除一级缓存。
5、 当在某一个作用域 (一级缓存Session/二级缓存
Namespaces) 进行了 C/U/D 操作后,默认该作用域下所
有 select 中的缓存将被clear。

03.mybatis - 图9

六、MyBatis工作原理

03.mybatis - 图10

mybatis的框架分层架构

03.mybatis - 图11

1、SQLSessionFactory的初始化

03.mybatis - 图12

configuration 封装了所有的配置文件的详细信息

整个SqlSessionFactory的初始化总结来说:

把配置文件的信息解析并保存在Configuration对象中,并返回DefaultSqlSessionFactory对象

2、openSession获取SqlSession对象

03.mybatis - 图13

3、getMapper获取到接口的代理对象

03.mybatis - 图14

4、查询实现

总结:

1、根据配置文件(全局配置,sql映射文件),初始化configuration对象

2、创建一个DefaultSqlSession对象,里面包含Configuration 以及 executor

3、DefaultSqlSession.getMapper(): 拿到mapper接口对应的mapperProxy

 mapperProxy中有 DefaultSqlSession

4、执行增删改查方法

1)调用 DefaultSqlSession的增删改查

2)创建一个StatementHangler对象,同时也会创建一个ParameterHandler 和 ResultSetHandler

3)调用StatementHangler预编译参数和设置参数值

4)调用StatementHangler的增删改查方法

5)使用ResultHandler来封装结果

【注意】

四大对象创建的时候都有一个 interceptorChain.pluginAll(parameterHandler);