多对多查询以及缓存机制

个人理解:多对多的查询其实相当于一对多查询,只是添加一个中间表,其思路跟一对多的用法一样;

MyBatis的查询缓存分为一级缓存和二级缓存。

一级缓存:如果同一个SqlSession对象多次执行完全相同的SQL语句,在执行第一次完成后,Mybatis将会将查询的结果写入到一级缓存中,在此后的执行中除了执行插入、更新、删除操作,当第二次执行相同的查询语句时,MyBatis会直接读取一级缓存中的数据,而不用再去数据库查询,从而提高数据库的查询效率。

二级缓存:相同的Mapper类使用相同的SQL语句,如果SqlSession不同的话,则两个SqlSession查询数据时会向数据库发送两次语句,这样会降低数据库的查询效率,同时二级缓存跟一级缓存相比二级缓存的范围更大,多个SqlSession可以共用二级缓存,并且二级缓存可以自定义缓存资源,同样当程序执行插入、更新、删除操作后,会更新缓存中的数据。

目录

一、创建数据库

二、导入相关依

三、创建日志文件

四、创建POJO实体

五、创建数据库连接信息配置文件

六、创建映射文件

七、创建MyBatis的核心配置文件

八、创建MyBatis的工具类

九、创建测试类

十、运行结果

一、创建数据库

  1. 连接数据库:在命令窗口输入mysql -u root -p回车然后输入密码回车
  2. 创建数据库:create database mybatis;
  3. 创建表和插入数据
    use mybatis;
    
    create table tb_orders(
        id int(10) primary key auto_increment,
        number varchar(32) not null,
        user_id int(10) not null,
        foreign key(user_id) references tb_user(id));
    insert into tb_orders values(1,'1000001',1),(2,'1000002',1),(3,'1000003',2);
    
    
    create table tb_product(
       id int(10) primary key auto_increment,
       name varchar(32),
       price double);
    insert into tb_product values(1,'JavaEE',59.8),(2,'Mysql',34),(3,'软件测试',44);
    
    
    create table tb_ordersitem(
         id int(32) primary key auto_increment,
         orders_id int(32),
         product_id int(32),
         foreign key(orders_id) references tb_orders(id),
         foreign key (product_id) references tb_product(id));
    insert into tb_ordersitem values(1,1,1),(2,1,3),(3,3,3);

二、导入相关依

pom.xml代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>Mybatis的关系映射</artifactId>
    <version>1.0-SNAPSHOT</version>
        <dependencies>
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>3.5.2</version>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>8.0.11</version>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
                <scope>compile</scope>
            </dependency>
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>1.2.17</version>
            </dependency>
        </dependencies>
        <build>
            <resources>
                <resource>
                    <directory>src/main/java</directory>
                    <includes>
                        <include>**/*.properties</include>
                        <include>**/*.xml</include>
                    </includes>
                    <filtering>true</filtering>
                </resource>
            </resources>
        </build>
</project>

三、创建日志文件

在项目的src/main/resources目录下创建日志文件log4j.properties,代码如下:

log4j.rootLogger=DEBUG,console,FILE
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.threshold=INFO
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%5p] - %c -%F(%L) -%m%n
log4j.appender.FILE=org.apache.log4j.RollingFileAppender
log4j.appender.FILE.Append=true
log4j.appender.FILE.File=logs/log4jtest.log
log4j.appender.FILE.Threshold=INFO
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%5p] - %c -%F(%L) -%m%n
log4j.appender.FILE.MaxFileSize=10MB

四、创建POJO实体

在src/main/java目录下创建一个com.itheima.pojo包,在包下分别创建Orders.java、Product.java,同时使该实体类序列化,否则在开启二级缓存会报错,其代码如下:

Orders.java:

package com.itheima.pojo;

import java.io.Serializable;
import java.util.List;

public class Orders implements Serializable {
    private Integer id;//订单id
    private String number;//订单编号
    private List<Product>  productList;//商品的信息

    public Integer getId() {
        return id;
    }

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

    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number;
    }

    public List<Product> getProductList() {
        return productList;
    }

    public void setProductList(List<Product> productList) {
        this.productList = productList;
    }

    @Override
    public String toString() {
        return "Orders{" +
                "id=" + id +
                ", number='" + number + '\'' +
                ", productList=" + productList +
                '}';
    }
}

Product.java: 

package com.itheima.pojo;

import java.io.Serializable;
import java.util.List;

public class Product implements Serializable {
    private Integer id;//商品id
    private String name;//商品名称
    private Double price;//商品价格
    private List<Orders> ordersList;

    public Integer getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public Double getPrice() {
        return price;
    }

    public void setPrice(Double price) {
        this.price = price;
    }

    public List<Orders> getOrdersList() {
        return ordersList;
    }

    public void setOrdersList(List<Orders> ordersList) {
        this.ordersList = ordersList;
    }

    @Override
    public String toString() {
        return "Product{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", price=" + price +
                ", ordersList=" + ordersList +
                '}';
    }
}

五、创建数据库连接信息配置文件

在项目的src/main/resources目录下创建数据库连接信息配置文件,其命名为:db.properties代码如下:

mysql.driver=com.mysql.cj.jdbc.Driver
mysql.url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&\
  characterEncoding=utf-8&useUnicode=true&useSSL=false
mysql.username=root
mysql.password=123456

六、创建映射文件

在项目的src/main/resources目录下创建一个mapper文件夹,分别在该文件夹下创建OrdersMapper.xml、ProductMapper.xml,该文件主要用于配置SQL语句和Java对象之间的映射,使SQL语句查询出来的数据能够被封装成Java对象,其代码分别为:

ProductMapper.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="mapper.ProductMapper">
    <select id="findproduct" parameterType="Integer" resultType="com.itheima.pojo.Product">
        select * from tb_product where id in(
        select product_id from tb_ordersitem where orders_id=#{id}
        )
    </select>
</mapper>

OrdersMapper.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">
<!--collection元素来处理一对一关联关系,其属性property用于指定映射到的实体类对象的属性,与表字段一一对应,column用于指定表中对应的字段
javaType用于指定映射到实体对象的属性的类型,ofType属性表示集合中的数据类型,jdbcType用于指定数据表中对应字段的类型,autoMapping用于指定是否自动映射-->
<mapper namespace="mapper.OrdersMapper">
   <!--开启二级缓存,cache的属性有:flushInterval是刷新间隔时间,单位为毫秒-->
    <cache></cache>
    <!--多对多嵌套查询方式-->
    <select id="findorder1" parameterType="Integer" resultMap="ProductwithOrdersResult1">
        select * from tb_orders where id=#{id}
    </select>
    <resultMap id="ProductwithOrdersResult1" type="com.itheima.pojo.Orders">
        <id property="id" column="id"/>
        <result property="number" column="number"/>
        <collection property="productList" column="id" ofType="com.itheima.pojo.Product"
        select="mapper.ProductMapper.findproduct"/>
    </resultMap>

    <!--多对多嵌套结果方式-->
    <select id="findorder2" parameterType="Integer" resultMap="ProductwithOrdersResult2">
        select o.id,o.number,p.id as pid,p.name,p.price from tb_orders o,tb_ordersitem os,tb_product p
        where os.orders_id=o.id and os.product_id=p.id and o.id=#{id}
    </select>
    <resultMap id="ProductwithOrdersResult2" type="com.itheima.pojo.Orders">
    <id property="id" column="id"/>
    <result property="number" column="number"/>
    <collection property="productList"  ofType="com.itheima.pojo.Product">
        <id property="id" column="pid"/>
        <result property="name" column="name"/>
        <result property="price" column="price"/>
    </collection>
    </resultMap>
</mapper>

七、创建MyBatis的核心配置文件

在项目的src/main/resources目录下创建Mybatis的核心配置文件,其命名为: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>
    <!--引入数据库连接信息-->
    <properties resource="db.properties" />
    <settings>
        <!--设置打印信息 -->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
        <!--开启二级缓存的全局配置-->
        <setting name="cacheEnabled" value="true"/>
    </settings>
    <!--使用扫描包的形式定义别名 -->
    <typeAliases>
        <package name="com.itheima.pojo"/>
    </typeAliases>
    <!--配置运行环境,可以选择事务管理器和数据源,environments有两个子元素:分别为事务管理transactionManager元素和数据源dataSource元素-->
    <!--事务管理transactionManager元素有两种类型,分别为JDBC和MANAGED,JDBC配置直接提交和回滚设置-->
    <!--数据源dataSource元素配置不提交或回滚一个连接,而是让容器管理事务的整个生命周期,有无连接池UNPOOLED、连接池POOLED和JNDI-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <!-- 数据库驱动-->
                <property name="driver" value="${mysql.driver}"/>
                <!--连接数据库的url-->
                <property name="url" value="${mysql.url}"/>
                <!-- 连接数据库的用户名-->
                <property name="username" value="${mysql.username}"/>
                <!-- 连接数据库的密码-->
                <property name="password" value="${mysql.password}"/>
            </dataSource>
        </environment>
    </environments>
    <!--mapper的映射文件有4种,分别为类路径引入、本地路径引入、接口类引入、包名引入-->
    <mappers>
        <mapper resource="mapper/OrdersMapper.xml"/>
        <mapper resource="mapper/ProductMapper.xml"/>
    </mappers>
</configuration>

八、创建MyBatis的工具类

在src/main/java目录下创建一个com.itheima.utils包,在包下创建一个MyBatisUtils.java工具类,代码如下:

package com.itheima.utils;

import java.io.Reader;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

/**
 * 工具类
 */

public class MyBatisUtils {
    private static SqlSessionFactory sqlSessionFactory = null;

    // 初始化SqlSessionFactory对象
    static {
        try {
            // 使用MyBatis提供的Resources类加载MyBatis的配置文件
            Reader reader =
                    Resources.getResourceAsReader("mybatis-config.xml");
            // 构建SqlSessionFactory工厂
            sqlSessionFactory =
                    new SqlSessionFactoryBuilder().build(reader);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 获取SqlSession对象的静态方法
    public static SqlSession getSession() {
        return sqlSessionFactory.openSession();
    }
}

九、创建测试类

在src/main/java目录下创建一个com.itheima.test包,在包下创建一个MybatisTest.java测试类,代码如下:

多对多查询测试:

package com.itheima.test;

import com.itheima.pojo.Orders;
import com.itheima.utils.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

public class mybatisTest {
    @Test
    public void findByIdTest1() {
        // 通过工具类生成SqlSession对象
        SqlSession session = MyBatisUtils.getSession();
        Orders orders1 =
                session.selectOne("mapper.OrdersMapper.findorder1", 1);
        Orders orders2 =
                session.selectOne("mapper.OrdersMapper.findorder2", 1);
        System.out.println("多对多嵌套查询方式\t");
        System.out.println(orders1);
        System.out.println("多对多嵌套结果方式\t");
        System.out.println(orders2);
        session.commit();
        // 关闭SqlSession
        session.close();

    }
}

一级缓存测试:

package com.itheima.test;

import com.itheima.pojo.Orders;
import com.itheima.utils.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

public class mybatisTest {
    @Test
    public void findByIdTest1() {
        // 通过工具类生成SqlSession对象
        SqlSession session = MyBatisUtils.getSession();
        Orders orders1 =
                session.selectOne("mapper.OrdersMapper.findorder2", 1);
        Orders orders2 =
                session.selectOne("mapper.OrdersMapper.findorder2", 1);
        System.out.println("一级缓存");
        System.out.println(orders1);
        System.out.println(orders2);
        session.commit();
        // 关闭SqlSession
        session.close();

    }
}

 二级缓存测试:

先在配置文件上通过<setting>元素开启二级缓存的全局配置

<settings>
<!--开启二级缓存的全局配置-->
    <setting name="cacheEnabled" value="true"/>
</settings>

 在OrdersMapper.xml的<mappers>下添加<cache>元素开启Mapper下的二级缓存

<cache/>
package com.itheima.test;

import com.itheima.pojo.Orders;
import com.itheima.utils.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

public class mybatisTest {
    @Test
    public void findByIdTest1() {
        // 通过工具类生成SqlSession对象
        SqlSession session1 = MyBatisUtils.getSession();
        SqlSession session2 = MyBatisUtils.getSession();
        //第一次查询
        Orders orders1 =
                session1.selectOne("mapper.OrdersMapper.findorder2", 1);
        System.out.println("第一次查询");
        System.out.println(orders1);
        session1.commit();
        // 关闭SqlSession
        session1.close();
        //第二次查询
        Orders orders2 =
                session2.selectOne("mapper.OrdersMapper.findorder2", 1);
        System.out.println("第二次查询");
        System.out.println(orders2);
        session2.commit();
        // 关闭SqlSession
        session2.close();

    }
}

十、运行结果

多对多查询结果:

一级缓存测试结果:

通过程序查询的结果可知,当通过第一次查询id=1的信息时,程序向数据库发送了SQL语句,当程序再次执行时,程序没有再向数据库发送SQL语句,但还是可以查询到数据,结果跟第一次的一样,这说明,第二次的程序直接从一级缓存中获取到要查询的数据。

注意:当程序对数据库执行了插入、更新、删除操作后,MyBatis会清空一级缓存中的内容。

 二级缓存测试结果:

通过程序查询的结果可知,当通过第一个SqlSession对象session1查询id=1的信息时,此时缓存命中率为0,程序向数据库发送了SQL语句,当第二次SqlSession对象session2执行程序时,程序没有再向数据库发送SQL语句,同时此时的缓存命中率为0.5,而且两次查询结果跟第一次的一样,这说明,第二次的程序直接从二级缓存中获取到要查询的数据。

猜你喜欢

转载自blog.csdn.net/weixin_47365427/article/details/125753173
今日推荐