Mybatis学习教程③

加载策略及注解开发

一 MyBatis加载策略

1.1 什么是延迟加载

问题

  • 通过前面的学习,我们已经掌握了Mybatis中一对一,一对多,多对多关系的配置及实现,可以实现对象的关联查询。实际开发过程中很多时候我们并不需要总是在加载用户信息时就一定要加载他的订单信息。此时就是我们所说的延迟加载。

举个栗子

* 在一对多中,当我们有一个用户,它有个100个订单
	在查询用户的时候,要不要把关联的订单查出来?
	在查询订单的时候,要不要把关联的用户查出来?
* 回答
	在查询用户时,用户下的订单应该是,什么时候用,什么时候查询(延迟加载)。
	在查询订单时,订单所属的用户信息应该是随着订单一起查询出来(立即加载)。

延迟加载

  • 就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载。
* 优点:
	先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。
	
* 缺点:
	因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗时间,所以可能造成用户等待时间变长,造成用户体验下降。
	
* 在多表中:
	一对多,多对多:通常情况下采用延迟加载
	一对一(多对一):通常情况下采用立即加载
* 注意:
	延迟加载是基于嵌套查询来实现的

1.2 实现

1.2.1 局部延迟加载

  • 在association和collection标签中都有一个fetchType属性,通过修改它的值,可以修改局部的加载策略。
1) User实体
package com.myProject.domain;

import java.util.Date;
import java.util.List;

public class User {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    // 表示多方关系(集合):代表当前用户所具有的订单列表
    private List<Orders> ordersList; // 关联对象是集合,使用collection

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", birthday=" + birthday +
                ", sex='" + sex + '\'' +
                ", address='" + address + '\'' +
                ", ordersList=" + ordersList +
                '}';
    }

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

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

    public Integer getId() {
        return id;
    }

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

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String getSex() {
        return sex;
    }

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

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}
2)Orders实体
package com.myProject.domain;

public class Orders {
    private Integer id;
    private String ordertime;
    private Double total;
    private Integer uid;

    @Override
    public String toString() {
        return "Orders{" +
                "id=" + id +
                ", ordertime='" + ordertime + '\'' +
                ", total=" + total +
                ", uid=" + uid +
                '}';
    }

    public Integer getId() {
        return id;
    }

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

    public String getOrdertime() {
        return ordertime;
    }

    public void setOrdertime(String ordertime) {
        this.ordertime = ordertime;
    }

    public Double getTotal() {
        return total;
    }

    public void setTotal(Double total) {
        this.total = total;
    }

    public Integer getUid() {
        return uid;
    }

    public void setUid(Integer uid) {
        this.uid = uid;
    }
}
3)UserMapper接口
package com.myProject.mapper;

import com.myProject.domain.User;

import java.util.List;

public interface UserMapper {
    /*
    一对多嵌套查询 查询所有用户,与此同时查询出该用户具有的订单
     */
    public List<User> findAllWithOrder();
}
4)UserMapper.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.myProject.mapper.UserMapper">
    <!--一对多嵌套查询 查询所有用户,与此同时查询出该用户具有的订单-->
    <!-- 开启一对多 延迟加载 -->
    <resultMap id="userOrderMap" type="com.myProject.domain.User">
        <id property="id" column="id" />
        <result property="username" column="username" />
        <result property="birthday" column="birthday" />
        <result property="sex" column="sex" />
        <result property="address" column="address" />

        <!--fetchType="lazy": 延迟加载
            fetchType="eager": 立即加载
        -->
        <collection property="ordersList" ofType="com.myProject.domain.Orders" column="id" select="com.myProject.mapper.OrdersMapper.findByUid" fetchType="lazy"/>
    </resultMap>
    <select id="findAllWithOrder" resultMap="userOrderMap">
        SELECT * FROM USER
    </select>
</mapper>
5)OrderMapper接口
package com.myProject.mapper;

import com.myProject.domain.Orders;

import java.util.List;

public interface OrdersMapper {
    /*
    根据uid查询对应订单
     */
    public List<Orders> findByUid(Integer uid);
}
6)OrderMapper.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.myProject.mapper.OrdersMapper">
    <!--根据uid查询对应订单-->
    <select id="findByUid" parameterType="int" resultType="com.myProject.domain.Orders">
        SELECT * FROM orders o where o.uid = #{uid}
    </select>
</mapper>
7 )Mybatis核心配置文件
<?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文件-->
    <properties resource="jdbc.properties"></properties>
    
    <settings>
        <!--所有方法都会延迟加载-->
        <setting name="lazyLoadTriggerMethods" value="toString()"/>
    </settings>
    <!--设置别名-->
    <typeAliases>
        <!--方式二:批量起别名 (包别名) 别名就是类名且不区分大小写,推荐使用-->
        <package name="com.myProject.domain"/>
    </typeAliases>
    <!--environments: 运行环境-->
    <environments default="development">
        <environment id="development">
            <!--当前的事务管理交由JDBC管理-->
            <transactionManager type="JDBC"></transactionManager>
            <!--数据源信息 POOLED:使用mybatis的连接池-->
            <dataSource type="POOLED">
                <!--配置数据库的属性 动态获取JDBC的属性值-->
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>
    <!--引入映射配置文件-->
    <mappers>
        <!--批量加载映射 推荐使用-->
        <package name="com.myProject.mapper"/>
    </mappers>
</configuration>
8)测试代码
package com.myProject.test;

import com.myProject.domain.Orders;
import com.myProject.domain.User;
import com.myProject.mapper.OrdersMapper;
import com.myProject.mapper.UserMapper;
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;
import java.util.List;

public class MybatisTest {
    /*
    一对多嵌套查询 查询所有用户,与此同时查询出该用户具有的订单
     */
    @Test
    public void test() throws IOException {
        // 加载核心配置文件
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");

        // 获得SqlSessionFactory工厂对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);

        // 获得SqlSession会话对象
        SqlSession sqlSession = sqlSessionFactory.openSession();

        // 当前返回的其实就是基于UserMapper所产生的代理对象mapper;底层:JDK动态代理 实际类型:proxy
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        List<User> users = mapper.findAllWithOrder();
        for (User user : users) {
            System.out.println(user);

            System.out.println("========延迟加载的信息的输出开始:=============");
            // 要用到该用户的订单信息
            System.out.println(user.getOrdersList());
            System.out.println("========延迟加载的信息的输出结束=============");
        }

        // 释放资源
        sqlSession.close();
    }
}

1.2.2 设置触发延迟加载的方法

  • 大家在配置了延迟加载策略后,发现即使没有调用关联对象的任何方法,但是在你调用当前对象的equals、clone、hashCode、toString方法时也会触发关联对象的查询。

  • 我们可以在配置文件中使用lazyLoadTriggerMethods配置项覆盖掉上面四个方法。

  <settings>
  <!--所有方法都会延迟加载-->
  <setting name="lazyLoadTriggerMethods" value="toString()"/>
  </settings>

1.2.3 全局延迟加载

  • 在Mybatis的核心配置文件中可以使用setting标签修改全局的加载策略。
  <settings>
      <!--开启全局延迟加载功能-->
      <setting name="lazyLoadingEnabled" value="true"/>
  </settings>

注意

  • 局部的加载策略优先级高于全局的加载策略。
  <?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.myProject.mapper.OrdersMapper">
      <!--一对一嵌套查询: 查询所有订单,与此同时还要查询出每个订单所属的用户信息-->
      <!-- 关闭一对一 延迟加载 -->
      <resultMap id="ordersMap" type= "com.myProject.domain.Orders">
          <id property="id" column="id"/>
          <result property="ordertime" column="ordertime" />
          <result property="total" column="total" />
          <result property="uid" column="uid" />
  
          <!--association: 在进行一对一关联查询配置时,使用association标签进行关联
              property="user": 要封装User实体的属性名
              javaType="com.myProject.domain.User":要封装User实体的属性类型
              问题:1.在呢么去执行第二条sql, 2.如何执行第二条sql的时候,把uid作为参数进行传递
          -->
          <!--
              fetchType="lazy" 懒加载策略
              fetchType="eager" 立即加载策略
          -->
          <association property="user" javaType="user" select="com.myProject.mapper.UserMapper.findById" column="uid" fetchType="eager"/>
      </resultMap>
      <select id="findAllWithUser" resultMap="ordersMap">
          SELECT * FROM orders
      </select>
  </mapper>

二 MyBatis缓存

2.1 为什么使用缓存

  • 当用户频繁查询某些固定的数据时,第一次将这些数据从数据库中查询出来,保存在缓存中。当用户再次查询这些数据时,不用再通过数据库查询,而是去缓存里面查询。减少网络连接和数据库查询带来的损耗,从而提高我们的查询效率,减少高并发访问带来的系统性能问题。

一句话概括: 经常查询一些不经常发生变化的数据,使用缓存来提高查询效率。

  • 像大多数的持久化框架一样,Mybatis也提供了缓存策略,通过缓存策略来减少数据库的查询次数,从而提高性能。 Mybatis中缓存分为一级缓存,二级缓存。

2.2 一级缓存

2.2.1 介绍

  • 一级缓存是SqlSession级别的缓存,是默认开启的
  • 所以在参数和SQL完全一样的情况下,我们使用同一个SqlSession对象调用一个Mapper方法,往往只执行一次SQL,因为使用SelSession第一次查询后,MyBatis会将其放在缓存中,以后再查询的时候,如果没有声明需要刷新,并且缓存没有超时的情况下,SqlSession都会取出当前缓存的数据,而不会再次发送SQL到数据库。

在这里插入图片描述

2.2.2 验证

@Test
public void testOneCache() throws Exception {
	SqlSession sqlSession = MyBatisUtils.openSession();
	UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
	User user1 = userMapper.findById(1);
	System.out.println("第一次查询的用户:" + user1);
	User user2 = userMapper.findById(1);
	System.out.println("第二次查询的用户:" + user2);
	sqlSession.close();
}
  • 我们可以发现,虽然在上面的代码中我们查询了两次,但最后只执行了一次数据库操作,这就是Mybatis提供给我们的一级缓存在起作用了。因为一级缓存的存在,导致第二次查询id为1的记录时,并没有发出sql语句从数据库中查询数据,而是从一级缓存中查询。

  • pom.xml

  <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-log4j12</artifactId>
      <version>1.7.7</version>
  </dependency>
  • log4j.properties
  log4j.rootLogger = debug,console,logfile
  
  ### appender.console 输出到控制台 ###
  log4j.appender.console=org.apache.log4j.ConsoleAppender
  log4j.appender.console.layout=org.apache.log4j.PatternLayout
  log4j.appender.console.layout.ConversionPattern=<%d> %5p (%F:%L) [%t] (%c) - %m%n
  log4j.appender.console.Target=System.out
  
  ### appender.logfile 输出到日志文件 ###
  log4j.appender.logfile=org.apache.log4j.RollingFileAppender
  log4j.appender.logfile.File=SysLog.log
  log4j.appender.logfile.MaxFileSize=500KB
  log4j.appender.logfile.MaxBackupIndex=7
  log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
  log4j.appender.logfile.layout.ConversionPattern=<%d> %p (%F:%L) [%t] %c - %m%n

验证Mybatis默认开启一级缓存

/*
   验证mybatis中的一级缓存
*/
@Test
public void testOneCache() throws IOException {
    // 加载核心配置文件
    InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");

    // 获得SqlSessionFactory工厂对象
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);

    // 获得SqlSession会话对象
    SqlSession sqlSession = sqlSessionFactory.openSession();

    // 当前返回的其实就是基于UserMapper所产生的代理对象mapper;底层:JDK动态代理 实际类型:proxy
    UserMapper usermapper = sqlSession.getMapper(UserMapper.class);

    // 第一次查询,查询的是数据库
    List<User> users1 = usermapper.findAllWithOrder();
    users1.forEach(System.out::println);

    System.out.println("======Mybatis一级缓存中获取数据==========");

    // 第二次查询,由于Mybatis默认一级缓存是开启的。直接从一级缓存中获取数据
    List<User> users2 = usermapper.findAllWithOrder();
    users2.forEach(System.out::println);
}

在这里插入图片描述

2.2.3 分析

  • 一级缓存是SqlSession范围的缓存,执行SqlSession的C(增加)U(更新)D(删除)操作,或者调用clearCache()、commit()、close()方法,都会清空缓存。

在这里插入图片描述

1. 第一次发起查询用户id为41的用户信息,先去找缓存中是否有id为41的用户信息,如果没有,从数据库查询用户信息。

2. 得到用户信息,将用户信息存储到一级缓存中。

3. 如果sqlSession去执行commit操作(执行插入、更新、删除),清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。

4. 第二次发起查询用户id为41的用户信息,先去找缓存中是否有id为41的用户信息,缓存中有,直接从缓存中获取用户信息。

在这里插入图片描述

2.2.4 清除

@Test
public void testClearOneCache() throws Exception {
	SqlSession sqlSession = MybatisUtils.openSession();
	UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
	User user1 = userMapper.findById(41);
	System.out.println("第一次查询的用户:" + user1);
    
	//调用sqlSession清除缓存的方法
	sqlSession.clearCache();
    
	User user2 = userMapper.findById(41);
	System.out.println("第二次查询的用户:" + user2);
}
<!-- 每次查询时,都会清除缓存 -->
<select flushCache="true"></select>
1) Mybatis清除一级缓存方式一:手动清除缓存
/*
      验证mybatis中的一级缓存
 */
@Test
public void testOneCache() throws IOException {
    // 加载核心配置文件
    InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");

    // 获得SqlSessionFactory工厂对象
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);

    // 获得SqlSession会话对象
    SqlSession sqlSession = sqlSessionFactory.openSession();

    // 当前返回的其实就是基于UserMapper所产生的代理对象mapper;底层:JDK动态代理 实际类型:proxy
    UserMapper usermapper = sqlSession.getMapper(UserMapper.class);

    // 第一次查询,查询的是数据库
    List<User> users1 = usermapper.findAllWithOrder();
    users1.forEach(System.out::println);

    // clearCache: 手动清空缓存
    sqlSession.clearCache();

    System.out.println("======验证是否从Mybatis一级缓存中获取数据==========");

    List<User> users2 = usermapper.findAllWithOrder();
    users2.forEach(System.out::println);
}

在这里插入图片描述

2) Mybatis清除一级缓存方式二:自动刷新清除缓存
  • OrderMapper.xml映射:在映射文件中配置flushCache=“true”
  <?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.myProject.mapper.UserMapper">
      <!--一对多嵌套查询 查询所有用户,与此同时查询出该用户具有的订单-->
      <resultMap id="userOrderMap" type="com.myProject.domain.User">
          <id property="id" column="id" />
          <result property="username" column="username" />
          <result property="birthday" column="birthday" />
          <result property="sex" column="sex" />
          <result property="address" column="address" />
  
          <!--fetchType="lazy": 延迟加载
              fetchType="eager": 立即加载
          -->
          <collection property="ordersList" ofType="com.myProject.domain.Orders" column="id" select="com.myProject.mapper.OrdersMapper.findByUid" />
      </resultMap>
      <!--flushCache="true":每次查询时,都会清除缓存-->
      <select id="findAllWithOrder" resultMap="userOrderMap" flushCache="true">
          SELECT * FROM USER
      </select>
  </mapper>
  package com.myProject.test;
  
  import com.myProject.domain.Orders;
  import com.myProject.domain.User;
  import com.myProject.mapper.OrdersMapper;
  import com.myProject.mapper.UserMapper;
  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;
  import java.util.List;
  
  public class MybatisTest {
      /*
      一对多嵌套查询 查询所有用户,与此同时查询出该用户具有的订单
       */
      @Test
      public void test() throws IOException {
          // 加载核心配置文件
          InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
  
          // 获得SqlSessionFactory工厂对象
          SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
  
          // 获得SqlSession会话对象
          SqlSession sqlSession = sqlSessionFactory.openSession();
  
          // 当前返回的其实就是基于UserMapper所产生的代理对象mapper;底层:JDK动态代理 实际类型:proxy
          UserMapper mapper = sqlSession.getMapper(UserMapper.class);
  
          List<User> users = mapper.findAllWithOrder();
          for (User user : users) {
              System.out.println(user);
  
              System.out.println("========延迟加载的信息的输出开始:=============");
              // 要用到该用户的订单信息
              System.out.println(user.getOrdersList());
              System.out.println("========延迟加载的信息的输出结束=============");
          }
  
          // 释放资源
          sqlSession.close();
      }
  
      /*
          验证mybatis中的一级缓存
       */
      @Test
      public void testOneCache() throws IOException {
          // 加载核心配置文件
          InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
  
          // 获得SqlSessionFactory工厂对象
          SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
  
          // 获得SqlSession会话对象
          SqlSession sqlSession = sqlSessionFactory.openSession();
  
          // 当前返回的其实就是基于UserMapper所产生的代理对象mapper;底层:JDK动态代理 实际类型:proxy
          UserMapper usermapper = sqlSession.getMapper(UserMapper.class);
  
          // 第一次查询,查询的是数据库
          List<User> users1 = usermapper.findAllWithOrder();
          users1.forEach(System.out::println);
  
          System.out.println("======验证是否从Mybatis一级缓存中获取数据==========");
  
          List<User> users2 = usermapper.findAllWithOrder();
          users2.forEach(System.out::println);
      }
  }

在这里插入图片描述

2.3 二级缓存

2.3.1 介绍

  • 二级缓存是namspace级别(跨sqlSession)的缓存,是默认不开启的
  • 二级缓存的开启需要进行配置,实现二级缓存的时候,MyBatis要求返回的POJO必须是可序列化的。 也就是要求实现Serializable接口,配置方法很简单,只需要在映射XML文件配置 就可以开启二级缓存了

在这里插入图片描述

2.3.2 验证

1) 配置核心配置文件
<settings>
    <!--
    因为cacheEnabled的取值默认就为true,所以这一步可以省略不配置。
    为true代表开启二级缓存;为false代表不开启二级缓存。
    -->
	<setting name="cacheEnabled" value="true"/>
</settings>
  • Mybatis核心配置文件
<?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文件-->
    <properties resource="jdbc.properties"></properties>
    
    <settings>
        <!--开启全局延迟加载功能-->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!--所有方法都会延迟加载-->
        <setting name="lazyLoadTriggerMethods" value="toString()"/>

        <!--
            因为cacheEnabled的取值默认就为true,所以这一步可以省略不配置。
            为true代表开启二级缓存;为false代表不开启二级缓存。
        -->
        <setting name="cacheEnabled" value="true"/>
    </settings>
    <!--设置别名-->
    <typeAliases>
        <!--方式二:批量起别名 (包别名) 别名就是类名且不区分大小写,推荐使用-->
        <package name="com.myProject.domain"/>
    </typeAliases>
    <!--environments: 运行环境-->
    <environments default="development">
        <environment id="development">
            <!--当前的事务管理交由JDBC管理-->
            <transactionManager type="JDBC"></transactionManager>
            <!--数据源信息 POOLED:使用mybatis的连接池-->
            <dataSource type="POOLED">
                <!--配置数据库的属性 动态获取JDBC的属性值-->
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>
    <!--引入映射配置文件-->
    <mappers>
        <!--批量加载映射 推荐使用-->
        <package name="com.myProject.mapper"/>
    </mappers>
</configuration>
2) 配置UserMapper.xml映射
mapper namespace="com.myProject.mapper.UserMapper">
    <!--当前映射文件开启二级缓存-->
    <cache></cache>
    <!--
        <select>标签中设置useCache=”true”代表当前这个statement要使用二级缓存。
        如果不使用二级缓存可以设置为false
        注意:
        	针对每次查询都需要最新的数据sql,要设置成useCache="false",禁用二级缓存。
    -->
    <select id="findAllWithOrder" resultMap="userOrderMap" useCache="true">
        SELECT * FROM USER
    </select>
</mapper>
  • UserMapper.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.myProject.mapper.UserMapper">
    <!--当前映射开启二级缓存 -->
    <cache></cache>
    <!--一对多嵌套查询 查询所有用户,与此同时查询出该用户具有的订单-->
    <resultMap id="userOrderMap" type="com.myProject.domain.User">
        <id property="id" column="id" />
        <result property="username" column="username" />
        <result property="birthday" column="birthday" />
        <result property="sex" column="sex" />
        <result property="address" column="address" />

        <!--fetchType="lazy": 延迟加载
            fetchType="eager": 立即加载
        -->
        <collection property="ordersList" ofType="com.myProject.domain.Orders" column="id" select="com.myProject.mapper.OrdersMapper.findByUid" />
    </resultMap>

    <!--useCache="true": 当前这个statement使用的是二级缓存-->
    <select id="findAllWithOrder" resultMap="userOrderMap" useCache="true">
        SELECT * FROM USER
    </select>
</mapper>
3) 修改User实体
package com.myProject.domain;

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

// 开启mybatis的二级缓存,User实体必须实现Serializable接口
public class User implements Serializable {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    // 表示多方关系(集合):代表当前用户所具有的订单列表
    private List<Orders> ordersList; // 关联对象是集合,使用collection

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", birthday=" + birthday +
                ", sex='" + sex + '\'' +
                ", address='" + address + '\'' +
                ", ordersList=" + ordersList +
                '}';
    }

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

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

    public Integer getId() {
        return id;
    }

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

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String getSex() {
        return sex;
    }

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

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}
4) 测试结果
/*
     验证mybatis中的二级缓存
 */
@Test
public void testTwoCache() throws IOException {
    // 加载核心配置文件
    InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");

    // 获得SqlSessionFactory工厂对象
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);

    // 获得SqlSession会话对象
    SqlSession sqlSession1 = sqlSessionFactory.openSession();
    UserMapper usermapper1 = sqlSession1.getMapper(UserMapper.class);

    // 第一次查询
    List<User> users1 = usermapper1.findAllWithOrder();
    users1.forEach(System.out::println);

    // 释放资源
    // 只有执行sqlSession.commit或者sqlSession.close。 那么一级缓存中内容才会刷新到二级缓存
    sqlSession1.close();

    SqlSession sqlSession2 = sqlSessionFactory.openSession();
    UserMapper usermapper2 = sqlSession2.getMapper(UserMapper.class);
    List<User> users2 = usermapper2.findAllWithOrder();
    users2.forEach(System.out::println);
    sqlSession2.close();
}

在这里插入图片描述

2.3.3 分析

  • 二级缓存是mapper映射级别的缓存,多个SqlSession去操作同一个Mapper映射的sql语句,多个 SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。

在这里插入图片描述

1. 映射语句文件中的所有select语句将会被缓存。

2. 映射语句文件中的所有insert、update和delete语句会刷新缓存。

2.3.4 注意问题(脏读)

  • mybatis的二级缓存因为是namespace级别,所以在进行多表查询时会产生脏读问题

在这里插入图片描述

2.4 小结

1. mybatis的缓存,都不需要我们手动存储和获取数据。mybatis自动维护的。

2. mybatis开启了二级缓存后,那么查询顺序:二级缓存--》一级缓存--》数据库。

注意:mybatis的二级缓存会存在脏读问题,需要使用第三方的缓存技术解决问题。

三 MyBatis注解

3.1 MyBatis常用注解

  • 这几年来注解开发越来越流行,Mybatis也可以使用注解开发方式,这样我们就可以减少编写 Mapper映射文件了。我们先围绕一些基本的CRUD来学习,再学习复杂映射多表操作。
* @Insert:实现新增,代替了<insert></insert>

* @Delete:实现删除,代替了<delete></delete>

* @Update:实现更新,代替了<update></update>

* @Select:实现查询,代替了<select></select>

* @Result:实现结果集封装,代替了<result></result>

* @Results:可以与@Result 一起使用,封装多个结果集,代替了<resultMap></resultMap>

* @One:实现一对一结果集封装,代替了<association></association>

* @Many:实现一对多结果集封装,代替了<collection></collection>

3.2 MyBatis注解的增删改查【重点】

3.2.1 创建UserMapper接口

package com.myProject.mapper;

import com.myProject.domain.User;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;

import java.util.List;

public interface UserMapper {
    /*
        查询用户
     */
    @Select("select * from user")
    public List<User> findAll();

    /*
        添加用户
     */
    @Insert("insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})")
    public void save(User user);

    /*
        更新用户
     */
    @Update("update user set username = #{username}, birthday = #{birthday} where id = #{id}")
    public void update(User user);

    /*
        删除用户
     */
    @Delete("delete from user where id = #{id}")
    public void delete(Integer id);
}

3.2.2 编写mybatis核心配置文件

<!--我们使用了注解替代的映射文件,所以我们只需要加载使用了注解的Mapper接口即可-->
<mappers>
    <!--扫描使用注解的Mapper类-->
    <mapper class="com.myProject.mapper.UserMapper"></mapper>
</mappers>
<!--或者指定扫描包含映射关系的接口所在的包也可以-->
<mappers>
    <!--扫描使用注解的Mapper类所在的包-->
    <package name="com.myProject.mapper"></package>
</mappers>

3.2.3 测试代码

package com.myProject.test;

import com.myProject.domain.User;
import com.myProject.mapper.UserMapper;
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.After;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.List;

public class MybatisTest {
    private SqlSessionFactory sqlSessionFactory;
    private SqlSession sqlSession;

    // 在@Test方法标注的方法执行之前来执行
    @Before
    public void before() throws IOException {
        // 加载核心配置文件
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");

        // 获得SqlSessionFactory工厂对象
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);

        // 获得SqlSession会话对象
        sqlSession = sqlSessionFactory.openSession();
    }

    @After
    public void after() {
        sqlSession.commit();
        // 释放资源
        sqlSession.close();
    }

    /*
        测试查询方法
     */
    @Test
    public void testSelect() throws IOException {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> all = mapper.findAll();
        all.forEach(System.out::println);
    }

    /**
     * 测试添加方法
     */
    @Test
    public void testInsert() {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = new User();
        user.setUsername("BB2");
        user.setSex("女");
        user.setBirthday(new Date());
        user.setAddress("北京");

        mapper.save(user);
    }

    /**
     * 测试更新
     */
    @Test
    public void testUpdate() {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        User user = new User();
        user.setUsername("lucy");
        user.setId(8);

        mapper.update(user);
    }

    /**
     * 测试删除
     */
    @Test
    public void testDelete() {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        mapper.delete(8);
    }
}

3.3 使用注解实现复杂映射开发

  • 之前我们在映射文件中通过配置 <resultMap><association><collection> 来实现复杂关系映射。
  • 使用注解开发后,我们可以使用 @Results、@Result,@One、@Many 注解组合完成复杂关系的配置。

在这里插入图片描述

3.4 一对一查询

3.4.1 介绍

  • 需求:查询一个订单,与此同时查询出该订单所属的用户

一对一查询语句

SELECT * FROM orders;
SELECT * FROM `user` WHERE id = #{订单的uid};

3.4.2 代码实现

1) Orders实体
package com.myProject.domain;

public class Orders {
    private Integer id;
    private String ordertime;
    private Double total;
    private Integer uid;

    private User user;

    @Override
    public String toString() {
        return "Orders{" +
                "id=" + id +
                ", ordertime='" + ordertime + '\'' +
                ", total=" + total +
                ", uid=" + uid +
                ", user=" + user +
                '}';
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    public Integer getId() {
        return id;
    }

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

    public String getOrdertime() {
        return ordertime;
    }

    public void setOrdertime(String ordertime) {
        this.ordertime = ordertime;
    }

    public Double getTotal() {
        return total;
    }

    public void setTotal(Double total) {
        this.total = total;
    }

    public Integer getUid() {
        return uid;
    }

    public void setUid(Integer uid) {
        this.uid = uid;
    }
}
2) User实体
package com.myProject.domain;

import java.util.Date;

public class User {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", birthday=" + birthday +
                ", sex='" + sex + '\'' +
                ", address='" + address + '\'' +
                '}';
    }

    public Integer getId() {
        return id;
    }

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

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String getSex() {
        return sex;
    }

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

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}
3)OrderMapper接口
package com.myProject.mapper;

import com.myProject.domain.Orders;
import com.myProject.domain.User;
import org.apache.ibatis.annotations.One;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.mapping.FetchType;

import java.util.List;

public interface OrdersMapper {
    /**
     * 查询所有订单,同时查询订单所属的用户信息
     */
    @Select("select * from orders")
    @Results({ // 代替的就是resultMap标签
            // id = true : 表示为主键
            @Result(property = "id", column = "id", id = true),
            @Result(property = "ordertime", column = "ordertime"),
            @Result(property = "total", column = "total"),
            @Result(property = "uid", column = "uid"),
            // 一对一查询,fetchType = FetchType.EAGER:启用立即加载,尽量不要修改mybatis的settings配置,借助局部加载优先级高于全局加载的特点,修改局部加载的配置即可
            @Result(property = "user", javaType = User.class, column = "uid", one = @One(select = "com.myProject.mapper.UserMapper.findById",fetchType = FetchType.EAGER))
    })
    public List<Orders> findAllWithUser();
}
4)UserMapper接口
package com.myProject.mapper;

import com.myProject.domain.User;
import org.apache.ibatis.annotations.Select;

public interface UserMapper {
    /**
     * 根据id查询用户
     */

    @Select("select * from user where id = #{uid}")
    public User findById(Integer uid);
}
5)测试代码
package com.myProject.test;

import com.myProject.domain.Orders;
import com.myProject.mapper.OrdersMapper;
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.After;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class MybatisTest {
    private SqlSessionFactory sqlSessionFactory;
    private SqlSession sqlSession;

    // 在@Test方法标注的方法执行之前来执行
    @Before
    public void before() throws IOException {
        // 加载核心配置文件
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");

        // 获得SqlSessionFactory工厂对象
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);

        // 获得SqlSession会话对象
        sqlSession = sqlSessionFactory.openSession();
    }

    @After
    public void after() {
        sqlSession.commit();
        // 释放资源
        sqlSession.close();
    }

    /**
     * 一对一查询
     */
    @Test
    public void testOnetoOne() {
        OrdersMapper mapper = sqlSession.getMapper(OrdersMapper.class);
        List<Orders> allWithUser = mapper.findAllWithUser();
        allWithUser.forEach(System.out::println);
    }
}

3.5 一对多查询

3.5.1 介绍

  • 需求:查询一个用户,与此同时查询出该用户具有的订单

一对多查询语句

SELECT * FROM `user`;
SELECT * FROM orders where uid = #{用户id};

3.5.2 代码实现

1)User实体
package com.myProject.domain;

import java.util.Date;
import java.util.List;

public class User {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    // 表示多方关系(集合):代表当前用户所具有的订单列表
    private List<Orders> ordersList; // 关联对象是集合,使用collection

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", birthday=" + birthday +
                ", sex='" + sex + '\'' +
                ", address='" + address + '\'' +
                ", ordersList=" + ordersList +
                '}';
    }

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

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

    public Integer getId() {
        return id;
    }

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

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String getSex() {
        return sex;
    }

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

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}
2)Orders实体
package com.myProject.domain;

public class Orders {
    private Integer id;
    private String ordertime;
    private Double total;
    private Integer uid;

    @Override
    public String toString() {
        return "Orders{" +
                "id=" + id +
                ", ordertime='" + ordertime + '\'' +
                ", total=" + total +
                ", uid=" + uid +
                '}';
    }

    public Integer getId() {
        return id;
    }

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

    public String getOrdertime() {
        return ordertime;
    }

    public void setOrdertime(String ordertime) {
        this.ordertime = ordertime;
    }

    public Double getTotal() {
        return total;
    }

    public void setTotal(Double total) {
        this.total = total;
    }

    public Integer getUid() {
        return uid;
    }

    public void setUid(Integer uid) {
        this.uid = uid;
    }
}
3) UserMapper接口
package com.myProject.mapper;

import com.myProject.domain.User;
import org.apache.ibatis.annotations.*;

import java.util.List;

public interface UserMapper {
    /**
     * 查询所有用户,及关联的订单信息
     */
    @Select("select *  from user")
    @Results({
        @Result(property = "id", column = "id",id=true),
        @Result(property = "username", column = "username"),
        @Result(property = "birthday", column = "birthday"),
        @Result(property = "sex", column = "sex"),
        @Result(property = "address", column = "address"),
        @Result(property = "ordersList", javaType = List.class,column = "id",many = @Many(select = "com.myProject.mapper.OrdersMapper.findOrderByUid"))
    })
    public List<User> findAllWithOrder();
}
4) OrderMapper接口
package com.myProject.mapper;

import com.myProject.domain.Orders;
import org.apache.ibatis.annotations.Select;

import java.util.List;

public interface OrdersMapper {
    /**
     * 根据传递过来的用户id,查询该用户所具有的订单信息
     */
    @Select("select * from orders where uid = #{uid}")
    public List<Orders> findOrderByUid(Integer uid);
}
5) 测试代码
package com.myProject.test;

import com.myProject.domain.Orders;
import com.myProject.domain.User;
import com.myProject.mapper.OrdersMapper;
import com.myProject.mapper.UserMapper;
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.After;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.List;

public class MybatisTest {
    private SqlSessionFactory sqlSessionFactory;
    private SqlSession sqlSession;

    // 在@Test方法标注的方法执行之前来执行
    @Before
    public void before() throws IOException {
        // 加载核心配置文件
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");

        // 获得SqlSessionFactory工厂对象
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);

        // 获得SqlSession会话对象
        sqlSession = sqlSessionFactory.openSession();
    }

    @After
    public void after() {
        sqlSession.commit();
        // 释放资源
        sqlSession.close();
    }
    
    /**
     * 一对多查询
     */
    @Test
    public void testOnetoMany() {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> allWithOrder = mapper.findAllWithOrder();
        for (User user : allWithOrder) {
            System.out.println(user);
            System.out.println(user.getOrdersList());
        }
    }
}

在这里插入图片描述

3.6 多对多查询

3.6.1 介绍

  • 需求:查询所有用户,同时查询出该用户的所有角色

多对多查询语句

-- 需求:查询所有用户,同时查询出该用户的所有角色
	-- 先查询所有用户
	SELECT * FROM USER 
	
	-- 根据查询出来的用户id,查询角色信息
	SELECT
	*
	FROM
		sys_role sr
	INNER JOIN sys_user_role sur ON
		sur.roleid = sr.id
	WHERE
		sur.userid = #{用户id};

3.6.2 代码实现

1)User实体
package com.myProject.domain;

import java.util.Date;
import java.util.List;

public class User {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    // 表示多方关系(集合):代表当前用户所具有的角色列表
    private List<Role> roleList; // 关联对象是集合,使用collection

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", birthday=" + birthday +
                ", sex='" + sex + '\'' +
                ", address='" + address + '\'' +
                ", roleList=" + roleList +
                '}';
    }

    public Integer getId() {
        return id;
    }

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

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String getSex() {
        return sex;
    }

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

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public List<Role> getRoleList() {
        return roleList;
    }

    public void setRoleList(List<Role> roleList) {
        this.roleList = roleList;
    }
}
2)Role实体
package com.myProject.domain;

public class Role {
    private Integer id;
    private String rolename;
    private String roleDesc;

    @Override
    public String toString() {
        return "Role{" +
                "id=" + id +
                ", rolename='" + rolename + '\'' +
                ", roleDesc='" + roleDesc + '\'' +
                '}';
    }

    public Integer getId() {
        return id;
    }

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

    public String getRolename() {
        return rolename;
    }

    public void setRolename(String rolename) {
        this.rolename = rolename;
    }

    public String getRoleDesc() {
        return roleDesc;
    }

    public void setRoleDesc(String roleDesc) {
        this.roleDesc = roleDesc;
    }
}
3)UserMapper接口
package com.myProject.mapper;

import com.myProject.domain.User;
import org.apache.ibatis.annotations.*;

import java.util.List;

public interface UserMapper {
    /**
     * 查询所有用户及关联的角色信息
     */
    @Select("select * from user")
    @Results({
            @Result(property = "id", column = "id",id=true),
            @Result(property = "username", column = "username"),
            @Result(property = "birthday", column = "birthday"),
            @Result(property = "sex", column = "sex"),
            @Result(property = "address", column = "address"),
            @Result(property = "roleList", javaType = List.class, column = "id", many = @Many(select = "com.myProject.mapper.RoleMapper.findAllByUid"))
    })
    public List<User> findAllWithRole();
}
4)RoleMapper接口
package com.myProject.mapper;

import com.myProject.domain.Role;
import org.apache.ibatis.annotations.Select;

import java.util.List;

public interface RoleMapper {
    /*
    	根据用户id查询对应角色
     */
    @Select("SELECT * FROM sys_role sr INNER JOIN sys_user_role sur ON sur.roleid = sr.id WHERE sur.userid = #{uid}")
    public List<Role> findAllByUid(Integer uid);
}
5)测试代码
package com.myProject.test;

import com.myProject.domain.User;
import com.myProject.mapper.UserMapper;
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.After;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class MybatisTest {
    private SqlSessionFactory sqlSessionFactory;
    private SqlSession sqlSession;

    // 在@Test方法标注的方法执行之前来执行
    @Before
    public void before() throws IOException {
        // 加载核心配置文件
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");

        // 获得SqlSessionFactory工厂对象
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);

        // 获得SqlSession会话对象
        sqlSession = sqlSessionFactory.openSession();
    }

    @After
    public void after() {
        sqlSession.commit();
        // 释放资源
        sqlSession.close();
    }

    /**
     * 多对多查询
     */
    @Test
    public void testManytoMany() {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> allWithOrder = mapper.findAllWithRole();
        for (User user : allWithOrder) {
            System.out.println(user);
            System.out.println(user.getRoleList());
        }
    }
}

3.7 基于注解的二级缓存

3.7.1 配置SqlMapConfig.xml文件开启二级缓存的支持

<settings>
    <!--
        因为cacheEnabled的取值默认就为true,所以这一步可以省略不配置。
        为true代表开启二级缓存;为false代表不开启二级缓存。
    -->
    <setting name="cacheEnabled" value="true"/>
</settings>

3.7.2 在Mapper接口中使用注解配置二级缓存

package com.myProject.mapper;

import com.myProject.domain.User;
import org.apache.ibatis.annotations.*;

// 注解配置二级缓存
@CacheNamespace
public interface UserMapper {
    /**
     * 根据id查询用户
     */

    @Select("select * from user where id = #{uid}")
    public User findById(Integer uid);
}

3.7.3 代码测试

package com.myProject.test;

import com.myProject.domain.User;
import com.myProject.mapper.UserMapper;
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.After;
import org.junit.Before;
import org.junit.Test;

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

public class MybatisTest {
    private SqlSessionFactory sqlSessionFactory;
    private SqlSession sqlSession;

    // 在@Test方法标注的方法执行之前来执行
    @Before
    public void before() throws IOException {
        // 加载核心配置文件
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");

        // 获得SqlSessionFactory工厂对象
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);

        // 获得SqlSession会话对象
        sqlSession = sqlSessionFactory.openSession();
    }

    @After
    public void after() {
        sqlSession.commit();
        // 释放资源
        sqlSession.close();
    }

    /**
     * 测试注解实现二级缓存
     */
    @Test
    public void cacheTest(){
        SqlSession sqlSession1 = sqlSessionFactory.openSession();
        SqlSession sqlSession2 = sqlSessionFactory.openSession();

        UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
        UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);

        User user1 = userMapper1.findById(1);
        System.out.println(user1);

        // 执行sqlSession1.close(),才能将内容从一级缓存刷新到二级缓存
        sqlSession1.close();

        User user2 = userMapper2.findById(1);
        System.out.println(user2);
        sqlSession2.close();
    }
}
  • 第一次查询

在这里插入图片描述

  • 第二次查询

在这里插入图片描述

3.8 注解延迟加载

  • 不管是一对一还是一对多 ,在注解配置中都有fetchType的属性
* fetchType = FetchType.LAZY 表示懒加载

* fetchType = FetchType.EAGER 表示立即加载

* fetchType = FetchType.DEFAULT 表示使用全局配置

3.9 小结

* 注解开发和xml配置优劣分析
    1.注解开发和xml配置相比,从开发效率来说,注解编写更简单,效率更高。
    
    2.从可维护性来说,注解如果要修改,必须修改源码,会导致维护成本增加。xml维护性更强。
    
    3.在实际开发中,两者可结合使用,单表(一对一)使用注解开发;多表或复杂应用开发建议使用xml配置。

四 Mybatis插件机制----通用mapper

4.1 什么是通⽤Mapper

  • 通⽤Mapper就是为了解决单表增删改查,基于Mybatis的插件机制。开发⼈员不需要编写SQL,不需要在DAO中增加⽅法,只要写好实体类,就能⽀持相应的增删改查⽅法

如何使⽤

1)⾸先在maven项⽬,在pom.xml中引⼊mapper的依赖

<dependency>
	 <groupId>tk.mybatis</groupId>
	 <artifactId>mapper</artifactId>
	 <version>3.1.2</version>
</dependency>

2)Mybatis配置⽂件中完成配置

<plugins>
	 <!--分⻚插件:如果有分⻚插件,要排在通⽤mapper之前-->
	 <plugin interceptor="com.github.pagehelper.PageHelper">
	 	<property name="dialect" value="mysql"/>
	 </plugin>
	 <plugin interceptor="tk.mybatis.mapper.mapperhelper.MapperInterceptor">
		 <!-- 通⽤Mapper接⼝,多个通⽤接⼝⽤逗号隔开 -->
		 <property name="mappers" value="tk.mybatis.mapper.common.Mapper"/>
	 </plugin>
</plugins>

3)实体类设置主键

@Table(name = "user")
public class User {
	 @Id
	 @GeneratedValue(strategy = GenerationType.IDENTITY)
	 private Integer id;
	 private String username;
}

4)定义通⽤mapper

import com.myProject.domain.User;
import tk.mybatis.mapper.common.Mapper;
public interface UserMapper extends Mapper<User> {
}

5)测试

public class UserTest {
    @Test
    public void test() throws IOException {
        Inputstream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = build.openSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        User user = new User();
        user.setId(4);
        //(1)mapper基础接⼝
        //select 接⼝
        User user1 = userMapper.selectOne(user); //根据实体中的属性进⾏查询,只能有—个返回值
        List<User> users = userMapper.select(null); //查询全部结果
        userMapper.selectByPrimaryKey(1); //根据主键字段进⾏查询,⽅法参数必须包含完整的主键属性,查询条件使⽤等号
        userMapper.selectCount(user); //根据实体中的属性查询总数,查询条件使⽤等号
        // insert 接⼝
        int insert = userMapper.insert(user); //保存⼀个实体,null值也会保存,不会使⽤数据库默认值
        int i = userMapper.insertSelective(user); //保存实体,null的属性不会保存,会使⽤数据库默认值
        // update 接⼝
        int i1 = userMapper.updateByPrimaryKey(user);//根据主键更新实体全部字段,null值会被更新
        // delete 接⼝
        int delete = userMapper.delete(user); //根据实体属性作为条件进⾏删除,查询条件 使⽤等号
        userMapper.deleteByPrimaryKey(1); //根据主键字段进⾏删除,⽅法参数必须包含完整的主键属性
        //(2)example⽅法
        Example example = new Example(User.class);
        example.createCriteria().andEqualTo("id", 1);
        example.createCriteria().andLike("val", "1");
        //⾃定义查询
        List<User> users1 = userMapper.selectByExample(example);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值