您当前的位置: 首页 >  mybatis

星许辰

暂无认证

  • 0浏览

    0关注

    466博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

MyBatis3——入门介绍

星许辰 发布时间:2021-09-16 20:23:02 ,浏览量:0

目录
  • 1.MyBatis简介
  • 2.MyBatis——入门案例
  • 3.MyBatis——全局配置文件常用标签
    • (1)properties属性
    • (2)settings设置
    • (3)typeAliases别名处理器
    • (4)typeHandlers类型处理器
    • (5)plugins插件
    • (6)environments环境
    • (7)databaseIdProvider环境
    • (7)mapper映射
  • 4.MyBatis——映射文件
    • (1)简单的增删改查
    • (2)获取自增主键的值
    • (3)获取非自增主键的值(Oracle)
    • (4)参数处理——单个参数
    • (5)参数处理——多个参数
    • (6)参数处理——命名参数
    • (7)参数处理——POJO & Map & TO
    • (8)参数处理——参数封装扩展思考
    • (9)参数处理——#与&取值的区别
    • (10)Select
      • ① 返回值类型为List
      • ② 记录封装map
      • ③ resultMap——自定义结果映射规则
      • ④ resultMap——关联查询_环境搭建
      • ⑤ resultMap——级联属性封装结果
      • ⑥ resultMap——association定义关联对象封装规则
      • ⑦ resultMap——association分步查询 & 延迟加载
      • ⑧ resultMap——collection定义关联集合封装规则
      • ⑨ resultMap——collection分步查询 & 延迟加载
      • ⑩ resultMap——discriminator鉴别器
  • 5.MyBatis——动态SQL
    • (1)简介 & 环境搭建
    • (2)动态SQL标签——if
    • (3)动态SQL标签——where
    • (3)动态SQL标签——trim
    • (4)动态SQL标签——choose
    • (5)动态SQL标签——set(与if结合的动态更新)
    • (6)动态SQL标签——foreach遍历集合
    • (7)动态SQL标签——mysql下foreach批量插入的两种方式
    • (8)动态SQL标签——内置参数_parameter和_databaseId
    • (9)动态SQL标签——bind绑定
    • (10)动态SQL标签——抽取可重用的sql片段
  • 6.MyBatis——缓存机制
    • (1)缓存介绍
      • ① 一级缓存
      • ② 二级缓存
    • (2)一级缓存体验
    • (3)一级失效的四种情况
    • (4)二级缓存的使用
    • (5)缓存有关的设置/属性
    • (6)缓存原理图示
    • (7)第三方缓存整合——ehcache
      • ① 导入ehcache适配包
      • ② 在config目录下添加ehcache.xml文件
      • ③ 在EmployeeMapper.xml文件中进行配置
      • ④ 测试
      • ⑤ 扩展——引用缓存
  • 7.MyBatis——SSM框架整合
    • (1)在IDEA中新建一个Java Web项目
    • (2)导入需要的jar包
    • (3)进行各种配置以及代码的编写
    • (4)测试
  • 8.MyBatis——逆向工程
    • (1)MyBatis Generator
    • (2)编写相应的配置文件及代码
  • 9.MyBatis——工作原理
    • (1)框架分层架构
    • (2)入门案例的运行流程
  • 10.MyBatis——插件开发
    • (1)插件原理
    • (2)编写插件
    • (3)多个插件运行流程
    • (4)开发插件
    • (5)PageHelper分页插件的使用
  • 11.Mybatis扩展
    • (1)批量操作
    • (2)自定义类型处理器——MyBatis中枚举类型的默认处理
    • (3)自定义类型处理器——使用自定义的类型处理器处理枚举类型

本文章的笔记整理来自尚硅谷视频https://www.bilibili.com/video/BV1mW411M737

1.MyBatis简介

(1)MyBatis历史 原是Apache的一个开源项目iBatis, 2010年6月这个项目由Apache Software Foundation 迁移到了Google Code,随着开发团队转投Google Code旗下, iBatis3.x正式更名为MyBatis ,代码于2013年11月迁移到Github。iBatis一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。 iBatis提供的持久层框架包括SQL Maps和Data Access Objects(DAO)。 (2)MyBatis特点 ① MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的半自动化持久层框架。 ② MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。 ③ MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录。 (3)MyBatis与其它对应框架或组件的对比

MyBatis半自动化的持久化层框架JDBC① SQL夹在Java代码块里,耦合度高导致硬编码内伤。② 维护不易且实际开发需求中sql是有变化,频繁修改的情况多见。Hibernate和JPA① 长难复杂SQL,对于Hibernate而言处理也不容易内部自动生产的SQL,不容易做特殊优化。② 基于全映射的全自动框架,大量字段的POJO进行部分映射时比较困难,这会导致数据库性能下降。

总之,对开发人员而言,核心sql还是需要自己来优化的。除此之外,如果sql代码和java代码分开,那么它们的功能边界就更加清晰,一个专注业务、一个专注数据。

(4)MyBatis地址为https://github.com/mybatis/mybatis-3/

2.MyBatis——入门案例

(1)创建测试表tbl_employee并添加一条测试数据,表结构及数据如下:

在这里插入图片描述 在这里插入图片描述

(2)在IDEA中创建一个Java项目,并在项目的根目录下创建lib目录(用于存放要导入的 jar 包)和config目录(用于存放配置文件),另外,需要右键点击config目录,找到Mark Directoy as并选择Resources Root。

在这里插入图片描述 (3)导入相关的 jar 包并引入到项目中

在这里插入图片描述 在这里插入图片描述 (4)Mybatis入门案例项目的整体目录结构如下:

在这里插入图片描述

各个部分的具体代码如下: ① 日志配置文件log4j.xml


DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
 

 
 
   
   
    
   
 
 
   
 
 
   
 
 
   
   
 

② 与数据库中的表tbl_employee对应的实体类Employee.java

package com.atguigu.mybatis.bean;
import org.apache.ibatis.type.Alias;

//@Alias("别名"):单独为Employee起别名
//@Alias("emp")
public class Employee {
    //属性名称应该与对应数据库表(tbl_employee)中的字段一致
    private Integer id;
    private String last_name;
    private String email;
    private String gender;
    
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    
    public String getLast_name() {
        return last_name;
    }
    
    public void setLast_name(String last_name) {
        this.last_name = last_name;
    }
    
    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=" + last_name + ", email="
                + email + ", gender=" + gender + "]";
    }
}

③ Mapper接口EmployeeMapper.java

package com.atguigu.mybatis.dao;

import com.atguigu.mybatis.bean.Employee;

public interface EmployeeMapper {
    
    public Employee getEmpById(Integer id);
}

④ sql映射文件EmployeeMapper.xml


DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">


	
    
    
		select * from tbl_employee where id = #{id}
	

⑤ 数据库配置文件dbconfig.properties

jdbc.drive=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf8
jdbc.username=root
jdbc.password=123456

⑥ mybatis的全局配置文件mybatis-config.xml


DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">



    

    
        
            
            
                
                
                
                
            
        
    
    
    
    
        
    

⑦ 测试代码

package com.atguigu.mybatis.test;

import com.atguigu.mybatis.bean.Employee;
import com.atguigu.mybatis.dao.EmployeeMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;

public class MyBatisTest {
    
    //根据全局配置文件mybatis-config.xml,利用SqlSessionFactoryBuilder创建SqlSessionFactory
    public SqlSessionFactory getSqlSessionFactory() throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        return new SqlSessionFactoryBuilder().build(inputStream);
    }
    
    /*
     * 1、根据xml配置文件(全局配置文件)创建一个SqlSessionFactory对象,其包含数据源等一些运行环境信息
     * 2、sql映射文件;配置了每一个sql,以及sql的封装规则等。
     * 3、将sql映射文件注册在全局配置文件中
     * 4、写代码:
     * 		1)、根据全局配置文件得到SqlSessionFactory;
     * 		2)、使用sqlSession工厂,获取到sqlSession对象使用他来执行增删改查
     * 			一个sqlSession就是代表和数据库的一次会话,用完关闭
     * 		3)、使用sql的唯一标志来告诉MyBatis执行哪个sql。sql都是保存在sql映射文件中的。
     *
     * @throws IOException
     */
     
    //旧版本常用方式
    @Test
    public void test01() throws IOException {
        // 获取sqlSession实例,能直接执行已经映射的sql语句
        // sql的唯一标识:statement Unique identifier matching the statement to use.
        // 执行sql要用的参数:parameter A parameter object to pass to the statement.
        
		//使用SqlSessionFactory获取sqlSession对象,一个SqlSession对象代表和数据库的一次会话
        SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
        SqlSession openSession = sqlSessionFactory.openSession();
        try {
            Employee employee = openSession.selectOne(
                    "com.atguigu.mybatis.dao.EmployeeMapper.getEmpById", 1);
            System.out.println(employee);
        } finally {
            openSession.close();
        }
    }
    
    //新版本常用的方式:接口式编程
    /*
     * 1、接口式编程
     * 	原生:		Dao		====>  DaoImpl
     * 	mybatis:	Mapper	====>  xxMapper.xml
     *
     * 2、SqlSession代表和数据库的一次会话;用完必须关闭;
     * 3、SqlSession和connection一样她都是非线程安全。每次使用都应该去获取新的对象。
     * 4、mapper接口没有实现类,但是mybatis会为这个接口生成一个代理对象。
     * 		(将接口和xml进行绑定)
     * 		EmployeeMapper empMapper =	sqlSession.getMapper(EmployeeMapper.class);
     * 5、两个重要的配置文件:
     * 		mybatis的全局配置文件:包含数据库连接池信息,事务管理器信息等...系统运行环境信息
     * 		sql映射文件:保存了每一个sql语句的映射信息:将sql抽取出来。
     *
     */
    @Test
    public void test02() throws IOException {
        // 1、获取sqlSessionFactory对象
        SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
        // 2、获取sqlSession对象
        SqlSession openSession = sqlSessionFactory.openSession();
        try {
            // 3、获取接口的实现类对象
            //会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
            EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
            Employee employee = mapper.getEmpById(1);
            System.out.println(mapper.getClass());
            System.out.println(employee);
        }finally{
            openSession.close();
        }
    }
}

结果如下: 在这里插入图片描述

3.MyBatis——全局配置文件常用标签 (1)properties属性

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

# 数据库配置文件dbconfig.properties
jdbc.drive=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf8
jdbc.username=root
jdbc.password=123456




    
        
        
            
            
            
            
        
    

(2)settings设置

settings设置是MyBatis 中极为重要的调整设置,它们会改变MyBatis 运行时的行为。 在这里插入图片描述 例如开启自动驼峰命名规则映射:


    
    

(3)typeAliases别名处理器

typeAliases:别名处理器,它可以为java类型起别名(别名不区分大小写)


    
    

    
    

    

除此之外,MyBatis已经为Java中一些常见的数据类型起好了别名,在开发时直接使用即可。 在这里插入图片描述

(4)typeHandlers类型处理器

无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型。 在这里插入图片描述

(5)plugins插件

插件是MyBatis提供的一个非常强大的机制,我们可以通过插件来修改MyBatis的一些核心行为。插件通过动态代理机制,可以介入四大对象的任何一个方法的执行。后面会有专门的章节来介绍mybatis运行原理以及插件。 • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed) • ParameterHandler (getParameterObject, setParameters) • ResultSetHandler (handleResultSets, handleOutputParameters) • StatementHandler (prepare, parameterize, batch, update, query)

(6)environments环境

MyBatis可以配置多种环境,例如开发、测试和生产环境需要有不同的配置。每种环境使用一个environment标签进行配置并指定唯一标识符。可以通过environments标签中的default属性指定一个环境的标识符来快速地切换环境。 ① environment——指定具体的环境:

id指定当前环境的唯一标识transactionManager、dataSource分别表示事务管理、数据源,必须存在于environment中

    
        
        
            
            
            
            
        
    

② transactionManager——事务管理

typeJDBC、MANAGED、自定义JDBC使用了 JDBC 的提交和回滚设置,依赖于从数据源得到的连接来管理事务范围MANAGED不提交或回滚一个连接、让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)自定义实现TransactionFactory接口,type=全类名/别名

③ dataSource——数据源

typeUNPOOLED、POOLED、JNDI、自定义UNPOOLED不使用连接池POOLED使用连接池JNDI在EJB 或应用服务器这类容器中查找指定的数据源自定义实现DataSourceFactory接口,定义数据源的获取方式注:在实际开发中,一般使用Spring管理数据源,并进行事务控制的配置来覆盖上述配置。 (7)databaseIdProvider环境

MyBatis 可以根据不同的数据库厂商执行不同的语句。



    
    
    
    

在sql映射文件中编写具体的sql语句时指定需要操作的数据库(当然这需要提前导入相应的jar包以及配置好数据库环境、数据源信息等,这样才能顺利切换需要操作的数据库)。



	select * from tbl_employee where id = #{id}

MyBatis匹配规则如下: ① 如果没有配置databaseIdProvider标签,那么databaseId=null; ② 如果配置了databaseIdProvider标签,使用标签配置的name去匹配数据库信息,匹配上设置databaseId=配置指定的值,否则依旧为null; ③ 如果databaseId不为null,他只会找到配置databaseId的sql语句; ④ MyBatis 会加载不带 databaseId 属性和带有匹配当前数据库databaseId 属性的所有语句。如果同时找到带有 databaseId 和不带databaseId 的相同语句,则后者会被舍弃;

(7)mapper映射

mappers将写好的sql映射文件注册到全局配置中。


    
    
    
    
    
    

注:当使用mapper中的class属性来注册接口时,不需要映射文件并且sql语句利用注解写在接口上的例子如下:

package com.atguigu.mybatis.dao;

import com.atguigu.mybatis.bean.Employee;
import org.apache.ibatis.annotations.Select;

public interface EmployeeMapperAnnotation {
    
    //直接在注解中编写sql语句,不要sql映射文件
    @Select("select * from tbl_employee where id = #{id}")
    public Employee getEmpById(Integer id);
}

以上介绍了MyBatis中常用的标签,所有的标签如下图所示。不过在使用这些标签时也需要按照下图中从上到下规定的顺序(某个标签可以没有,但如果有则顺序不能颠倒,否则会报错!!!) 在这里插入图片描述

4.MyBatis——映射文件

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

(1)简单的增删改查

① 在接口EmployeeMapper中编写抽象的增删改查方法

package com.atguigu.mybatis.dao;

import com.atguigu.mybatis.bean.Employee;

public interface EmployeeMapper {    
    Employee getEmpById(Integer id);    
    void addEmp(Employee employee);    
    boolean updateEmp(Employee employee);
    void deleteEmpById(Integer id);
}

② 在对应的sql映射文件EmployeeMapper.xml中编写sql语句


DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">


    
    
		select * from tbl_employee where id = #{id}
	

    
    
        insert into tbl_employee(last_name,email,gender)
        values (#{last_name},#{email},#{gender})
    

    
        update tbl_employee
        set last_name=#{last_name},email=#{email},gender=#{gender}
        where id=#{id}
    

    
        delete from tbl_employee where id=#{id}
    

③ 测试

/*
     * 测试增删改
     * 1、mybatis允许增删改直接定义以下类型返回值
     * 		Integer、Long、Boolean、void
     * 2、我们需要手动提交数据
     * 		sqlSessionFactory.openSession();===》手动提交
     * 		sqlSessionFactory.openSession(true);===》自动提交
     */
    @Test
    public void test04() throws IOException {
        // 1、获取sqlSessionFactory对象
        SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
        // 2、获取sqlSession对象
        SqlSession openSession = sqlSessionFactory.openSession();
        try {
            // 3、获取接口的实现类对象
            //会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
            EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
            
            //添加数据
            Employee employee1 = new Employee(null, "Jerry", "1","Jerry@163.com");
            mapper.addEmp(employee1);
            
            //修改数据
            Employee employee2 = new Employee(2, "Hi", "1","Hi@163.com");
            //若修改影响的行数大于等于1,则返回true,否则返回false
            boolean flag = mapper.updateEmp(employee2);
            System.out.println(flag);
            
            //删除数据
            mapper.deleteEmpById(2);
            
            //手动提交数据
            openSession.commit();
        }finally{
            openSession.close();
        }
    }

除此之外,insert、update、delete标签中常见的属性如下: 在这里插入图片描述

(2)获取自增主键的值


    insert into tbl_employee(last_name,email,gender)
    values (#{last_name},#{email},#{gender})

//添加数据
Employee employee = new Employee(null, "Jerry", "1","Jerry@163.com");
mapper.addEmp(employee);
//输出得到的自增主键的值
System.out.println(employee.getId());
(3)获取非自增主键的值(Oracle)


	
	
		
		
		select EMPLOYEES_SEQ.nextval from dual 
		
	
	
	
	
	insert into employees(EMPLOYEE_ID,LAST_NAME,EMAIL) 
	values(#{id},#{lastName},#{email}) 
	

selectKey标签的相关属性如下: 在这里插入图片描述

(4)参数处理——单个参数

对于单个参数,mybatis不会做特殊处理,即按照 #{参数名/任意名} 就可以取出参数值。



    delete from tbl_employee where id=#{id}
    

(5)参数处理——多个参数

对于多个参数,mybatis会做特殊处理,即多个参数会被封装成一个map,其中key为param1…paramN(或者参数的索引),value为传入的参数值,#{x}就等于从map中获取 key 为 x 的value的值。 ① 定义含有2个参数的抽象方法:

Employee getEmpByIdAndLast_name(Integer id,String last_name);

② 错误示例:按照单个参数的方式进行取值


    select * from tbl_employee where id=#{id} and last_name=#{last_name}

@Test
public void test05() throws IOException {
    // 1、获取sqlSessionFactory对象
    SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    // 2、获取sqlSession对象
    SqlSession openSession = sqlSessionFactory.openSession();
    try {
        // 3、获取接口的实现类对象
        //会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
        EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
        Employee employee = mapper.getEmpByIdAndLast_name(1, "Tom");
        System.out.println(employee);
        openSession.commit();
    }finally{
        openSession.close();
    }
}

错误提示如下:

### Error querying database.  Cause: org.apache.ibatis.binding.BindingException: Parameter 'id' not found. Available parameters are [0, 1, param1, param2]
### Cause: org.apache.ibatis.binding.BindingException: Parameter 'id' not found. Available parameters are [0, 1, param1, param2]

③ 正确示例


    select * from tbl_employee where id=#{param1} and last_name=#{param2}
    

(6)参数处理——命名参数

对于多个参数的处理还有另外一种方法,即命名参数,使用注解@Param明确指定封装参数时map的key。

Employee getEmpByIdAndLast_name(@Param("id") Integer id,@Param("last_name") String last_name);

    select * from tbl_employee where id=#{id} and last_name=#{last_name}

(7)参数处理——POJO & Map & TO

① 如果多个参数正好是我们业务逻辑的数据模型,那么就可以直接传入对应的POJO(Plain Old Java Objects,普通的Java对象),然后按照 #{属性名} 的方式取出传入的POJO的属性值。

 boolean updateEmp(Employee employee);

    update tbl_employee
    set last_name=#{last_name},email=#{email},gender=#{gender}
    where id=#{id}

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

Employee getEmpByMap(Map map);

    select * from tbl_employee where id=#{id} and last_name=#{last_name}

@Test
public void test05() throws IOException {
    // 1、获取sqlSessionFactory对象
    SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    // 2、获取sqlSession对象
    SqlSession openSession = sqlSessionFactory.openSession();
    try {
        // 3、获取接口的实现类对象
        //会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
        EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
        Map map = new HashMap();
        map.put("id",1);
        map.put("last_name","Tom");
        Employee employee = mapper.getEmpByMap(map);
        System.out.println(employee);
        openSession.commit();
    }finally{
        openSession.close();
    }
}

③ 如果多个参数不是业务模型中的数据,但是经常要使用,推荐来编写一个TO(Transfer Object)来数据传输对象。 例如对于数据的分页处理,我们可以单独定义一个Page类,里面自定义需要的属性,其用法与POJO类似。

(8)参数处理——参数封装扩展思考 public Employee getEmp(@Param(“id”)Integer id,String last_name);取值:id==>#{id/param1} last_name==>#{param2}public Employee getEmp(Integer id, @Param(“e”)Employee emp);取值:id==>#{param1} last_name===>#{param2.last_name/e.last_name}如果是Collection(List、Set)类型或者是数组,也会特殊处理,也是把传入的list或者数组封装在map中key:Collection(collection)如果是List数组使用这个key(list)数组(array)public Employee getEmpById(List ids);取值:取出第一个id的值: #{list[0]} (9)参数处理——#与&取值的区别

① #{ } 与 ${ }都可以获取map中的值或者pojo对象属性的值。 ② 在具体的细节上#{ } 与 ${ }有一些不同:

#{ }是以预编译的形式,将参数设置到 sql 语句中,可以防止 sql 注入${ }取出的值直接拼装在 sql 语句中,可能会存在安全问题

在这里插入图片描述 ③ 在大多情况下,我们应该使用 #{ } 去取参数的值,但是在一些特殊情况下需要使用 ${ }:

# 在原生JDBC不支持占位符的地方,就可以使用${}进行取值,例如分表、排序等
select * from ${year}_salary where xxx;
select * from ${tbl_name} where id=${id} and last_name=#{last_name};
(10)Select

Select标签用来定义查询操作,常用的属性如下:

id唯一标识符,用来引用这条语句,需要和接口的方法名一致parameterType参数类型,可以不传,MyBatis会根据TypeHandler自动推断resultType返回值类型,别名或者全类名,如果返回的是集合,定义集合中元素的类型,不能和resultMap同时使用其余属性如下:在这里插入图片描述 ① 返回值类型为List
List getEmpsByLast_NameLike(String last_name);


    select * from tbl_employee where last_name like #{last_name}

@Test
public void test06() throws IOException {
    // 1、获取sqlSessionFactory对象
    SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    // 2、获取sqlSession对象
    SqlSession openSession = sqlSessionFactory.openSession();
    try {
        // 3、获取接口的实现类对象
        //会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
        EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
        //查询姓名中含有字母e的所有员工
        List emps = mapper.getEmpsByLast_NameLike("%e%");
        System.out.println(emps);
        openSession.commit();
    }finally{
        openSession.close();
    }
}
② 记录封装map

1)返回一条记录的map,key为列名,value为对应的值

Map getEmpByIdReturnMap(Integer id);

    select * from tbl_employee where id=#{id}

@Test
public void test06() throws IOException {
    // 1、获取sqlSessionFactory对象
    SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    // 2、获取sqlSession对象
    SqlSession openSession = sqlSessionFactory.openSession();
    try {
        // 3、获取接口的实现类对象
        //会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
        EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
        Map map = mapper.getEmpByIdReturnMap(1);
        System.out.println(map);
        openSession.commit();
    }finally{
        openSession.close();
    }
}

2)多条记录封装一个map:Map,键是这条记录的主键,值是记录封装后的javaBean

//@MapKey:告诉mybatis封装这个map的时候使用哪个属性作为map的key
@MapKey("id")
Map getEmpByLast_NameLikeReturnMap(String last_name);

    select * from tbl_employee where last_name like #{last_name}

@Test
public void test06() throws IOException {
    // 1、获取sqlSessionFactory对象
    SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    // 2、获取sqlSession对象
    SqlSession openSession = sqlSessionFactory.openSession();
    try {
        // 3、获取接口的实现类对象
        //会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
        EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
        Map map = mapper.getEmpByLast_NameLikeReturnMap("%r%");
        System.out.println(map);
        openSession.commit();
    }finally{
        openSession.close();
    }
}
③ resultMap——自定义结果映射规则
package com.atguigu.mybatis.dao;

import com.atguigu.mybatis.bean.Employee;

public interface EmployeeMapperPlus{

    Employee getEmpById(Integer id);
}

注:记得要在MyBatis全局配置文件(mybatis-config.xml)中注册新的sql映射文件!


DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

    
    
        
        
        
        
        
        
        
    

    
    
        select  * from tbl_employee where id=#{id}
    

④ resultMap——关联查询_环境搭建

1)创建一张新的表 tbl_dept

create table tbl_dept(
	id INT(11) primary key AUTO_INCREMENT,
	departmentName VARCHAR(255)
)

2)为表 tbl_employee 加上一列,用来存储员工所在的部门id

alter table tbl_employee add COLUMN d_id INT(11);

3)将表 tbl_employee 中的字段d_id(员工所在的部门id)设置为外键

ALTER TABLE tbl_employee add CONSTRAINT fk_emp_dept FOREIGN key(d_id) REFERENCES tbl_dept(id);

4)创建部门类Department

package com.atguigu.mybatis.bean;

public class Department {
    
    private Integer id;
    private String departmentName;
    
    public Integer getId() {
        return id;
    }
    
    public void setId(Integer id) {
        this.id = id;
    }
    
    public String getDepartmentName() {
        return departmentName;
    }
    
    public void setDepartmentName(String departmentName) {
        this.departmentName = departmentName;
    }
    
    @Override
    public String toString() {
        return "Department{" +
                "id=" + id +
                ", departmentName='" + departmentName + '\'' +
                '}';
    }
}

5)在Employee.java中添加新的属性(即员工所在的部门Department),并添加对应的 get/set 方法

private Department dept;

public Department getDept() {
    return dept;
}

public void setDept(Department dept) {
    this.dept = dept;
}
⑤ resultMap——级联属性封装结果
//EmployeeMapperPlus.java
Employee getEmpAndDept(Integer id);




    
    
    
    
    
    



    SELECT e.id id,e.last_name last_name,e.gender gender,e.email email,e.d_id d_id,
           d.id did,d.deptName deptName
    FROM tbl_employee e,tbl_dept d
    WHERE e.d_id=d.id AND e.id=#{id}

@Test
public void test06() throws IOException {
    // 1、获取sqlSessionFactory对象
    SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    // 2、获取sqlSession对象
    SqlSession openSession = sqlSessionFactory.openSession();
    try {
        // 3、获取接口的实现类对象
        //会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
        EmployeeMapperPlus mapper = openSession.getMapper(EmployeeMapperPlus.class);
        Employee employee = mapper.getEmpAndDept(1);
        System.out.println(employee);
        System.out.println(employee.getDept());
        openSession.commit();
    }finally{
        openSession.close();
    }
}
⑥ resultMap——association定义关联对象封装规则


    
    
    
    
    
    
        
        
    

⑦ resultMap——association分步查询 & 延迟加载

1)新建DepartmentMapper.java,并定义抽象方法getDeptById()。

package com.atguigu.mybatis.dao;

import com.atguigu.mybatis.bean.Department;

public interface DepartmentMapper {
    //根据部门id查询部门信息
    Department getDeptById(Integer id);
}

2)新建对应的sql映射文件(记得要在MyBatis全局配置文件(mybatis-config.xml)中注册)。


DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">


    
    
        select id,deptName from tbl_dept where id=#{id}
    

3)在EmployeeMapperPlus.java中增加抽象方法getEmpByIdStep()。

//EmployeeMapperPlus.java
Employee getEmpByIdStep(Integer id);

4)在EmployeeMapperPlus.xml中编写对应的sql语句。





    
    
    
    
    
    
    



    select * from tbl_employee where id=#{id}

5)测试

@Test
public void test06() throws IOException {
    // 1、获取sqlSessionFactory对象
    SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    // 2、获取sqlSession对象
    SqlSession openSession = sqlSessionFactory.openSession();
    try {
        // 3、获取接口的实现类对象
        //会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
        EmployeeMapperPlus mapper = openSession.getMapper(EmployeeMapperPlus.class);
        //分步查询
        Employee employee = mapper.getEmpByIdStep(1);
        System.out.println(employee);
        System.out.println(employee.getDept());
        openSession.commit();
    }finally{
        openSession.close();
    }
}

6)延迟加载——在MyBatis全局配置文件(mybatis-config.xml)中加上两个设置 在这里插入图片描述


    
    
    
    
    
    
    
    
    

⑧ resultMap——collection定义关联集合封装规则

1)在Department.java中添加属于该部门的所有员工的集合List,并添加相应的 get/set 方法。

package com.atguigu.mybatis.bean;

import java.util.List;

public class Department {
    
    private Integer id;
    private String deptName;
    private List emps;
    
    public List getEmps() {
        return emps;
    }
    
    public void setEmps(List emps) {
        this.emps = emps;
    }
    
    public Integer getId() {
        return id;
    }
    
    public void setId(Integer id) {
        this.id = id;
    }
    
    
    public String getDeptName() {
        return deptName;
    }
    
    public void setDeptName(String deptName) {
        this.deptName = deptName;
    }
    
    @Override
    public String toString() {
        return "Department{" +
                "id=" + id +
                ", deptName='" + deptName + '\'' +
                '}';
    }
}

2)在EmployeeMapperPlus.java中添加抽象方法getDeptByIdPlus()。

//EmployeeMapperPlus.java
//通过部门id查询部门信息,以及属于该部门的所有员工的集合List
Department getDeptByIdPlus(Integer id);

3)在DepartmentMapper.xml中编写上述方法对应的sql语句。




    
    
    
    
        
        
        
        
        
    



    select d.id did,d.deptName deptName,
           e.id eid,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};

4)测试

 @Test
public void test07() throws IOException {
    // 1、获取sqlSessionFactory对象
    SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    // 2、获取sqlSession对象
    SqlSession openSession = sqlSessionFactory.openSession();
    try {
        // 3、获取接口的实现类对象
        //会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
        DepartmentMapper mapper = openSession.getMapper(DepartmentMapper.class);
        //查询部门id为1的部门信息,以及属于该部门的所有员工信息(在list中)
        Department dept = mapper.getDeptByIdPlus(1);
        //输出部门信息
        System.out.println(dept);
        //输出属于该部门的所有员工信息
        System.out.println(dept.getEmps());
        openSession.commit();
    }finally{
        openSession.close();
    }
}

在这里插入图片描述

⑨ resultMap——collection分步查询 & 延迟加载

1)在EmployeeMapperPlus.java中添加抽象方法getEmpsByDeptId()。

//EmployeeMapperPlus.java
//根据部门id查询所有属于该部门的员工信息
List getEmpsByDeptId(Integer deptId);

2)在EmployeeMapperPlus.xml中编写对应的sql语句。



    select * from tbl_employee where d_id=#{deptId};

3)在DepartmentMapper.java中添加抽象的分步查询方法getDeptByIdStep()。

//DepartmentMapper.java
//通过部门id查询部门信息,以及属于该部门的所有员工的集合List(分步查询)
Department getDeptByIdStep(Integer id);

4)在DepartmentMapper.xml中编写对应的sql语句。



    
    
    



    select id,deptName from tbl_dept where id=#{id}

5)扩展——传递多列值 在collection分步查询时,其中column表示需要传递的参数,当只有一个参数时,直接写进去即可: 在这里插入图片描述 当需要传递多个参数时,选需要将多列的值封装在map中进行传递,column=“{key1=column1,key2=column2}”: 在这里插入图片描述 6)扩展——延迟加载 fetchType=“lazy”:表示使用延迟加载,fetchType=“eager”:表示使用立即加载。(该设置可以覆盖掉全局配置文件中的延迟加载) 测试代码:

@Test
public void test07() throws IOException {
    // 1、获取sqlSessionFactory对象
    SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    // 2、获取sqlSession对象
    SqlSession openSession = sqlSessionFactory.openSession();
    try {
        // 3、获取接口的实现类对象
        //会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
        DepartmentMapper mapper = openSession.getMapper(DepartmentMapper.class);
        Department dept = mapper.getDeptByIdStep(1);
        System.out.println(dept.getDeptName());
        System.out.println(dept.getEmps());
        openSession.commit();
    }finally{
        openSession.close();
    }
}

测试对比: 在这里插入图片描述 在这里插入图片描述

⑩ resultMap——discriminator鉴别器

在EmployeeMapper.xml中添加鉴别器




    
    
    
    
    
    
        
        
            
            
        
        
        
            
            
            
            
        
    

5.MyBatis——动态SQL (1)简介 & 环境搭建

① 动态 SQL是MyBatis强大特性之一,它极大地简化拼装SQL的操作,动态 SQL 元素和使用 JSTL 或其他类似基于 XML 的文本处理器相似。此外,MyBatis 采用了功能强大的基于 OGNL 的表达式来简化操作。 ② 环境搭建 新建EmployeeMapperDynamicSQL.java

package com.atguigu.mybatis.dao;

public interface EmployeeMapperDynamicSQL {

}

新建EmployeeMapperDynamicSQL.xml(记得要在全局配置文件中注册该sql映射文件!)


DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

    

(2)动态SQL标签——if

① 在EmployeeMapperDynamicSQL.java中添加抽象方法

//携带了哪个字段查询条件就带上这个字段的值
List getEmpsByConditionIf(Employee employee);

② 在EmployeeMapperDynamicSQL.xml中编写对应的sql语句



    select * from tbl_employee
    where 1=1
    
    
        and id=#{id}
    
    
        and last_name like #{last_name}
    
    
        and email=#{email}
    
    
    
        and gender=#{gender}
    

③ 测试

@Test
public void testDynamicSQL() throws IOException {
    // 1、获取sqlSessionFactory对象
    SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    // 2、获取sqlSession对象
    SqlSession openSession = sqlSessionFactory.openSession();
    try {
        // 3、获取接口的实现类对象
        //会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
        EmployeeMapperDynamicSQL mapper = openSession.getMapper(EmployeeMapperDynamicSQL.class);
        Employee employee = new Employee(null,"%e%",null,null);
        List emps = mapper.getEmpsByConditionIf(employee);
        System.out.println(emps);
        openSession.commit();
    }finally{
        openSession.close();
    }
}

在这里插入图片描述

(3)动态SQL标签——where

查询的时候如果某些条件没带可能会出现sql拼装问题,现有以下两种解决方案: ① 给where后面加上1=1,以后的条件都是and xxx(上面(2)中的代码就是这样写的)。 ② MyBatis使用 where 标签来将所有的查询条件包括在内。即在where标签拼装sql语句,并且会去掉多出来的and或者or,但是where只会去掉第一个多出来的and或者or。



    select * from tbl_employee
    
        
            id=#{id}
        
        
            and last_name like #{last_name}
        
        
            and email=#{email}
        
        
        
            and gender=#{gender}
        
    

(3)动态SQL标签——trim

① 在EmployeeMapperDynamicSQL.java中添加抽象方法

List getEmpsByConditionTrim(Employee employee);

② 在EmployeeMapperDynamicSQL.xml中编写对应的sql语句



    select * from tbl_employee
    
    
    
        
            id=#{id} and
        
        
            last_name like #{last_name} and
        
        
            email=#{email} and
        
        
        
            gender=#{gender}
        
    

(4)动态SQL标签——choose

① 在EmployeeMapperDynamicSQL.java中添加抽象方法

//如果带了id就用id查,如果带了last_name就用last_name查,只会进入其中一个
List getEmpsByConditionChoose(Employee employee);

② 在EmployeeMapperDynamicSQL.xml中编写对应的sql语句



    select * from tbl_employee
    
        
        
            
                id=#{id}
            
            
                last_name like #{last_name}
            
            
                email = #{email}
            
            
                gender = 0
            
        
    

(5)动态SQL标签——set(与if结合的动态更新)

① 在EmployeeMapperDynamicSQL.java中添加抽象方法

//更新employee中不为空的属性(列),即哪列有值就更新哪列(id主键必须有值)
void updateEmp(Employee employee);

② 在EmployeeMapperDynamicSQL.xml中编写对应的sql语句



    update tbl_employee
    
    
        
            last_name=#{last_name},
        
        
            email=#{email},
        
        
            gender=#{gender}
        
    
    where id=#{id}

(6)动态SQL标签——foreach遍历集合

① 在EmployeeMapperDynamicSQL.java中添加抽象方法

//查询所有id在给定集合中的员工
List getEmpsByConditionForeach(@Param("ids")List ids);

② 在EmployeeMapperDynamicSQL.xml中编写对应的sql语句



    select * from tbl_employee
    
    
        #{item_id}
    

③ 测试

@Test
public void testDynamicSQL() throws IOException {
    // 1、获取sqlSessionFactory对象
    SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    // 2、获取sqlSession对象
    SqlSession openSession = sqlSessionFactory.openSession();
    try {
        // 3、获取接口的实现类对象
        //会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
        EmployeeMapperDynamicSQL mapper = openSession.getMapper(EmployeeMapperDynamicSQL.class);
        Employee employee = new Employee(null,null,null,null);
        List ids = new ArrayList();
        ids.add(1);
        ids.add(2);
        ids.add(3);
        List emps = mapper.getEmpsByConditionForeach(ids);
        for (Employee emp : emps) {
            System.out.println(emp);
        }
        openSession.commit();
    }finally{
        openSession.close();
    }
}

在这里插入图片描述

(7)动态SQL标签——mysql下foreach批量插入的两种方式

① 在EmployeeMapperDynamicSQL.java中添加抽象方法

//批量插入员工信息
void addEmps(@Param("emps")List emps);

② 在EmployeeMapperDynamicSQL.xml中编写对应的sql语句


--


	insert into tbl_employee(last_name, email, gender,d_id)
	values
	
	    (#{emp.last_name},#{emp.email},#{emp.gender},#{emp.dept.id})
	





	
		insert into tbl_employee(last_name,email,gender,d_id)
		values(#{emp.last_name},#{emp.email},#{emp.gender},#{emp.dept.id})
	

③ 测试

@Test
public void testBatchSave() throws IOException {
    // 1、获取sqlSessionFactory对象
    SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    // 2、获取sqlSession对象
    SqlSession openSession = sqlSessionFactory.openSession();
    try {
        // 3、获取接口的实现类对象
        //会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
        EmployeeMapperDynamicSQL mapper = openSession.getMapper(EmployeeMapperDynamicSQL.class);
        List emps = new ArrayList();
        emps.add(new Employee(null,"Smith","1","Smith@163.com",new Department(1)));
        emps.add(new Employee(null,"Smith2","1","Smith2@163.com",new Department(2)));
       9 mapper.addEmps(emps);
        openSession.commit();
    }finally{
        openSession.close();
    }
}
(8)动态SQL标签——内置参数_parameter和_databaseId

① 在EmployeeMapperDynamicSQL.java中添加抽象方法

List getEmpsTestInnerParameter(Employee employee);

② 在EmployeeMapperDynamicSQL.xml中编写对应的sql语句



    
        select * from tbl_employee
        
            where last_name like #{_parameter.last_name}
        
    
    
        select * from tbl_employee
    

(9)动态SQL标签——bind绑定

bind:可以将OGNL表达式的值绑定到一个变量中,方便后来引用这个变量的值。以上面的sql语句为例:



    
    
    
        select * from tbl_employee
        
            where last_name like #{_lastName}
        
    
    
        select * from tbl_employee
    

测试代码如下:

@Test
public void testBind() throws IOException {
    // 1、获取sqlSessionFactory对象
    SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    // 2、获取sqlSession对象
    SqlSession openSession = sqlSessionFactory.openSession();
    try {
        // 3、获取接口的实现类对象
        //会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
        EmployeeMapperDynamicSQL mapper = openSession.getMapper(EmployeeMapperDynamicSQL.class);
        Employee employee = new Employee();
        employee.setLast_name("e");
        List emps = mapper.getEmpsTestInnerParameter(employee);
        System.out.println(emps);
        openSession.commit();
    }finally{
        openSession.close();
    }
}
(10)动态SQL标签——抽取可重用的sql片段

抽取可重用的sql片段,方便后面引用 ① sql抽取:将经常要查询的列名,或者插入用的列名抽取出来,方便引用; ② 用include来引用已经抽取的sql; ③ 此外,include还可以自定义一些property,sql标签内部就能使用自定义的属性;include-property:正确的取值方式为${prop}


	
		employee_id,last_name,email
	
	
		last_name,email,gender,d_id
	


	insert into tbl_employee() 
	values
	
		(#{emp.last_name},#{emp.email},#{emp.gender},#{emp.dept.id})
	

6.MyBatis——缓存机制 (1)缓存介绍

MyBatis 包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。缓存极大地提升了查询效率。MyBatis中默认定义了两级缓存,即一级缓存和二级缓存。

① 一级缓存

一级缓存(local cache),即本地缓存,作用域默认为sqlSession。当 Session flush 或 close 后,该Session 中的所有 Cache 将被清空。除此之外,本地缓存不能被关闭,但是可以调用 clearCache() 来清空本地缓存,或者改变缓存的作用域。

② 二级缓存

二级缓存(second level cache),即全局作用域缓存,它是基于namespace级别的缓存,一个namespace对应一个二级缓存。二级缓存在默认情况下不开启,若要开启则需要手动配置。MyBatis提供二级缓存的接口以及实现,缓存实现要求POJO实现Serializable接口。 二级缓存的工作机制如下: 1)一个会话,查询一条数据,这个数据就会被放在当前会话的一级缓存中; 2)如果会话关闭,一级缓存中的数据会被保存到二级缓存中;新的会话查询信息,就可以参照二级缓存中的内容; 3)不同namespace查出的数据会放在自己对应的缓存中(map) 4)查出的数据都会被默认先放在一级缓存中,只有会话提交或者关闭以后,一级缓存中的数据才会转移到二级缓存中,那么此时的效果就是数据会从二级缓存中获取。

(2)一级缓存体验

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

@Test
public void testFirstLevelCache() throws IOException {
    // 1、获取sqlSessionFactory对象
    SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    // 2、获取sqlSession对象
    SqlSession openSession = sqlSessionFactory.openSession();
    try {
        // 3、获取接口的实现类对象
        //会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
        EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
        Employee emp01 = mapper.getEmpById(1);
        System.out.println(emp01);
        //xxx
        //再次调用getEmpById()方法
        Employee emp02 = mapper.getEmpById(1);
        System.out.println(emp02);
        //判断emp01和emp02是否为同一个对象
        System.out.println(emp01==emp02);
        openSession.commit();
    }finally{
        openSession.close();
    }
}

在这里插入图片描述

(3)一级失效的四种情况

① 不同的SqlSession对应不同的一级缓存 ② 同一个SqlSession但是查询条件不同 ③ 两次查询之间执行了增删改操作(这次增删改可能对当前数据有影响) ④ 同一个SqlSession两次查询期间手动清空了缓存(openSession.clearCache())

(4)二级缓存的使用

① 在全局配置文件 mybatis-config.xml 中开启全局二级缓存配置:


② 去相应的mapper.xml(以EmployeeMapper.xml为例)中配置使用二级缓存:



③ 相应的POJO(Employee.java、Department.java)需要实现序列化接口 在这里插入图片描述 在这里插入图片描述 ④ 测试

@Test
public void testSecondLevelCache() throws IOException {
    SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    SqlSession openSession = sqlSessionFactory.openSession();
    SqlSession openSession2 = sqlSessionFactory.openSession();
    try {
        EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
        EmployeeMapper mapper2 = openSession2.getMapper(EmployeeMapper.class);

        Employee emp01 = mapper.getEmpById(1);
        System.out.println(emp01);
        //会话关闭,那么一级缓存中的数据会被保存到二级缓存中,新的会话查询信息,就可以参照二级缓存中的内容
        openSession.close();

        //第二次查询是从二级缓存中拿到的数据,并没有发送新的sql
        Employee emp02 = mapper2.getEmpById(1);
        System.out.println(emp02);
        System.out.println(emp01==emp02);
        openSession.close();
        
    }finally{
    
    }
}

在这里插入图片描述

(5)缓存有关的设置/属性 全局setting的cacheEnable配置二级缓存的开关,true为打开,false为关闭(一级缓存一直是打开的)全局setting的localCacheScope值为SESSION时,当前会话的所有数据保存在会话缓存中;值为STATEMENT时,禁用一级缓存select标签的useCache属性配置这个select是否使用二级缓存,true为使用,false为不使用(一级缓存一直是使用的 )sql标签的flushCache属性在增删改标签中默认flushCache=true,即sql执行以后,会同时清空一级和二级缓存。在查询标签中则默认flushCache=falsesqlSession.clearCache()只用来清除当前sqlSession一级缓存 (6)缓存原理图示

在这里插入图片描述

(7)第三方缓存整合——ehcache ① 导入ehcache适配包

在这里插入图片描述 在这里插入图片描述

② 在config目录下添加ehcache.xml文件


 
 
 
 
 


③ 在EmployeeMapper.xml文件中进行配置

④ 测试
@Test
public void testSecondLevelCache() throws IOException {
    SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    SqlSession openSession = sqlSessionFactory.openSession();
    SqlSession openSession2 = sqlSessionFactory.openSession();
    try {
        EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
        EmployeeMapper mapper2 = openSession2.getMapper(EmployeeMapper.class);

        Employee emp01 = mapper.getEmpById(1);
        System.out.println(emp01);
        openSession.close();

        //第二次查询是从二级缓存中拿到的数据,并没有发送新的sql
        Employee emp02 = mapper2.getEmpById(1);
        System.out.println(emp02);
        System.out.println("emp01==emp02的结果为:"+(emp01==emp02));
        openSession.close();
    }finally{
    
    }
}

在这里插入图片描述 在这里插入图片描述

⑤ 扩展——引用缓存

在DepartmentMapper.xml中引用缓存



7.MyBatis——SSM框架整合

此处的SSM框架整合未使用Maven等项目管理工具,若想看Maven版本的可以查看这篇文章。

(1)在IDEA中新建一个Java Web项目

在这里插入图片描述 在这里插入图片描述 在这里插入图片描述

(2)导入需要的jar包

在这里插入图片描述 右键lib,点击"Add as Library…",再点击OK即可。

(3)进行各种配置以及代码的编写

整体的项目结构如下: 在这里插入图片描述 有需要的可以下载本项目的源代码(提取码:test)

(4)测试

在这里插入图片描述 在这里插入图片描述

8.MyBatis——逆向工程 (1)MyBatis Generator

MyBatis Generator简称MBG,是一个专门为MyBatis框架使用者定制的代码生成器,可以快速的根据表生成对应的映射文件、接口、以及bean类等。它支持基本的增删改查,以及QBC风格的条件查询。但是表连接、存储过程等这些复杂sql的定义需要我们手工编写。

(2)编写相应的配置文件及代码

① 将之前Mybatis项目复制一份并改名为MyBatis_07_mbg,删除一些不用的文件,并导入Myabtis Generator的包: 在这里插入图片描述 ② 新建mbg.xml文件,放在src目录下(可以先从官网中复制过来,然后再根据实际情况进行修改)


DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">


    
    
        
        
        

        
        
            
        

        
        
            
            
        

        
        
            
        

        
        
            
        

        
        
        
    

③ 使用MyBatis逆向工程生成相应的代码及配置,即运行下面官网提供的代码:

@Test
public void testMbg() throws Exception {
    List warnings = new ArrayList();
    boolean overwrite = true;
    File configFile = new File("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);
}

④ 测试

@Test
public void testMyBatis3() throws IOException{
    SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    SqlSession openSession = sqlSessionFactory.openSession();
    try{
        EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
        //xxxExample就是封装查询条件的
        //1、查询所有
        //List emps = mapper.selectByExample(null);
        //2、查询员工名字中有e字母的,和员工性别是1的
        //封装员工查询条件的example
        EmployeeExample example = new EmployeeExample();
        //创建一个Criteria,这个Criteria就是拼装查询条件
        //select id, last_name, email, gender, d_id from tbl_employee 
        //WHERE ( last_name like ? and gender = ? ) or email like "%e%"
        Criteria criteria = example.createCriteria();
        criteria.andLastNameLike("%e%");
        criteria.andGenderEqualTo("1");
        
        Criteria criteria2 = example.createCriteria();
        criteria2.andEmailLike("%e%");
        example.or(criteria2);
        
        List list = mapper.selectByExample(example);
        for (Employee employee : list) {
            System.out.println(employee.getId());
        }
        
    }finally{
        openSession.close();
    }
}
9.MyBatis——工作原理 (1)框架分层架构

在这里插入图片描述 在这里插入图片描述

(2)入门案例的运行流程

在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述

10.MyBatis——插件开发 (1)插件原理

MyBatis在四大对象Executor、ParameterHandler、ResultSetHandler、StatementHandler的创建过程中,都会有插件进行介入。插件可以利用动态代理机制一层层的包装目标对象,而实现在目标对象执行目标方法之前进行拦截的效果。MyBatis 允许在已映射语句执行过程中的某一点进行拦截调用。 ① 每个创建出来的对象不是直接返回的,而是调用方法 interceptorChain.pluginAll(parameterHandler) 后返回。

public Object pluginAll(Object target) {
	for (Interceptor interceptor : interceptors) {
		target = interceptor.plugin(target);
	}
	return target;
}

② pluginAll() 方法获取到所有的Interceptor(拦截器,插件需要实现的接口)后,调用interceptor.plugin(target),即返回target包装后的对象。 ③ 插件机制,我们可以使用插件为目标对象创建一个代理对象(即AOP,面向切面编程),我们的插件可以为四大对象创建出代理对象,代理对象就可以拦截到四大对象的每一个执行。

(2)编写插件

以入门案例的代码为基础来编写插件,其具体步骤如下: ① 编写Interceptor的实现类 ② 使用@Intercepts注解完成插件签名

package com.atguigu.mybatis.dao;

import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.*;

import java.util.Properties;

//完成插件签名:告诉MyBatis当前插件用来拦截哪个对象的哪个方法
@Intercepts(
{
    @Signature(type= StatementHandler.class,method="parameterize",args=java.sql.Statement.class)
})
public class MyFirstPlugin implements Interceptor {
    
    //intercept:拦截目标对象的目标方法的执行
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        //执行目标方法
        Object proceed = invocation.proceed();
        //返回执行后的返回值
        return null;
    }
    
    //plugin:包装目标对象的,包装:为目标对象创建一个代理对象
    @Override
    public Object plugin(Object target) {
        //我们可以借助Plugin的wrap方法来使用当前Interceptor包装我们目标对象
        System.out.println("MyFirstPlugin...plugin:mybatis将要包装的对象"+target);
        Object wrap = Plugin.wrap(target, this);
        //返回为当前target创建的动态代理
        return wrap;
    }
    
    //setProperties:将插件注册时的property属性设置进来
    @Override
    public void setProperties(Properties properties) {
        System.out.println("插件配置的信息:"+properties);
    }
}

③ 将写好的插件注册到全局配置文件mybatis-config中



    
        
        
    

④ 运行任意一个测试方法

@Test
public void test02() throws IOException {
    // 1、获取sqlSessionFactory对象
    SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    // 2、获取sqlSession对象
    SqlSession openSession = sqlSessionFactory.openSession();
    try {
        // 3、获取接口的实现类对象
        //会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
        EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
        Employee employee = mapper.getEmpById(1);
        System.out.println(mapper.getClass());
        System.out.println(employee);
    }finally{
        openSession.close();
    }
}

在这里插入图片描述

(3)多个插件运行流程

① 编写第二个插件

package com.atguigu.mybatis.dao;

import java.util.Properties;

import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;

@Intercepts(
{
    @Signature(type=StatementHandler.class,method="parameterize",args=java.sql.Statement.class)
})
public class MySecondPlugin implements Interceptor{
    
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        System.out.println("MySecondPlugin...intercept:"+invocation.getMethod());
        return invocation.proceed();
    }
    
    @Override
    public Object plugin(Object target) {
        // TODO Auto-generated method stub
        System.out.println("MySecondPlugin...plugin:"+target);
        return Plugin.wrap(target, this);
    }
    
    @Override
    public void setProperties(Properties properties) {
        // TODO Auto-generated method stub
    }
}

② 注册插件



    
        
        
    
    

③ 测试运行 在这里插入图片描述 在这里插入图片描述

(4)开发插件
@Override
public Object intercept(Invocation invocation) throws Throwable {
	// TODO Auto-generated method stub
	System.out.println("MyFirstPlugin...intercept:"+invocation.getMethod());
	
	//动态的改变一下sql运行的参数:以前1号员工,实际从数据库查询3号员工
	
	Object target = invocation.getTarget();
	System.out.println("当前拦截到的对象:"+target);
	//拿到:StatementHandler==>ParameterHandler===>parameterObject
	//拿到target的元数据
	MetaObject metaObject = SystemMetaObject.forObject(target);
	Object value = metaObject.getValue("parameterHandler.parameterObject");
	System.out.println("sql语句用的参数是:"+value);
	//修改完sql语句要用的参数
	metaObject.setValue("parameterHandler.parameterObject", 3);
	//执行目标方法
	Object proceed = invocation.proceed();
	//返回执行后的返回值
	return proceed;
}
(5)PageHelper分页插件的使用

① PageHelper插件官网地址 ② 以入门案例的代码为例,在其基础上使用PageHelper插件。 1)导入需要的 jar 包 在这里插入图片描述 2)在全局配置文件mybatis-config中注册pageHelper插件



    

3)测试使用

@Test
public void testPageHelper01() throws IOException {
    SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    SqlSession openSession = sqlSessionFactory.openSession();
    try {
        EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
        //查询第1页的5条记录
        Page page = PageHelper.startPage(1, 5);
        List emps = mapper.getEmps();
        for (Employee emp : emps) {
            System.out.println(emp);
        }
        System.out.println("当前页码:"+page.getPageNum());
        System.out.println("总记录数:"+page.getTotal());
        System.out.println("每页的记录数:"+page.getPageSize());
    }finally{
        openSession.close();
    }
}

在这里插入图片描述

@Test
public void testPageHelper02() throws IOException {
    // 1、获取sqlSessionFactory对象
    SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    // 2、获取sqlSession对象
    SqlSession openSession = sqlSessionFactory.openSession();
    try {
        EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
        //查询第5页的1条记录
        Page page = PageHelper.startPage(5, 1);
    
        List emps = mapper.getEmps();
        //传入要连续显示多少页
        PageInfo info = new PageInfo(emps, 5);
        for (Employee employee : emps) {
            System.out.println(employee);
        }
        System.out.println("当前页码:"+info.getPageNum());
        System.out.println("总记录数:"+info.getTotal());
        System.out.println("每页的记录数:"+info.getPageSize());
        System.out.println("总页码:"+info.getPages());
        System.out.println("是否第一页:"+info.isIsFirstPage());
        
        int[] nums = info.getNavigatepageNums();
        System.out.println("连续显示的页码:");
        //当前页码为第5页,在连续显示时,当前页码居中
        for (int i = 0; i 
    insert into tbl_employee(last_name, gender, email)
    values (#{last_name},#{gender},#{email})

③ 测试

@Test
public void testBatch() throws IOException {
    SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    //可以执行批量操作的sqlSession
    SqlSession openSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
    long start = System.currentTimeMillis();
    try{
        EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
        //插入1000条数据
        for (int i = 0; i 设置参数===>10000次===>执行(1次))
        //Parameters: 616c1(String), 616c1@163.com(String), 1(String)==>4598
        //非批量:(预编译sql=设置参数=执行)==》10000    10200
        System.out.println("执行时长:"+(end-start)+"毫秒");
    }finally{
        openSession.close();
    }
}

在这里插入图片描述 ④ 与Spring整合中,推荐额外的配置一个可以专门用来执行批量操作的sqlSession



	
	

需要用到批量操作的时候,可以注入配置的这个批量SqlSession,通过它获取到mapper映射器进行操作即可。

(2)自定义类型处理器——MyBatis中枚举类型的默认处理

① 创建枚举类EmpStatus,然后在Employee.java中将该类作为其属性并构建 get/set 方法。

package com.atguigu.mybatis.bean;

public enum EmpStatus {
    //员工状态
    LOGIN,LOGOUT,REMOVE
}
//员工状态
private EmpStatus empStatus=EmpStatus.LOGOUT;

② 在表tbl_employee中新增一列,表示员工状态,同时也需要改变aEmployeeMapper.xml中对应的sql语句

alter table tbl_employee add empStatus varchar(11)


    insert into tbl_employee(last_name, gender, email,empStatus)
    values (#{last_name},#{gender},#{email},#{empStatus})

③ 测试

/*
 * 默认mybatis在处理枚举对象的时候保存的是枚举的名字:EnumTypeHandler
 * 改变使用:EnumOrdinalTypeHandler:
 */
@Test
public void testEnum() throws IOException{
    SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    SqlSession openSession = sqlSessionFactory.openSession();
    try{
        EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
        Employee employee = new Employee("test_enum", "enum@163.com","1");
        mapper.addEmp(employee);
        openSession.commit();
    }finally{
        openSession.close();
    }
}

在这里插入图片描述 在全局配置文件mybatis-config.xml添加如下配置之后,再次测试时便保存枚举的索引


	

在这里插入图片描述

(3)自定义类型处理器——使用自定义的类型处理器处理枚举类型

① 重新编写枚举EmpStatus类

package com.atguigu.mybatis.bean;

//希望数据库保存的是100,200这些状态码,而不是默认0,1或者枚举的名
public enum EmpStatus {
    LOGIN(100,"用户登录"),LOGOUT(200,"用户登出"),REMOVE(300,"用户不存在");
   
    private Integer code;
    private String msg;
    private EmpStatus(Integer code,String msg){
        this.code = code;
        this.msg = msg;
    }
    public Integer getCode() {
        return code;
    }
    
    public void setCode(Integer code) {
        this.code = code;
    }
    public String getMsg() {
        return msg;
    }
    public void setMsg(String msg) {
        this.msg = msg;
    }
    
    //按照状态码返回枚举对象
    public static EmpStatus getEmpStatusByCode(Integer code){
        switch (code) {
            case 100:
                return LOGIN;
            case 200:
                return LOGOUT;
            case 300:
                return REMOVE;
            default:
                return LOGOUT;
        }
    }
}

② 自定义TypeHandler类

package com.atguigu.mybatis.typehandler;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;

import com.atguigu.mybatis.bean.EmpStatus;

//1、实现TypeHandler接口,或者继承BaseTypeHandler
public class MyEnumEmpStatusTypeHandler implements TypeHandler {
    
    //定义当前数据如何保存到数据库中
    @Override
    public void setParameter(PreparedStatement ps, int i, EmpStatus parameter,
                             JdbcType jdbcType) throws SQLException {
        // TODO Auto-generated method stub
        System.out.println("要保存的状态码:"+parameter.getCode());
        ps.setString(i, parameter.getCode().toString());
    }
    
    @Override
    public EmpStatus getResult(ResultSet rs, String columnName)
            throws SQLException {
        // TODO Auto-generated method stub
        //需要根据从数据库中拿到的枚举的状态码返回一个枚举对象
        int code = rs.getInt(columnName);
        System.out.println("从数据库中获取的状态码:"+code);
        EmpStatus status = EmpStatus.getEmpStatusByCode(code);
        return status;
    }
    
    @Override
    public EmpStatus getResult(ResultSet rs, int columnIndex)
            throws SQLException {
        // TODO Auto-generated method stub
        int code = rs.getInt(columnIndex);
        System.out.println("从数据库中获取的状态码:"+code);
        EmpStatus status = EmpStatus.getEmpStatusByCode(code);
        return status;
    }
    
    @Override
    public EmpStatus getResult(CallableStatement cs, int columnIndex)
            throws SQLException {
        // TODO Auto-generated method stub
        int code = cs.getInt(columnIndex);
        System.out.println("从数据库中获取的状态码:"+code);
        EmpStatus status = EmpStatus.getEmpStatusByCode(code);
        return status;
    }
}

③ 在全局配置文件mybatis-config.xml注册



    
    
    

在这里插入图片描述

关注
打赏
1665627467
查看更多评论
立即登录/注册

微信扫码登录

0.5435s