Mybatis分解式查询

Posted 会洗碗的CV工程师

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mybatis分解式查询相关的知识,希望对你有一定的参考价值。

目录

 

一、Mybatis一对多分解式查询

1. 新增持久层接口方法

2. 新增映射文件对应的标签

3. 新增测试方法

4. 运行效果

二、Mybatis一对一分解式查询

1. 新增持久层接口方法

2. 新增映射文件对应的标签

3. 新增测试方法

4. 运行效果

三、Mybatis延迟加载

1. 开启延迟加载

2. 测试延迟加载


 

一、Mybatis一对多分解式查询

分解式查询就是将一条Sql语句拆分成多条

在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查询

优点:结果集分步获取,节省内存空间。

缺点:由于需要执行多次查询,相比连接查询效率低。

我们以查询班级时关联查询出学生为例,使用N+1查询: 

1. 新增持久层接口方法

新增ClassesMapper2.java接口

package com.example.mapper;

import com.example.pojo.Classes;

import java.util.List;

public interface ClassesMapper2 
    List<Classes> findAll();

新增StudentMapper.java接口

package com.example.mapper;

import com.example.pojo.Student;

import java.util.List;

public interface StudentMapper2 
    List<Student> findByClassId(int classId);

2. 新增映射文件对应的标签

新增ClassesMapper.xml映射文件

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

    <!-- 自定义映射关系 -->
    <resultMap id="myClassesMapper" type="com.example.pojo.Classes">
        <id property="cid" column="cid"/>
        <result property="className" column="className"/>
        <!-- select: 从表查询调用的方法 column:调用方法时传入的参数字段 -->
        <collection property="studentList" column="cid"
                    ofType="com.example.pojo.Student"
                    select="com.example.mapper.StudentMapper2.findByClassId"/>
    </resultMap>

    <select id="findAll" resultMap="myClassesMapper">
        select * from classes
    </select>
</mapper>

新增StudentMapper.xml映射文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.StudentMapper2">
    <select id="findByClassId"
            parameterType="int"
            resultType="com.example.pojo.Student">
        select * from student where classId = $classId
    </select>
</mapper>

3. 新增测试方法

// 分解式查询一对多
    @Test
    public void testFindAllClasses2()
        ClassesMapper2 classesMapper2 = session.getMapper(ClassesMapper2.class);
        List<Classes> all = classesMapper2.findAll();
        all.forEach(System.out::println);
    

4. 运行效果

        在这里我们可以看到确实是分开了了两条查询语句

二、Mybatis一对一分解式查询

查询学生时关联查询出班级也可以使用分解式查询,首先将查询语句分开:

select * from student;

select * from classes where cid = ?

1. 新增持久层接口方法

新增StudentMapper3.java接口

package com.example.mapper;

import com.example.pojo.Student;

import java.util.List;

public interface StudentMapper3 
    // 查询所有学生
    List<Student> findAll();

新增ClassesMapper3.java接口 

package com.example.mapper;

import com.example.pojo.Classes;

import java.util.List;

public interface ClassesMapper3 
    // 根据ID查询班级
    Classes findById(int cid);

2. 新增映射文件对应的标签

新增ClassesMapper.xml映射文件

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

    <select id="findByCid"
            resultType="com.example.pojo.Classes"
            parameterType="int">
        select * from classes where cid = $cid
    </select>
</mapper>

新增StudentMapper.xml映射文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.StudentMapper3">
    <!-- 自定义映射关系 -->
    <resultMap id="MyClassesMapper" type="com.example.pojo.Student">
        <id property="sid" column="sid"/>
        <result property="name" column="name"/>
        <result property="age" column="age"/>
        <result property="sex" column="sex"/>
        <!-- select: 从表查询调用的方法 column:调用方法时传入的参数字段 -->
        <association property="classes" column="classId"
                     javaType="com.example.pojo.Classes"
                     select="com.example.mapper.ClassesMapper3.findByCid"/>
    </resultMap>

    <select id="findAll" resultMap="MyClassesMapper">
        select * from student
    </select>
</mapper>

3. 新增测试方法

// 分解式查询一对一
    @Test
    public void testFindAllStudent2()
        StudentMapper3 studentMapper3 = session.getMapper(StudentMapper3.class);
        List<Student> all = studentMapper3.findAll();
        all.forEach(System.out::println);
    

4. 运行效果

OK,确实是查询出来了。 

三、Mybatis延迟加载

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

        立即加载:在查询主表时就执行所有的Sql语句。

        延迟加载:又叫懒加载,首先执行主表的查询语句,使用从表数据时才触发从表的查询语句。

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

1. 开启延迟加载

设置所有的N+1查询都为延迟加载,在Mybatis配置文件中添加以下设置:

    <settings>
        <setting name="lazyLoadingEnabled" value="true"/>
        <setting name="lazyLoadTriggerMethods" value=""/>
    </settings>

设置某个方法为延迟加载:

在 <association> 、 <collection> 中添加fetchType属性设置加载方式。

lazy:延迟加载;eager:立即加载。

2. 测试延迟加载

         由于打印对象时会调用对象的 toString 方法, toString 方法默认会触发延迟加载的查询,所以我们无法测试出延迟加载的效果。

        我们在配置文件设置lazyLoadTriggerMethods属性,该属性指定对象的什么方法触发延迟加载,设置为空字符串即可。  

测试方法: 

    @Test
    public void testFindAllClasses2()
        ClassesMapper2 classesMapper2 = session.getMapper(ClassesMapper2.class);
        List<Classes> all = classesMapper2.findAll();
        all.forEach(System.out::println);
        System.out.println("---------------------");
        System.out.println(all.get(0).getStudentList());
    

运行结果:

        OK,这个很明显了,就是第一次查询的时候没有将学生列表查询出来,等到后续需要查询的时候载查询。     

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

 

[mybatis]接口式编程

While this approach works, and is familiar to users of previous versions of MyBatis, there is now a
cleaner approach. Using an interface (e.g. BlogMapper.class) that properly describes the parameter
and return value for a given statement, you can now execute cleaner and more type safe code, without
error prone string literals and casting.

小结

  • 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抽取出来

process

项目结构:

pom.xml引入依赖:

    <dependencies>

        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.6</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>RELEASE</version>
            <scope>compile</scope>
        </dependency>


        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.22</version>
        </dependency>



    </dependencies>

Employee实体类:

package com.atguigu.mybatis.bean;



public class Employee 

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

    @Override
    public String toString() 
        return "Employee" +
                "id=" + id +
                ", lastName='" + lastName + '\\'' +
                ", email='" + email + '\\'' +
                ", gender='" + gender + '\\'' +
                '';
    

    public Employee() 
    

    public Employee(Integer id, String lastName, String email, String gender) 
        this.id = id;
        this.lastName = lastName;
        this.email = email;
        this.gender = gender;
    

    public Integer getId() 
        return id;
    

    public void setId(Integer id) 
        this.id = id;
    

    public String getLastName() 
        return lastName;
    

    public void setLastName(String lastName) 
        this.lastName = lastName;
    

    public String getEmail() 
        return email;
    

    public void setEmail(String email) 
        this.email = email;
    

    public String getGender() 
        return gender;
    

    public void setGender(String gender) 
        this.gender = gender;
    

EmployeeMapper:

package com.atguigu.mybatis.dao;

import com.atguigu.mybatis.bean.Employee;

public interface EmployeeMapper 


    public Employee getEmpById(Integer id);

    


配置文件mybatis-config.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mybatis/mapper/EmployeeMapper.xml"/>
    </mappers>
</configuration>

映射文件EmployeeMapper:

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



    <select id="getEmpById" resultType="com.atguigu.mybatis.bean.Employee">

    select id,last_name lastName,email,gender from tb1_employee where id = #id

</select>
</mapper>

测试类:

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.jupiter.api.Test;

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

public class MybatisTest 

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

    


    @Test
    public void test01() throws IOException 
        //1.获取sqlSessionFactory对象
        SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();

        //2.获取sqlSession对象
        SqlSession sqlSession = sqlSessionFactory.openSession();


        try 
            //3.获取接口的实现类对象
            //会为接口自动的创建一个代理对象,代理对象执行增删改查
            EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);

            Employee empById = mapper.getEmpById(1);

            System.out.println(mapper.getClass());

            System.out.println(empById);
        finally 
            sqlSession.close();
        

    



以上是关于Mybatis分解式查询的主要内容,如果未能解决你的问题,请参考以下文章

Maven 项目使用mybatis的环境搭建-基于xml形式实现查询所有的功能

Maven 项目使用mybatis的环境搭建-基于xml形式实现查询所有的功能

Mybatis

mybatis学习——快速搭建基于maven的项目

什么是mybatis 为什么要使用my batis

什么是mybatis 为什么要使用my batis