MyBatis框架总结

目录

什么是框架?

什么是ORM框架?

什么是MyBatis?

MyBatis的核心对象

SqlSessionFactoryBuilder对象

SqlSessionFactory对象

SqlSession对象

Mapper代理对象

MyBatis的工作流程

入门案例

        环境搭建

        MyBatis框架搭建的步骤

        创建持久层接口和映射文件

        测试持久层接口方法

        重点:映射文件注意事项

MyBatis增删改查案例

        新增

        删除     

        修改

        查询

        代码编写流程

        注意:优化测试类

MyBatis如何绑定实体类的参数

        1、使用${}绑定参数

        2、使用#{}绑定参数

        3、使用bind标签定义参数

        ${}和#{}绑定参数的区别

如果方法有多个参数如何绑定

        1、顺序传参

        2、@Param传参

        3、POJO传参

        4、Map传参

ParameterType标签

MyBatis核心配置文件SqlMapConfig中的标签

        结构

        properties标签

        settings标签

                plugin标签

                typeAliases标签

        environment标签

                transactionManager标签

                dataSource标签

        mapper标签

                1、使用相对路径注册映射文件

                2、使用绝对路径注册映射文件

                3、注册持久层接口

                4、注册一个包下的所有持久层接口

MyBatis映射文件标签

当POJO属性名和数据库列名不一致时

        Sql语句的查询字段起与POJO属性相同的别名

        自定义映射文件Result

特殊字符处理

动态sql

        if标签

        where标签

        set标签

        choose标签和when标签和otherwise标签

        foreach标签

                foreach标签遍历数组

                foreach标签遍历Collection

                foreach标签遍历Map

缓存

        一级缓存

        清空一级缓存

        二级缓存

        开启二级缓存

        测试二级缓存

关联查询

        association标签

        collection标签

        一对一关联查询

        一对多关联查询

        多对多关联查询

分解式查询

        一对多

        select属性和column属性

        延迟加载

注解开发

        环境搭配

        增删改查注解

        主键回填注解@SelectKey

        动态sql

        自定义映射@Result

        开启二级缓存@CacheNamespace

        一对一关联查询

        一对多查询

        注解和映射文件对比

分页插件PageHelper

MyBatisGenerator工具

        在pom文件中配置MBG插件

        编写generatorConfig配置文件

        运行插件

        增删改方法

        查询方法

        复杂查询


声明部分图片来自百战尚学堂  

什么是框架?

框架即一个半成品软件。开发者从头开发一个软件需要花费大量精力,于是有一些项目组开发出半成品软件,开发者在这些软件的基础上进行开发,这样的软件就称之为框架。

如果将开发完成的软件比作是一套已经装修完毕的新房,框架就好比是一套已经修建好的毛坯房。用户直接购买毛坯房,保证建筑质量和户型合理的同时可以进行风格的自由装修。

使用框架开发的好处:

  1. 省去大量的代码编写、减少开发时间、降低开发难度
  2. 限制程序员必须使用框架规范开发,增强代码的规范性,降低程序员之间沟通及日后维护的成本。
  3. 将程序员的注意力从技术中抽离出来,更集中在业务层面

使用框架就好比和世界上最优秀的软件工程师共同完成一个项目,并且他们完成的还是基础、全局的工作。

什么是ORM框架?

学过JDBC的同学都知道ORM映射吧?ORM映射全程为Object Relation Mapping(对象关系映射),即在数据库和对象之间作映射处理,将查询出的一条数据包装为一个对象。

什么是MyBatis?

我们前面说过JDBC有ORM映射思想,但是JDBC是需要手动进行ORM映射的,ORM映射并不是什么难的代码逻辑,但是在处理业务中常常会用到ORM映射,所以MyBatis的作用就是帮助我们完成ORM映射,不需要我们手动打代码,只需要配置一些配置文件即可

MyBatis的核心对象

SqlSessionFactoryBuilder对象

SqlSession工厂构建者对象,使用构造者模式创建SqlSession工厂对象。

SqlSessionFactory对象

SqlSession工厂,使用工厂模式创建SqlSession对象。

SqlSession对象

该对象可以操作数据库,也可以使用动态代理模式创建持久层接口的代理对象操作数据库。

Mapper代理对象

持久层接口的代理对象,他具体实现了持久层接口,用来操作数据库。

MyBatis的工作流程

  1. 创建SqlSessionFactoryBuilder对象
  2. 通过SqlSessionFactoryBuilder对象的build方法构建SqlSessionFactory对象:构造者模式
  3. 通过SqlSessionFactory对象的openSession方法生产SqlSession对象:工厂模式
  4. SqlSession对象创建了持久层接口的代理对象:动态代理模式
  5. 代理对象操作数据库

        

入门案例

        环境搭建

1、创建maven工程并在pom.xml中引入依赖,需要引入mybatis的依赖

2、创建myBatis的核心配置文件SqlMapConfig.xml
3. 将log4j.properties文件放入resources中,让控制台打印SQL语句。
4. 创建实体类(实体类的名字建议和表名相同,且实体类的属性和表的列名相同)

1. 创建maven工程,引入依赖

  
  
    org.mybatis
    mybatis
    3.5.7
  
  
  
    mysql
    mysql-connector-java
    8.0.26
  
  
  
    junit
    junit
    4.10
  
  
  
    log4j
    log4j
    1.2.12
  


2. 创建mybatis核心配置文件SqlMapConfig.xml



    
    
        
            
            
            
            
                
                
                
                
            
        
    


3. 将log4j.properties文件放入resources中,让控制台打印SQL语句。
4. 创建实体类

MyBatis框架总结_第1张图片

        MyBatis框架搭建的步骤

  1. java项目中创建持久层接口
  2. resource项目中创建映射文件
  3. 将映射文件注册到SqlMapConfig.xml文件中

首先我们需要有一张表进行测试,我们这里使用user表进行测试

MyBatis框架总结_第2张图片

        创建持久层接口和映射文件

MyBatis框架总结_第3张图片


//1. 在java目录创建持久层接口
public interface UserMapper{
//当使用MyBatis调用该方法时,
//MyBatis会自动根据sql语句查询并封装为User对象放到该容器中
  ListfindAll();
}
2. 在resource目录创建映射文件



  
3. 将映射文件配置到mybatis核心配置文件中(SqlMapConfig.xml)


       

        测试持久层接口方法

public class TestUserMapper {
    @Test
    public void testFindAll() throws IOException {
        //读取核心配置文件
        InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
        //创建SqlSessionFactoryBuilder对象
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        //SqlSessionFactoryBuilder对象创建SqlSessionFactory对象
        SqlSessionFactory factory = builder.build(is);
        //SqlSessionFactory对象创建SqlSession对象
        SqlSession session = factory.openSession();
        //获取代理对象
        UserMapper userMapper = session.getMapper(UserMapper.class);

        //调用方法
        List list = userMapper.findAll();
        list.forEach(System.out::println);
    }
}

MyBatis框架总结_第4张图片

        重点:映射文件注意事项

  • 映射文件要和接口名称相同。
  • 映射文件要和接口的目录结构相同。

MyBatis框架总结_第5张图片

  • 映射文件中namespace属性要写接口的全名。
  • 映射文件中标签的id属性是接口方法的方法名
  • 映射文件中标签的resultType属性是接口方法的返回值类型
  • 映射文件中标签的parameterType属性是接口方法的参数类型
  • 映射文件中resultType、parameterType属性要写全类名,如果是集合类型,则写其泛型的全类名。

MyBatis增删改查案例

持久层接口和映射文件都创建出来后,那么我们来测试增删改查,代码的编写步骤是:

1、持久层接口添加方法

2、映射文件中添加对应标签

3、测试

        新增

1、持久层添加方法       

MyBatis框架总结_第6张图片

2、映射文件中添加对应标签,因为是新增,所以需要添加insert标签

id属性时持久层接口的方法名,parameterType是参数类型,必须写参数的全类名

 3、测试

@Test
    public void testAdd() throws IOException {
        //读取核心配置文件
        InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
        //创建SqlSessionFactoryBuilder对象
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        //SqlSessionFactoryBuilder对象创建SqlSessionFactory对象
        SqlSessionFactory factory = builder.build(is);
        //SqlSessionFactory对象创建SqlSession对象
        SqlSession session = factory.openSession();
        //获取代理对象
        UserMapper userMapper = session.getMapper(UserMapper.class);

        //调用方法
        User user = new User("zhangsan","男","上海");
        userMapper.add(user);

        //提交事务
        session.commit();
    }

MyBatis框架总结_第7张图片

        删除     

public interface UserMapper {
    //查询所有
    List findAll();

    //新增
    void add(User user);

    //删除
    void delete(int id);
}

      delete from user where id = #{id}
@Test
    public void testDelete() throws IOException {
        //读取核心配置文件
        InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
        //创建SqlSessionFactoryBuilder对象
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        //SqlSessionFactoryBuilder对象创建SqlSessionFactory对象
        SqlSessionFactory factory = builder.build(is);
        //SqlSessionFactory对象创建SqlSession对象
        SqlSession session = factory.openSession();
        //获取代理对象
        UserMapper userMapper = session.getMapper(UserMapper.class);

        //调用方法
        userMapper.delete(18);

        //提交事务
        session.commit();
    }

MyBatis框架总结_第8张图片

        修改

public interface UserMapper {
    //查询所有
    List findAll();

    //新增
    void add(User user);

    //删除
    void delete(int id);

    //修改
    void update(User user);
}

    update user set username1 = #{username1},sex1 = #{sex1},address1 = #{address1}
@Test
    public void testUpdate() throws IOException {
        //读取核心配置文件
        InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
        //创建SqlSessionFactoryBuilder对象
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        //SqlSessionFactoryBuilder对象创建SqlSessionFactory对象
        SqlSessionFactory factory = builder.build(is);
        //SqlSessionFactory对象创建SqlSession对象
        SqlSession session = factory.openSession();
        //获取代理对象
        UserMapper userMapper = session.getMapper(UserMapper.class);

        //调用方法
        User user = new User("一拳超人","男","网络");
        userMapper.update(user);

        //提交事务
        session.commit();
    }

MyBatis框架总结_第9张图片

        查询

public interface UserMapper {
    //查询所有
    List findAll();

    //新增
    void add(User user);

    //删除
    void delete(int id);

    //修改
    void update(User user);

    //根据id查询
    User findById(int id);
}
@Test
    public void testFindById() throws IOException {
        //读取核心配置文件
        InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
        //创建SqlSessionFactoryBuilder对象
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        //SqlSessionFactoryBuilder对象创建SqlSessionFactory对象
        SqlSessionFactory factory = builder.build(is);
        //SqlSessionFactory对象创建SqlSession对象
        SqlSession session = factory.openSession();
        //获取代理对象
        UserMapper userMapper = session.getMapper(UserMapper.class);

        //调用方法
        User user = userMapper.findById(1);
        System.out.println(user);
    }

        代码编写流程

  1. 持久层接口添加方法
  2. 映射文件中添加对应标签
  3. 编写测试方法

        注意:优化测试类

写到这里大家有没有发现,我们每次测试的时候,都要创建SqlSessionFactoryBuilder、SqlSessionFactory、SqlSession对象、代理对象,每次都要重复写一样的代码,这样使得代码非常冗余,那么有什么方法可以使代码简化呢?

利用Junit的前置后置方法@Before和@After注解简化代码,优化测试类代码

优化过后我们会就可以将以前代码中的这些对象删除了,应为junit在启动的时候会自动调用@Before修饰的方法,清理了过后我们会发现整段代码都干净了很多

public class TestUserMapper {
    InputStream is = null;
    SqlSession session = null;
    UserMapper userMapper = null;
    @Before
    public void before() throws IOException {
        //读取核心配置文件
        is = Resources.getResourceAsStream("SqlMapConfig.xml");
        //创建SqlSessionFactoryBuilder对象
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        //SqlSessionFactoryBuilder对象创建SqlSessionFactory对象
        SqlSessionFactory factory = builder.build(is);
        //SqlSessionFactory对象创建SqlSession对象
        session = factory.openSession();
        //获取代理对象
        userMapper = session.getMapper(UserMapper.class);
    }
    @After
    public void after() throws IOException {
        //释放资源
        session.close();
        is.close();
    }
    @Test
    public void testFindAll() throws IOException {

        //调用方法
        List list = userMapper.findAll();
        list.forEach(System.out::println);
    }

    @Test
    public void testAdd() throws IOException {
        //调用方法
        User user = new User("zhangsan","男","上海");
        userMapper.add(user);

        //提交事务
        session.commit();
    }

    @Test
    public void testDelete() throws IOException {
        //调用方法
        userMapper.delete(18);

        //提交事务
        session.commit();
    }

    @Test
    public void testUpdate() throws IOException {
        //调用方法
        User user = new User("一拳超人","男","网络");
        userMapper.update(user);

        //提交事务
        session.commit();
    }

    @Test
    public void testFindById() throws IOException {
        //调用方法
        User user = userMapper.findById(1);
        System.out.println(user);
    }
}

MyBatis如何绑定实体类的参数

        1、使用${}绑定参数

${}绑定参数相当于JDBC里边的Statement对象,是通过字符串拼接实现的绑定,所以${}绑定参数会出现sql注入的问题

//测试方法写法如下:
@Test
public void testFindByNameLike(){
  List users = userMapper.findByUsernameLike("尚学堂");
  users.forEach(System.out::println);
}

根据log4j输出的加载信息可以看到,条件是直接拼接到sql语句中的,所以这就会出现sql注入的问题

MyBatis框架总结_第10张图片

        2、使用#{}绑定参数

#{}绑定参数相当于JDBC里的PreparedStatement对象,PreparedStatement对象是通过预编译,然后绑定参数的方式实现避免sql注入

    @Test
    public void testFindByNameLike(){
        List users = userMapper.findByUsernameLike("%尚学堂%");
        users.forEach(System.out::println);
    }

MyBatis框架总结_第11张图片

        3、使用bind标签定义参数

如果使用#还不想在调用方法的参数中添加%,可以使用允许我们在 Sql语句以外创建一个变量,并可以将其绑定到当前的Sql语句中。用法如下:

        ${}和#{}绑定参数的区别

  1. #表示sql模板的占位符$表示将字符串拼接到sql模板中。
  2. #可以防止sql注入,一般能用#就不用$。
  3. ${}内部的参数名必须写value。

其实也就相当于PreparedStatementStatement的区别 

如果方法有多个参数如何绑定

        1、顺序传参

Sql中的参数使用arg0,arg1...param1,param2...表示参数的顺序。此方法可读性较低,在开发中不建议使用

1、持久层接口方法

    /**
     * 分页查询
     * @param startIndex 开始索引
     * @param pageSize 每页条数
     * @return
     */
    List findPage(int startIndex,int pageSize);

2、映射文件

    

3、测试

    @Test
    public void testFindPage(){
        List users = userMapper.findPage(0,3);
        users.forEach(System.out::println);
    }

MyBatis框架总结_第12张图片

        2、@Param传参

1、持久层接口方法

    /**
     * 分页查询
     * @param startIndex 开始索引
     * @param pageSize 每页条数
     * @return
     */
    List findPage(int startIndex,int pageSize);

2、映射文件

    

3、测试

    @Test
    public void testFindPage(){
        List users = userMapper.findPage(0,3);
        users.forEach(System.out::println);
    }

MyBatis框架总结_第13张图片

        3、POJO传参

自定义POJO类,该类的属性就是要传递的参数

在SQL语句中绑定参数时使用POJO的属性名作为参数名即可。此方式推荐使用。

1、持久层接口创建方法

List findPage2(PageQuery pageQuery);

2、自定义POJO类,给类的属性就是要传递的参数

public class PageQuery {
    private int startIndex;
    private int pageSize;

    public PageQuery(int pageNum, int pageSize) {
        this.startIndex = pageNum;
        this.pageSize = pageSize;
    }

    public int getStartIndex() {
        return startIndex;
    }

    public void setStartIndex(int startIndex) {
        this.startIndex = startIndex;
    }

    public int getPageSize() {
        return pageSize;
    }

    public void setPageSize(int pageSize) {
        this.pageSize = pageSize;
    }

    @Override
    public String toString() {
        return "PageQuery{" +
                "pageNum=" + startIndex +
                ", pageSize=" + pageSize +
                '}';
    }
}

3、映射对象添加对应标签

    

4、测试

    @Test
    public void testFindPage2(){
        PageQuery pageQuery=new PageQuery(3, 3);
        List users = userMapper.findPage2(pageQuery);
        users.forEach(System.out::println);
    }

MyBatis框架总结_第14张图片

        4、Map传参

如果不想自定义POJO,可以使用Map作为传递参数的载体,在SQL语句中绑定参数时使用Map的Key作为参数名即可。此方法推荐使用。

1、持久层接口添加方法

    List findPage3(Map params);

2、映射文件中添加对应标签

    

3、测试

    @Test
    public void testFindPage3(){
        Map params = new HashMap();
        params.put("startIndex",0);
        params.put("pageSize",4);
        List users = userMapper.findPage3(params);
        users.forEach(System.out::println);
    }

MyBatis框架总结_第15张图片  

ParameterType标签

可能现在还有童鞋不知道ParameterType标签是干什么用的,其实前面的映射文件注意事项里讲过,ParameterType标签就是用来绑定参数的,但是它只能绑定一个参数,也就是说如果方法有多个参数就只能通过上面的四种方式任意一种绑定 

MyBatis核心配置文件SqlMapConfig中的标签

        结构

-configuration

    -properties(属性)

        -property

    -settings(全局配置参数)

        -setting

    -plugins(插件)

        -plugin

    -typeAliases(别名)

        -typeAliase

        -package

    -environments(环境)

        -environment

            -transactionManager(事务管理)

            -dataSource(数据源)

    -mappers(映射器)

        -mapper

        -package

        properties标签

属性值定义。properties标签中可以定义属性值,也可以引入外部配置文件。无论是内部定义还是外部引入,都可以使用${name}获取值

例如:我们可以将数据源配置写到外部的db.properties中再使用properties标签引入外部配置文件,这样可以做到动态配置数据源。

MyBatis框架总结_第16张图片

MyBatis框架总结_第17张图片

        settings标签

                plugin标签

是配置MyBatis插件的。插件可以增强MyBatis功能,比如进行sql增强,打印日志,异常处理等。后期我们会使用该标签配置分页插件。

                typeAliases标签

作用:该标签的作用就是用来给类起别名的

1、为一个类配置别名

为一个类配置别名

    type="全类名" alias="别名">

此时我们即可在映射文件中使用自定义别名,如:
1. 配置文件:

    

2. 映射文件:

2、为一个所有包下的所有类配置别名

  name="包名">

此时该包下的所有类都有了别名,别名代替了包名。如:
3. 配置文件:

  

4. 映射文件:

        environment标签

                transactionManager标签

作用:控制是否做事务处理


  
    
    
  

                dataSource标签

连接池

  
    
    
    
      
    
  

dataSource的type属性:

  • POOLED:使用连接池管理连接,使用MyBatis自带的连接池。
  • UNPOOLED:不使用连接池,直接由JDBC连接。
  • JNDI:由JAVAEE服务器管理连接,如果使用Tomcat作为服务器则使用Tomcat自带的连接池管理。

        mapper标签

用于注册映射文件或持久层接口,只有注册的映射文件才能使用,共有四种方式都可以完成注册:

                1、使用相对路径注册映射文件

使用mapper标签的resource属性实现使用相对路径注册映射文件



 

                2、使用绝对路径注册映射文件

使用mapper标签的url属性实现使用绝对路径注册映射文件

  

                3、注册持久层接口

使用mapper标签的class属性实现注册持久层接口

  

                4、注册一个包下的所有持久层接口

使用package标签的name属性实现注册一个包下的所有持久层接口

  

MyBatis映射文件标签

当POJO属性名和数据库列名不一致时

Result标签的作用自定义映射关系

MyBatis可以将数据库结果集封装到对象中,是因为结果集的列名和对象属性名相同

MyBatis框架总结_第18张图片

当POJO属性名和数据库列名不一致时,MyBatis无法自动完成映射关系。如:

MyBatis框架总结_第19张图片

此时有两种解决方案:

        Sql语句的查询字段起与POJO属性相同的别名


        自定义映射文件Result

• 在映射文件中,使用自定义映射关系:


  
  
  
  

• 在
   select * from teacher

特殊字符处理

在Mybatis映射文件中尽量不要使用一些特殊字符,如:<>等。

我们可以使用符号的实体来表示:

符号

实体

<

<

>

>

&

&

'

"

动态sql

一个查询的方法的Sql语句不一定是固定的。比如电商网站的查询商品,用户使用不同条件查询,Sql语句就会添加不同的查询条件。此时就需要在方法中使用动态Sql语句。

        if标签

标签内的Sql片段在满足条件后才会添加

用法为:。例如:根据不同条件查询用户:

1、持久层接口创建方法

    // 用户通用查询
    List findByCondition(User user);

2、映射文件添加对应标签

    

3、测试

    @Test
    public void testFindByCondition(){
        User user = new User("%尚学堂%","男");
        List users = userMapper.findByCondition(user);
        users.forEach(System.out::println);
    }

MyBatis框架总结_第20张图片  

        where标签

可以自动添加where,并代替sql中的where 1=1 和第一个and

更符合程序员的开发习惯,使用后的映射文件如下:

我们通过上面if标签的findByCondition方法修改一下映射文件中对应的标签进行测试

    

 MyBatis框架总结_第21张图片MyBatis框架总结_第22张图片

        set标签

标签用在update语句中。借助,可以只对有具体值的字段进行更新。会自动添加set关键字,并去掉最后一个if语句中多余的逗号

1、持久层创建方法

    //修改
    void update(User user);

2、映射文件对应标签

    
        update user
        
            
                username1 = #{username1},
            
            
                sex1 = #{sex1},
            
        
        
            id = #{id}
        
    

 3、测试

    @Test
    public void testUpdate() throws IOException {
        //调用方法
        User user = new User(16,"浩克");
        userMapper.update(user);

        //提交事务
        session.commit();
    }

MyBatis框架总结_第23张图片

        choose标签和when标签和otherwise标签

这些标签表示多条件分支,类似JAVA中的switch...case类似switch类似case类似default,用法如下:

1、持久层接口创建方法

    // 用户通用查询2
    List findByCondition2(User user);

2、映射文件创建对应标签

    
1、用户名<5模糊查询
    @Test
    public void testFindByCondition2(){
        User user = new User("尚学堂");
        List users = userMapper.findByCondition2(user);
        users.forEach(System.out::println);
    }

MyBatis框架总结_第24张图片

2、5<=用户名<10 精准查询
    @Test
    public void testFindByCondition2(){
        User user = new User("尚学堂666");
        List users = userMapper.findByCondition2(user);
        users.forEach(System.out::println);
    }

MyBatis框架总结_第25张图片

3、用户名>=10 直接查询id=1的数据
    @Test
    public void testFindByCondition2(){
        User user = new User("尚学堂66666666");
        List users = userMapper.findByCondition2(user);
        users.forEach(System.out::println);
    }

MyBatis框架总结_第26张图片

        foreach标签

类似JAVA中的for循环,可以遍历集合或数组。有如下属性:

  • collection:遍历的对象类型
  • open:开始的sql语句
  • close:结束的sql语句
  • separator:遍历每项间的分隔符
  • item:表示本次遍历获取的元素,遍历List、Set、数组时表示每项元素,遍历map时表示键值对的值
  • index:遍历List、数组时表示遍历的索引,遍历map时表示键值对的键

                foreach标签遍历数组

我们使用遍历数组进行批量删除。

1、持久层接口创建方法

    //批量删除
    void deleteBatch(int[] ids);

2、映射文件添加对应标签

    
        delete from user
        
            
                #{id}
            
        
    

3、测试

    @Test
    public void testDeleteBatch(){
        int[] ids = new int[]{16,19,20};
        userMapper.deleteBatch(ids);

        //提交事务
        session.commit();
    }

 删除前

MyBatis框架总结_第27张图片

 删除后

MyBatis框架总结_第28张图片

                foreach标签遍历Collection

1、持久层添加方法

    //批量添加
    void insertBatch(List users);

2、映射文件添加对应标签 

    
        insert into user
            
                (#{user.id},#{user.username1},#{user.sex1},#{user.address1})
            
    

3、测试 

    @Test
    public void testInsertBatch(){
        User user = new User("一拳超人","男","网络");
        User user1 = new User("路飞","男","海贼王");
        List list = new ArrayList<>();
        list.add(user);
        list.add(user1);
        //添加一批数据
        userMapper.insertBatch(list);

        //事务提交
        session.commit();
    }

 添加数据前

MyBatis框架总结_第29张图片

 添加数据后MyBatis框架总结_第30张图片

                foreach标签遍历Map

我们使用遍历Map进行多条件查询。

1、持久层添加方法

    /**
     * 多条件查询
     * @param map 查询的条件键值对 键:属性名 值:属性值
     * @return
     */
    List findUser(@Param("queryMap")  Mapmap);

2、映射文件添加对应标签

    

3、测试

    @Test
    public void testFindUser(){
        Map queryMap = new HashMap();
        queryMap.put("sex1","男");
        queryMap.put("address1","北京");
        List users = userMapper.findUser(queryMap);
        users.forEach(System.out::println);
    }

 MyBatis框架总结_第31张图片

缓存

MyBatis框架总结_第32张图片

缓存是内存当中一块存储数据的区域,目的是提高查询效率。MyBatis会将查询结果存储在缓存当中,当下次执行相同的SQL时不访问数据库,而是直接从缓存中获取结果,从而减少服务器的压力。

  • 什么是缓存?
    存在于内存中的一块数据。
  • 缓存有什么作用?
    减少程序和数据库的交互,提高查询效率,降低服务器和数据库的压力。
  • 什么样的数据使用缓存?
    经常查询但不常改变的,改变后对结果影响不大的数据。
  • MyBatis缓存分为哪几类?
    一级缓存和二级缓存
  • 如何判断两次Sql是相同的?
  1. 查询的Sql语句相同
  2. 传递的参数值相同
  3. 对结果集的要求相同
  4. 预编译的模板Id相同

        一级缓存

MyBatis框架总结_第33张图片

  • MyBatis一级缓存也叫本地缓存。SqlSession对象中包含一个Executor对象,Executor对象中包含一个PerpetualCache对象,在该对象存放一级缓存数据。
  • 由于一级缓存是在SqlSession对象中,所以只有使用同一个SqlSession对象操作数据库时才能共享一级缓存
  • MyBatis的一级缓存是默认开启的,不需要任何的配置。

        清空一级缓存

进行以下操作可以清空MyBatis一级缓存:

  1. SqlSession调用close():操作后SqlSession对象不可用,该对象的缓存数据也不可用。
  2. SqlSession调用clearCache()/commit():操作会清空一级缓存数据。
  3. SqlSession调用增删改方法:操作会清空一级缓存数据,因为增删改后数据库发生改变,缓存数据将不准确。

        二级缓存

MyBatis框架总结_第34张图片

  • MyBatis二级缓存也叫全局缓存。数据存放在SqlSessionFactory中,只要是同一个工厂对象创建的SqlSession,在进行查询时都能共享数据。一般在项目中只有一个SqlSessionFactory对象,所以二级缓存的数据是全项目共享的。
  • MyBatis一级缓存存放的是对象,二级缓存存放的是对象的数据。所以要求二级缓存存放的POJO必须是是要实现Serializable接口
  • MyBatis二级缓存默认不开启,手动开启后数据先存放在一级缓存中,
  • 只有一级缓存数据失效后数据才会存到二级缓存中
    SqlSession调用clearCache()无法将数据存到二级缓存中。 

        开启二级缓存

开启二级缓存需要两步:

MyBatis配置文件添加如下设置:

  name="cacheEnabled" value="true"/>

 

映射文件添加标签,该映射文件下的所有方法都支持二级缓存。
如果查询到的集合中对象过多,二级缓存只能缓存1024个对象引用。可以通过标签的size属性修改该数量。
size="2048"/>

        测试二级缓存

1、持久层创建方法

    //根据id查询
    User findById(int id);

2、映射文件添加对应标签

    

 3、测试

    @Test
    public void testFindById() throws IOException {
        InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(is);
        SqlSession session1 = factory.openSession();
        SqlSession session2 = factory.openSession();
        UserMapper mapper1 = session1.getMapper(UserMapper.class);
        UserMapper mapper2 = session2.getMapper(UserMapper.class);

        User user1 = mapper1.findById(1);
        System.out.println(user1);
        session1.close();
        System.out.println("--------------------------");
        User user2 = mapper2.findById(1);
        System.out.println(user2);
    }

通过结果我们可以发现,mybatis只和数据库交互了一次,第二次是直接在缓存中获取的 

MyBatis框架总结_第35张图片

关联查询

MyBatis的关联查询分为一对一关联查询和一对多关联查询。

  • 查询对象时,将关联的另一个对象查询出来,就是一对一关联查询
  • 查询对象时,将关联的另一个对象的集合查询出来,就是一对多关联查询

        association标签

mybatis进行多表查询时会用上association标签,它的属性包括property,column,javaType等,它的作用是让实体类对象与数据库表的列相互对应,以便让mybatis可以进行多表查询。

使用的场景:association的使用场景为1:1的情况













        collection标签

作用:在Mybaits中collection标签是用来实现连表查询的

使用的场景:collection的使用场景为1:n和n:n两种情况

添加的内容:使用collection的时候需要在类中添加关联集合(查询哪个类就在哪个类中添加)。

 



    

    

    

    

  

所以在关联查询之前我们需要有几张有关联的表,并且创建对应的POJO类

MyBatis框架总结_第36张图片

import java.util.List;

public class Classes {
    private Integer cid;
    private String className;
    private List studentList;

    public Classes(Integer cid, String className, List studentList) {
        this.cid = cid;
        this.className = className;
        this.studentList = studentList;
    }

    public Integer getCid() {
        return cid;
    }

    public void setCid(Integer cid) {
        this.cid = cid;
    }

    public String getClassName() {
        return className;
    }

    public void setClassName(String className) {
        this.className = className;
    }

    public List getStudentList() {
        return studentList;
    }

    public void setStudentList(List studentList) {
        this.studentList = studentList;
    }

    @Override
    public String toString() {
        return "Classes{" +
                "cid=" + cid +
                ", className='" + className + '\'' +
                ", studentList=" + studentList +
                '}';
    }
}
public class Student {
    private Integer sid;
    private String name;
    private int age;
    private String sex;
    private Classes classes;

    public Student(Integer sid, String name, int age, String sex, Classes classes) {
        this.sid = sid;
        this.name = name;
        this.age = age;
        this.sex = sex;
        this.classes = classes;
    }

    public Integer getSid() {
        return sid;
    }

    public void setSid(Integer sid) {
        this.sid = sid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Classes getClasses() {
        return classes;
    }

    public void setClasses(Classes classes) {
        this.classes = classes;
    }

    @Override
    public String toString() {
        return "Student{" +
                "sid=" + sid +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                ", classes=" + classes +
                '}';
    }
}

        一对一关联查询

教室对于学生来说就是一对一的,因为一个学生只能在一个教室,所以我们来关联查询学生,当查出学生时把学生的教室一起查出来

1、持久层创建方法

import java.util.List;

public interface StudentMapper {
    //查找所有学生
    List findAll();
}

2、映射文件添加对应标签

    
        
        
        
        
        
        
        
        
            
            
            
            
        
    
    

 3、测试

    public class TestStudentMapper {
    InputStream is = null;
    SqlSession session = null;
    StudentMapper studentMapper = null;
    @Before
    public void before() throws IOException {
        //读取核心配置文件
        is = Resources.getResourceAsStream("SqlMapConfig.xml");
        //创建SqlSessionFactoryBuilder对象
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        //SqlSessionFactoryBuilder对象创建SqlSessionFactory对象
        SqlSessionFactory factory = builder.build(is);
        //SqlSessionFactory对象创建SqlSession对象
        session = factory.openSession();
        //获取代理对象
        studentMapper = session.getMapper(StudentMapper.class);
    }
    @After
    public void after() throws IOException {
        //释放资源
        session.close();
        is.close();
    }

    @Test
    public void testFindAll(){
        List list = studentMapper.findAll();
        list.forEach(System.out::println);
    }
}

 MyBatis框架总结_第37张图片

        一对多关联查询

学生对于教室来说就是一对多的关系,一个教室可以有多个学生,所以我们查询教室顺便把这个教室的所有学生查询出来

1、持久层接口创建方法

    //查询所有教室
    List findAll();

2、映射文件添加对应标签




    
        
        
        
        
        
        
            
            
            
            
            
            
        
    
    

 3、测试

public class TestClassesMapper {
    InputStream is = null;
    SqlSession session = null;
    ClassesMapper classesMapper = null;
    @Before
    public void before() throws IOException {
        //读取核心配置文件
        is = Resources.getResourceAsStream("SqlMapConfig.xml");
        //创建SqlSessionFactoryBuilder对象
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        //SqlSessionFactoryBuilder对象创建SqlSessionFactory对象
        SqlSessionFactory factory = builder.build(is);
        //SqlSessionFactory对象创建SqlSession对象
        session = factory.openSession();
        //获取代理对象
        classesMapper = session.getMapper(ClassesMapper.class);
    }
    @After
    public void after() throws IOException {
        //释放资源
        session.close();
        is.close();
    }

    @Test
    public void testFindAll(){
        List list = classesMapper.findAll();
        list.forEach(System.out::println);
    }
}

 

        多对多关联查询

MyBatis多对多关联查询本质就是两个一对多关联查询。

例如有老师类和班级类:

一个老师对应多个班级,也就是老师类中有一个班级集合属性。

一个班级对应多个老师,也就是班级类中有一个老师集合属性。

所以我们需要有三张表,老师表、教室表和中间表,并且创建对应的POJO类

MyBatis框架总结_第38张图片

public class Teacher {
    private Integer tid;
    private String tname;
    private List classesList;

    public Teacher(Integer tid, String tname, List classesList) {
        this.tid = tid;
        this.tname = tname;
        this.classesList = classesList;
    }

    public Teacher() {
    }

    public Integer getTid() {
        return tid;
    }

    public void setTid(Integer tid) {
        this.tid = tid;
    }

    public String getTname() {
        return tname;
    }

    public void setTname(String tname) {
        this.tname = tname;
    }

    public List getClassesList() {
        return classesList;
    }

    public void setClassesList(List classesList) {
        this.classesList = classesList;
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "tid=" + tid +
                ", tname='" + tname + '\'' +
                ", classesList=" + classesList +
                '}';
    }
}
public class Classes {
    private Integer cid;
    private String className;
    private List studentList;
    private List teacherList;

    public Classes(Integer cid, String className, List studentList, List teacherList) {
        this.cid = cid;
        this.className = className;
        this.studentList = studentList;
        this.teacherList = teacherList;
    }

    public Classes() {
    }

    public Integer getCid() {
        return cid;
    }

    public void setCid(Integer cid) {
        this.cid = cid;
    }

    public String getClassName() {
        return className;
    }

    public void setClassName(String className) {
        this.className = className;
    }

    public List getStudentList() {
        return studentList;
    }

    public void setStudentList(List studentList) {
        this.studentList = studentList;
    }

    public List getTeacherList() {
        return teacherList;
    }

    public void setTeacherList(List teacherList) {
        this.teacherList = teacherList;
    }

    @Override
    public String toString() {
        return "Classes{" +
                "cid=" + cid +
                ", className='" + className + '\'' +
                ", studentList=" + studentList +
                ", teacherList=" + teacherList +
                '}';
    }
}

1、持久层创建方法

public interface TeacherMapper {
    //查询所有老师
    List findAll();
}

2、映射文件添加对应标签

    


    
        
        
        
        
        
        
            
            
            
            
        
    
    


3、测试

    public class TestTeacherMapper {
    InputStream is = null;
    SqlSession session = null;
    TeacherMapper teacherMapper = null;
    @Before
    public void before() throws IOException {
        //读取核心配置文件
        is = Resources.getResourceAsStream("SqlMapConfig.xml");
        //创建SqlSessionFactoryBuilder对象
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        //SqlSessionFactoryBuilder对象创建SqlSessionFactory对象
        SqlSessionFactory factory = builder.build(is);
        //SqlSessionFactory对象创建SqlSession对象
        session = factory.openSession();
        //获取代理对象
        teacherMapper = session.getMapper(TeacherMapper.class);
    }
    @After
    public void after() throws IOException {
        //释放资源
        session.close();
        is.close();
    }

    @Test
    public void testFindAll(){
        List list = teacherMapper.findAll();
        list.forEach(System.out::println);
    }
}

MyBatis框架总结_第39张图片  

分解式查询

在MyBatis多表查询中,使用连接查询时一个Sql语句就可以查询出所有的数据。如:

# 查询班级时关联查询出学生

select *

  from classes

  left join student

  on student.classId = classes.cid

也可以使用分解式查询,即将一个连接Sql语句分解为多条Sql语句,如:

# 查询班级时关联查询出学生

select * from classes;

select * from student where classId =1;

select * from student where classId =2;

这种写法也叫N+1查询。

连接查询:

  • 优点:降低查询次数,从而提高查询效率
  • 缺点:如果查询返回的结果集较多会消耗内存空间。

N+1查询:

  • 优点:结果集分步获取,节省内存空间
  • 缺点:由于需要执行多次查询,相比连接查询效率低。

        一对多

分解式查询需要两个方法,以查询教室里的所有学生为例

Classes实体类里需要有findAll方法,这是我们要调用的方法

Student类里需要有根据关联列查询学生的方法,这是映射文件要调用的方法

1、持久层接口创建方法

public interface ClassesMapper {

    //查询所有教室
    List findAll();

}
public interface StudentMapper {
    //查找所有学生
    List findAll();

    //根据classId查找学生
    Student findByCid(int classId);
}

 2、映射文件添加对应标签

ClassesMapper.xml

    
    
        
        
        
        
        
        
        
    
    

StudentMapper.xml

    

 3、测试

public class TestClassesMapper {
    InputStream is = null;
    SqlSession session = null;
    ClassesMapper classesMapper = null;
    @Before
    public void before() throws IOException {
        //读取核心配置文件
        is = Resources.getResourceAsStream("SqlMapConfig.xml");
        //创建SqlSessionFactoryBuilder对象
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        //SqlSessionFactoryBuilder对象创建SqlSessionFactory对象
        SqlSessionFactory factory = builder.build(is);
        //SqlSessionFactory对象创建SqlSession对象
        session = factory.openSession();
        //获取代理对象
        classesMapper = session.getMapper(ClassesMapper.class);
    }
    @After
    public void after() throws IOException {
        //释放资源
        session.close();
        is.close();
    }

    @Test
    public void testFindAll(){
        List list = classesMapper.findAll();
        list.forEach(System.out::println);
    }
}

 MyBatis框架总结_第40张图片

        select属性和column属性

select:从表查询调用的方法 

column:调用方法时传入的参数字段

        延迟加载

分解式查询又分为两种加载方式:

  • 立即加载:在查询主表时就执行所有的Sql语句。
  • 延迟加载:又叫懒加载,首先执行主表的查询语句,使用从表数据时才触发从表的查询语句。

延迟加载在获取关联数据时速度较慢,但可以节约资源,即用即取。

开启延迟加载

  • 设置所有的N+1查询都为延迟加载:

        name="lazyLoadingEnabled" value="true"/>
  • 设置某个方法为延迟加载:
    中添加fetchType属性设置加载方式。lazy:延迟加载;eager:立即加载。

 一般情况下,一对多查询使用延迟加载,一对一查询使用立即加载。

注解开发

        环境搭配

MyBatis可以使用注解替代映射文件。映射文件的作用就是定义Sql语句,可以在持久层接口上使用@Select/@Delete/@Insert/@Update定义Sql语句,这样就不需要使用映射文件了。

  1. 创建maven工程,引入依赖
  2. 创建mybatis核心配置文件SqlMapConfig.xml
  3. 将log4j.properties文件放入resources中,让控制台打印SQL语句。
  4. 创建实体类
  5. 创建持久层接口,并在接口方法上定义Sql语句

public interface UserMapper {
    @Select("select * from user")
    List findAll();

}
public class TestUserMapper {
    InputStream is = null;
    SqlSession session = null;
    UserMapper userMapper = null;
    @Before
    public void before() throws IOException {
        //读取核心配置文件
        is = Resources.getResourceAsStream("SqlMapConfig.xml");
        //创建SqlSessionFactoryBuilder对象
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        //SqlSessionFactoryBuilder对象创建SqlSessionFactory对象
        SqlSessionFactory factory = builder.build(is);
        //SqlSessionFactory对象创建SqlSession对象
        session = factory.openSession();
        //获取代理对象
        userMapper = session.getMapper(UserMapper.class);
    }
    @After
    public void after() throws IOException {
        //释放资源
        session.close();
        is.close();
    }
    @Test
    public void testFindAll() throws IOException {

        //调用方法
        List list = userMapper.findAll();
        list.forEach(System.out::println);
    }

}

MyBatis框架总结_第41张图片

        增删改查注解

public interface UserMapper {
    @Select("select * from user")
    List findAll();

    //新增
    @Insert("insert into user values(id=#{id},username1 = #{username1},sex1=#{sex1},address1=#{address1})")
    void insert(User user);
 @Test
    public void testInsert() throws IOException {
        User user = new User("索隆","男","海贼王");
        userMapper.insert(user);
        System.out.println(user);
        //事务提交
        session.commit();
    }

MyBatis框架总结_第42张图片

        主键回填注解@SelectKey

当某个键为主键且为自动增长时,可以使用@SelectKey注解回填

@SelectKey

(keyColumn = "id", keyProperty = "id", resultType = int.class,before = false, statement = "SELECT LAST_INSERT_ID()")

keyProperty:主键属性名,

keyColumn:主键列名,

resultType:主键类型,

statement调用的方法

 

//新增
    @SelectKey(keyProperty = "id",keyColumn = "id",resultType = int.class,statement = "SELECT LAST_INSERT_ID()", before = false)
    @Insert("insert into user(username1,sex1,address1) values(#{username1},#{sex1},#{address1})")
    void insert(User user);
 @Test
    public void testInsert() throws IOException {
        User user = new User("娜美","女","海贼王");
        userMapper.insert(user);
        System.out.println(user);
        //事务提交
        session.commit();
    }

此时我们并没有给user设置id,但是通过该控制台我们可以发现mybatis插入数据后,主键回填了

MyBatis框架总结_第43张图片

        动态sql

1、使用脚本标签

Sql嵌套在

你可能感兴趣的:(mybatis,java,mysql,后端,apache)