.MyBatis

109 阅读4分钟

MyBatis

  • 半自动化的持久层框架

    • 半自动化

      • 还需要程序员手动编写sql语句
    • 持久层框架

      • 对JDBC的封装

1.1 HelloWorld

  • 创建maven模块(java类型)

  • 导入jar包

    • mybatis
    • mysql驱动
    • junit
  • 准备一个数据库和表格

  • 创建实体类

  • 创建EmployeeDao的接口

  • 创建EmployeeDao的实现类,被映射文件替代

    • 创建EmployeeDao的映射文件(xml配置文件)
  • 创建MyBatis的核心配置文件

    • 设置环境(数据库连接信息)
    • 加载映射文件
  • 测试

    • 获取到持久层对象
    • 调用方法测试即可

1.2添加日志记录

  • 目的是为了查看sql语句、参数和结果

  • 添加jar包

    • log4j
  • 添加log4j.xml配置文件

2.MyBatis的全局配置文件

2.1 properties

<!--
    1. properties    (后期会被Spring代替)
        功能:引入外部属性文件
-->
<properties resource="db.properties">
    <property name="password" value="123456"/>
</properties>

2.2 settings

<!--
    2. settings
        对MyBatis的整体设置
            cacheEnabled  是否开启二级缓存
            lazyLoadingEnabled   是否开启延迟加载
            aggressiveLazyLoading   按需加载
            mapUnderscoreToCamelCase  是否开启自动驼峰
            ...
-->
<settings>
    <setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

2.3 typeAliases

<!--
        3.typeAliases  起别名
            3.1 自带的类型,都有对应的别名
                基本上就是类名首字母小写即可
            3.2 自定义的类型
    -->
    <typeAliases>
        <!--方式一:单个类起别名-->
<!--        <typeAlias type="com.atguigu.bean.Employee" alias="emp"/>-->
        <!--方式二:设置包名 ★-->
        <package name="com.atguigu.bean"/>
    </typeAliases>

2.4 typeHandlers

<!--
    4. typeHandlers
        类型处理器,处理java和数据库数据类型的转换
        目前:不用显示的声明
-->
<!--<typeHandlers>
    <typeHandler handler=""
</typeHandlers>-->

2.5 plugins

<!--
    5. plugins
        添加插件:分页插件
-->
<!--<plugins>
    <plugin interceptor=""></plugin>
</plugins>-->

2.6 environments

<!--
    6.environments  (后期交给Spring)
        default属性:选择使用哪个环境
-->
<environments default="development">
    <environment id="development">
        <!--事务管理-->
        <transactionManager type="JDBC"/>
        <!--数据源-->
        <dataSource type="POOLED">
            <property name="driver" value="${driverClassName}"/>
            <property name="url" value="${url}"/>
            <property name="username" value="${username}"/>
            <property name="password" value="${password}"/>
        </dataSource>
    </environment>
    <environment id="test">
        <transactionManager type="JDBC"/>
        <dataSource type="POOLED">
            <property name="driver" value="${driverClassName}"/>
            <property name="url" value="${url}"/>
            <property name="username" value="${username}"/>
            <property name="password" value="${password}"/>
        </dataSource>
    </environment>
</environments>

2.7 databaseIdProvider

<!--
    7. databaseIdProvider
        为不同的数据库起个别名,映射文件里就可以写多套标签
        type:指定类型
-->
<databaseIdProvider type="DB_VENDOR">
    <property name="MySQL" value="mysql"/>
    <property name="Oracle" value="oracle"/>
</databaseIdProvider>

2.8 mappers

<!--
        8.加载映射文件   (Spring加载)
    -->
    <mappers>
        <!--第一种方式:通过映射文件的路径-->
<!--        <mapper resource="EmployeeDao.xml"/>-->
        <!--第二种方式:通过接口的全类名
            要求:接口和映射文件同包同名
        -->
<!--        <mapper class="com.atguigu.dao.EmployeeDao"/>-->
        <!--第三种方式:通过包名设置 ★
            设置接口的包名
            要求:接口和映射文件同包同名
        -->
        <package name="com.atguigu.dao"/>
    </mappers>

3.MyBatis的映射文件

3.1 select标签

<!--实现接口中的抽象方法-->
<!--Employee getById(Integer id);
    由于是查询,所以使用select标签
        id属性:指定实现方法的方法名
        parameterType属性:  参数类型   (MyBatis可以自行推断参数类型,所以可以省略)
        resultType属性:     自动映射: 处理结果类型(如果是自定义的,需要全类名)
        resultMap属性:      自定义映射(后面讲)
        flushCache属性:     刷新缓存(后面讲)
        useCache属性:       是否使用缓存(后面讲)
        databaseId属性      设置数据库的厂商标识
    标签体内编写sql语句即可
-->
<select id="getById" parameterType="int" resultType="employee" databaseId="oracle">
    select * from employees where id=#{abc}
</select>

3.2 insert/update/delete标签

<!--
    insert/update/delete
        id属性:  方法名
        parameterType属性:  参数类型(可以省略)
        flushCache属性:  刷新缓存(后面讲)
        useGeneratedKeys属性:  自动返回自增主键
        keyProperty属性:      设置自增主键到参数的哪个属性
        keyColumn属性:       如果主键不是第一列,设置列名
-->
<insert id="insert" parameterType="com.atguigu.bean.Employee" useGeneratedKeys="true" keyProperty="id">
    insert into employees values(null,#{lastName},#{email},#{gender},#{salary},#{deptId})
</insert><!-- Oracle自动返回自增主键 -->
<insert id="addEmployee" databaseId="oracle">
        <selectKey order="BEFORE" keyProperty="id" resultType="integer">
            select employee_seq.nextval from dual
        </selectKey>
        insert into oracle_employees(id,last_name,email,salary,dept_id)
        values(#{id},#{lastName},#{email},#{salary},#{deptId})
    </insert>
​
    <insert id="addEmployee" databaseId="oracle">
        <selectKey order="AFTER" keyProperty="id" resultType="integer">
            select employee_seq.currval from dual
        </selectKey>
        insert into oracle_employees(id,last_name,email,salary,dept_id)
        values(employee_seq.nextval,#{lastName},#{email},#{salary},#{deptId})
    </insert>

3.3 参数的传递方式

  • 单个普通参数

    • #{任意名字}
  • 多个普通参数

    • <!--List<Employee> findEmployeeByNameAndSalary(String name,Double salary);
          原理:将参数存放到Map集合内,一个参数放两次
              arg0-n
              param1-n
      -->
      <select id="findEmployeeByNameAndSalary" resultType="employee">
          select * from employees where last_name like concat('%',#{arg0},'%') and salary > #{param2}
      </select>
      
  • 命名参数

    • /**
       * 命名参数
       *      可以通过自定义的名字去取值
       *      原理:替换掉原来的arg系列,param系列不影响的
       */
      List<Employee> findEmployeeByNameAndSalary(@Param("name") String name,@Param("salary") Double salary);
      
  • javaBean

    • #{属性名}
  • Map集合

    • #{key值}
    • /**
       * Map集合
       *     映射文件通过key取值,并且没有arg或param相关的内容
       */
      List<Employee> findEmployeeByMap(Map map);
      

4.MyBatis缓存机制

  • 后期大概率采用redis(非关系型数据库)作为缓存框架

4.1 本地(一级)缓存

  • 默认开启,也关不掉

  • sqlSession级别的缓存

  • 本地缓存失效的情况

    • 不同的sqlSession对象
    • 查询条件不同
    • 两次查询中间做了增删改操作
    • sqlSession进行了提交
    • 手动清除一级缓存

4.2 二级缓存

  • 二级缓存默认是关闭的,如果想用的话需要开通

    • 总开关(默认是开启的)

      • settings

        • <setting name="cacheEnabled" value="true"/>
          
    • 分开关

      • 映射文件内添加标签cache

        • <!--二级缓存的开关-->
          <cache/>
          
  • 二级缓存的使用

    • 不同的SqlSession也是可以使用二级缓存,但是不同的SqlSession对象必须来自于同一个factory
    • 往二级缓存添加的数据必须序列化
    • 数据是在sqlSession.close的时候,才会添加

4.3 和缓存相关的配置

  • select标签

    • useCache 是否使用二级缓存

      • 默认是true
    • flushCache 是否清除缓存(一级和二级)

      • 默认值是false
  • insert/update/delete

    • flushCache 是否清除缓存(一级和二级)

      • 默认是true
  • 手动清除缓存只能清除一级,对二级无效

4.4 MyBatis如何整合第三方缓存框架

  • 导入第三方缓存框架的jar包

    • <!-- mybatis-ehcache -->
      <dependency>
          <groupId>org.mybatis.caches</groupId>
          <artifactId>mybatis-ehcache</artifactId>
          <version>1.0.3</version>
      </dependency><!-- slf4j-log4j12 -->
      <dependency>
          <groupId>org.slf4j</groupId>
          <artifactId>slf4j-log4j12</artifactId>
          <version>1.6.2</version>
          <scope>test</scope>
      </dependency>
      
  • 创建其配置文件

    • ehcache.xml

      • 文件名称必须是ehcache.xml
      • 内容从课件粘贴
  • 将缓存框架的核心类配置到映射文件内

    • <!--二级缓存的开关
          type属性:引入第三方缓存框架的核心类
      -->
      <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
      

5.逆向工程

  • 创建maven模块
  • 导入jar包
  • 准备数据库和表格
  • 创建实体类
  • 创建持久层接口
  • 创建映射文件
  • 创建核心配置文件
  • 测试

5.1 步骤

  • 导入jar包

    • <!-- mybatis-generator-core -->
      <dependency>
          <groupId>org.mybatis.generator</groupId>
          <artifactId>mybatis-generator-core</artifactId>
          <version>1.3.6</version>
      </dependency>
      
  • 创建逆向工程的配置文件

    • <!DOCTYPE generatorConfiguration PUBLIC
              "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
              "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
      <generatorConfiguration>
          <!--
              targetRuntime属性:设置逆向工程的模式
                  MyBatis3:  有基础的增删改查+QBC风格的查询
                  MyBatis3Simple:  有基础的增删改查
          -->
          <context id="simple" targetRuntime="MyBatis3Simple">
              <!--mysql8需要添加一个额外的配置-->
              <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                              connectionURL="jdbc:mysql://localhost:3306/ssm"
                              userId="root" password="root">
                  <property name="nullCatalogMeansCurrent" value="true" />
              </jdbcConnection>
              <!--实体类放在哪?-->
              <javaModelGenerator targetPackage="com.atguigu.mbg.bean" targetProject="src/main/java"/>
      ​
              <sqlMapGenerator targetPackage="com.atguigu.mbg.dao" targetProject="src/main/resources"/>
      ​
              <javaClientGenerator type="XMLMAPPER" targetPackage="com.atguigu.mbg.dao" targetProject="src/main/java"/>
      ​
              <table tableName="employees" />
              <table tableName="departments" />
          </context>
      </generatorConfiguration>
      
  • 执行逆向工程的程序

    • @Test
      public void test03(){
          try {
              List<String> warnings = new ArrayList<String>();
              boolean overwrite = true;
              File configFile = new File("src/main/resources/mbg.xml");
              ConfigurationParser cp = new ConfigurationParser(warnings);
              Configuration config = cp.parseConfiguration(configFile);
              DefaultShellCallback callback = new DefaultShellCallback(overwrite);
              MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
              myBatisGenerator.generate(null);
          } catch (IOException e) {
              e.printStackTrace();
          } catch (XMLParserException e) {
              e.printStackTrace();
          } catch (InvalidConfigurationException e) {
              e.printStackTrace();
          } catch (SQLException e) {
              e.printStackTrace();
          } catch (InterruptedException e) {
              e.printStackTrace();
          }
      }
      

5.2 测试

@Test
public void test01() throws Exception {
    InputStream input = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory build = new SqlSessionFactoryBuilder().build(input);
    SqlSession sqlSession = build.openSession();
​
    EmployeesMapper mapper = sqlSession.getMapper(EmployeesMapper.class);
    //QBC风格的条件
    //需求:查询部门id为2的员工信息
    EmployeesExample example=new EmployeesExample();
    //设置条件
    EmployeesExample.Criteria criteria = example.createCriteria();
    criteria.andDeptIdGreaterThanOrEqualTo(2);
​
    //需求:查询部门id大于等于2 并且 名字不为null
    criteria.andLastNameIsNotNull();
​
    //需求:(查询部门id大于等于2 并且 名字不为null ) 或者 工资大于15000
    EmployeesExample.Criteria criteria1 = example.createCriteria();
    criteria1.andSalaryGreaterThan(15000D);
    //设置两个Criteria对象是或者关系
    example.or(criteria1);
​
    List<Employees> employees = mapper.selectByExample(example);
    employees.forEach(System.out::println);
​
    sqlSession.commit();
    sqlSession.close();
}

6.分页插件

  • 功能:帮助我们进行分页

  • 步骤

    • 导入jar包

      • <!-- pagehelper -->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>5.0.0</version>
        </dependency>
        
    • 配置分页插件

      • 位置:核心配置文件

      • 代码

        • <!--配置分页插件-->
              <plugins>
                  <!--
                      4版本:com.github.pagehelper.PageHelper
                      5版本:com.github.pagehelper.PageInterceptor
                  -->
                  <plugin interceptor="com.github.pagehelper.PageInterceptor">
                      <!--配置方言:就是设置连接的数据库-->
          <!--            <property name="helperDialect" value="Mysql"/>-->
                  </plugin>
              </plugins>
          
    • 使用

      • //① 在查询之前,设置分页的信息
        Page page = PageHelper.startPage(6, 2);
        //② 查询
        List<Employees> employees = mapper.selectByExample(example);
        employees.forEach(System.out::println);
        //③ Page对象必须在查询之后使用
        int pageNum = page.getPageNum();
        System.out.println("pageNum = " + pageNum);
        ​
        int pageSize = page.getPageSize();
        System.out.println("pageSize = " + pageSize);
        ​
        long total = page.getTotal();
        System.out.println("total = " + total);//总条数
        ​
        int pages = page.getPages();
        System.out.println("pages = " + pages);//总页数//④ PageInfo对象,内容就比较多  ★
            //PageInfo对象中有查询结果,有当前页的所有信息
        PageInfo pageInfo=new PageInfo(employees,3);
        ​
        int prePage = pageInfo.getPrePage();
        System.out.println("prePage = " + prePage);
        ​
        int nextPage = pageInfo.getNextPage();
        System.out.println("nextPage = " + nextPage);
        ​
        boolean hasPreviousPage = pageInfo.isHasPreviousPage();
        System.out.println("hasPreviousPage = " + hasPreviousPage);
        ​
        boolean isFirstPage = pageInfo.isIsFirstPage();
        System.out.println("isFirstPage = " + isFirstPage);
        ​
        boolean hasNextPage = pageInfo.isHasNextPage();
        System.out.println("hasNextPage = " + hasNextPage);
        ​
        boolean isLastPage = pageInfo.isIsLastPage();
        System.out.println("isLastPage = " + isLastPage);
        ​
        int startRow = pageInfo.getStartRow();
        System.out.println("startRow = " + startRow);
        ​
        int endRow = pageInfo.getEndRow();
        System.out.println("endRow = " + endRow);
        ​
        int[] navigatepageNums = pageInfo.getNavigatepageNums();
        System.out.println(Arrays.toString(navigatepageNums));