Java教程

MyBatis延迟加载、缓存、分页插件

本文主要是介绍MyBatis延迟加载、缓存、分页插件,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

一、MyBatis延迟加载策略

(一)什么是延迟加载:

当时不使用的时候不加载,使用的时候再加载.

(二)延迟加载的好处

好处:提高性能

(三)使用懒加载:

在mybatis.xml配置文件

<settings>

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

<setting name="aggressiveLazyLoading" value="false"/>

</settings>

(四)具体案例

1.在mybatis.xml不配置懒加载

<select id="findById" parameterType="int" resultMap="personMap">
    select * from person where id=#{id}
</select>
<resultMap id="personMap" type="com.tjetc.entity.Person">
    <id column="id" property="id"></id>
    <result column="name" property="name"></result>
    <association property="idCard" column="id" select="findByPid"></association>
</resultMap>
<select id="findByPid" parameterType="int" resultType="com.tjetc.entity.IdCard">
    select * from idcard where pid=#{id}
</select>

 

 @Test
    public void testFindById() {
//        创建SqlSessionFactory
       
SqlSessionFactory sqlSessionFactory = null;
        try {
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader("mybatis.xml"));
//            得到session对象
           
SqlSession session = sqlSessionFactory.openSession();
            //得到mapper
           
PersonMapper personMapper = session.getMapper(PersonMapper.class);
            //调用PersonMapper的方法
           
Person person = personMapper.findById(1);
//            System.out.println(person);
//            IdCard idCard = person.getIdCard();
//            System.out.println(idCard);
           
session.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

 

==>  Preparing: select * from person where id=?

==> Parameters: 1(Integer)

<==    Columns: id, name

<==        Row: 1, 张三

====>  Preparing: select * from idcard where pid=?

====> Parameters: 1(Integer)

<====    Columns: id, code, pid

<====        Row: 1, 12345678234567, 1

<====      Total: 1

<==      Total: 1

 

2.在mybatis.xml配置懒加载

<settings>
    <setting name="logImpl" value="STDOUT_LOGGING"/>
    <setting name="lazyLoadingEnabled" value="true"/>
    <setting name="aggressiveLazyLoading" value="false"/>
</settings>

 

==>  Preparing: select * from person where id=?

==> Parameters: 1(Integer)

<==    Columns: id, name

<==        Row: 1, 张三

<==      Total: 1

 

二、Mybatis缓存

(一)一级缓存

1.概述

如下图所示,MyBatis会在一次会话的表示----一个SqlSession对象中创建一个本地缓存(local cache),对于每一次查询,都会尝试根据查询的条件去本地缓存中查找是否存在,如果在缓存中,就直接从缓存中取出,然后返回给用户;否则,从数据库读取数据,将查询结果存入缓存并返回给用户。

对于会话(Session)级别的数据缓存,我们称之为一级数据缓存,简称一级缓存

2.一级缓存的工作流程

1.对于某个查询,根据statementId,params,rowBounds来构建一个key值,根据这个key值去缓存Cache中取出对应的key值存储的缓存结果;

  1. 判断从Cache中根据特定的key值取的数据数据是否为空,即是否命中;
  2. 如果命中,则直接将缓存结果返回;
  3. 如果没命中:

        4.1  去数据库中查询数据,得到查询结果;

        4.2  将key和查询到的结果分别作为key,value对存储到Cache中;

        4.3. 将查询结果返回;

  1. 结束。

(二)二级缓存

1.概述

MyBatis的缓存机制整体设计以及二级缓存的工作模式

MyBatis的二级缓存是Application级别的缓存,它可以提高对数据库查询的效率,以提高应用的性能。

CachingExecutor是Executor的装饰者,以增强Executor的功能,使其具有缓存查询的功能,这里用到了设计模式中的装饰者模式。

2.二级缓存的相关配置

要想使某条Select查询支持二级缓存,你需要保证:

1.MyBatis支持二级缓存的总开关:全局配置变量参数cacheEnabled=true

2.该select语句所在的Mapper,配置了<cache> 或<cached-ref>节点,并且有效

3.该select语句的参数useCache=true

4.对象的类必须实现序列化接口

一级缓存和二级缓存的使用顺序 :

二级缓存———> 一级缓存——> 数据库

二级缓存作用域为 Mapper(Namespace);

configuration.MappedStatement.Cache;项目启动时会初始化;

3.Cache使用时的注意事项/避免使用二级缓存

注意事项

  1. 只能在【只有单表操作】的表上使用缓存,不只是要保证这个表在整个系统中只有单表操作,而且和该表有关的全部操作必须全部在一个namespace下。
  2. 在可以保证查询远远大于insert,update,delete操作的情况下使用缓存,这一点需要保证在1的前提下才可以!

 

避免使用二级缓存

可能会有很多人不理解这里,二级缓存带来的好处远远比不上他所隐藏的危害。

1.缓存是以namespace为单位的,不同namespace下的操作互不影响。

2.insert,update,delete操作会清空所在namespace下的全部缓存。

3.通常使用MyBatis Generator生成的代码中,都是各个表独立的,每个表都有自己的namespace。

 

多表操作一定不能使用缓存

首先不管多表操作写到那个namespace下,都会存在某个表不在这个namespace下的情况。

例如两个表:role和user_role,如果我想查询出某个用户的全部角色role,就一定会涉及到多表的操作。

<select id="selectUserRoles" resultType="UserRoleVO">

    select * from user_role a,role b where a.roleid = b.roleid and a.userid = #{userid}

</select>

 

像上面这个查询,你会写到那个xml中呢??

不管是写到RoleMapper.xml还是UserRoleMapper.xml,或者是一个独立的XxxMapper.xml中。如果使用了二级缓存,都会导致上面这个查询结果可能不正确。

如果你正好修改了这个用户的角色,上面这个查询使用缓存的时候结果就是错的。

三、typeHandler使用

数据表:

CREATE TABLE `admin` (

  `id` bigint NOT NULL AUTO_INCREMENT,

  `username` varchar(255) NOT NULL,

  `password` varchar(255) NOT NULL,

  `sex` int DEFAULT NULL,

  PRIMARY KEY (`id`)

)

枚举定义

/**
 *
性别枚举
 
*/
public enum EnumSex {
    FEMALE(0, "女"),
    MALE(1, "男");
    /**
     *
编码
    
*/
   
private int code;
    /**
     *
显示的名称
    
*/
   
private String name;
    EnumSex(int code, String name) {
        this.code = code;
        this.name = name;
    }
    public int getCode() {
        return code;
    }
    public String getName() {
        return name;
    }
    public static EnumSex getByCode(int code) {
        //获取所有的枚举值(枚举数组)
       
EnumSex[] values = EnumSex.values();
        //遍历数组,查找每一个枚举的code是否与传进来的参数code相等,如果相等就返回对应的枚举
       
for (EnumSex value : values) {
            if (value.getCode() == code) {
                return value;
            }
        }
        return null;
    }
}

自定义typeHandler处理器

/**
 *
自定义性别枚举处理器
 
*/
public class EnumSexTypeHandler implements TypeHandler<EnumSex> {
    /**
     * javaType
转换 jdbcType 参数设置调用
    
*
     * @param
preparedStatement
    
* @param i
    
* @param enumSex
    
* @param jdbcType
    
* @throws SQLException
    
*/
   
@Override
    public void setParameter(PreparedStatement preparedStatement, int i, EnumSex enumSex, JdbcType jdbcType) throws SQLException {
        //增加和修改 sex字段 用到jdbc参数设置
       
preparedStatement.setObject(i, enumSex.getCode());
    }

    /**
     * jdbctye
javatype  用户查询
    
*
     * @param
resultSet
    
* @param s
    
* @return
     * @throws
SQLException
    
*/
   
@Override
    public EnumSex getResult(ResultSet resultSet, String s) throws SQLException {
        //根据columnLabel 获取对应结果集的值 sex 对应code
       
int sexCode = resultSet.getInt(s);
        //sexCode转换成枚举值 返回出去
       
return EnumSex.getByCode(sexCode);
    }

    @Override
    public EnumSex getResult(ResultSet resultSet, int i) throws SQLException {
        //根据columnIndex 获取对应结果集的值 sex 对应code
       
int sexCode = resultSet.getInt(i);
        //sexCode转换成枚举值 返回出去
       
return EnumSex.getByCode(sexCode);
    }

    /**
     *
存储过程调用
    
*
     * @param
callableStatement
    
* @param i
    
* @return
     * @throws
SQLException
    
*/
   
@Override
    public EnumSex getResult(CallableStatement callableStatement, int i) throws SQLException {
        int sexCode = callableStatement.getInt(i);
        //sexCode转换成枚举值 返回出去
       
return EnumSex.getByCode(sexCode);
    }
}

Admin实体类

public class Admin {
    private Long id;
    private String username;
    private String password;
    private EnumSex sex;

    public Admin() {
    }

    public Admin(String username, String password, EnumSex sex) {
        this.username = username;
        this.password = password;
        this.sex = sex;
    }

    public Long getId() {
        return id;
    }

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

    public String getUsername() {
        return username;
    }

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

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public EnumSex getSex() {
        return sex;
    }

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

    public Admin(Long id, String username, String password, EnumSex sex) {
        this.id = id;
        this.username = username;
        this.password = password;
        this.sex = sex;
    }

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

AdminMapper接口

public interface AdminMapper {
    void insert(Admin admin);

    Admin selectById(Long id);
}

AdminMapper.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.tjetc.dao.AdminMapper">
    <insert id="insert" parameterType="admin" useGeneratedKeys="true" keyProperty="id">
        insert into admin(username,password,sex) values(#{username},#{password},#{sex,typeHandler=com.tjetc.handler.EnumSexTypeHandler})
    </insert>
    <select id="selectById" parameterType="long" resultMap="adminMap">
        select id,username,password,sex from admin where id=#{id}
    </select>
    <resultMap id="adminMap" type="admin">
        <id column="id" property="id"></id>
        <result column="username" property="username"></result>
        <result column="password" property="password"></result>
        <result column="sex" property="sex" typeHandler="com.tjetc.handler.EnumSexTypeHandler"></result>
    </resultMap>
</mapper>

Mybatis.xml增加配置

<!--配置自定义typeHandler-->
<typeHandlers>
    <!--<package name=""/>-->
   
<typeHandler handler="com.tjetc.handler.EnumSexTypeHandler"/>
</typeHandlers>

测试:

public class TestAdmin {
    @Test
    public void testInsert() throws IOException {
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        AdminMapper mapper = sqlSession.getMapper(AdminMapper.class);
        Admin admin = new Admin("wangwu", "5555", EnumSex.FEMALE);
        mapper.insert(admin);
        sqlSession.commit();
        System.out.println(admin);
        sqlSession.close();
    }

    @Test
    public void testSelectById() throws IOException {
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        AdminMapper mapper = sqlSession.getMapper(AdminMapper.class);
        Admin admin = mapper.selectById(4L);
        System.out.println(admin);
        sqlSession.close();
    }
}

 

四、基于Annotation注解实现映射处理

在mapper映射接口的方法上写SQL,这样可以省略mapper的xml映射文件

public interface UserMapper {
    @Select("SELECT * FROM user where id=#{id}")
    User findById(int id);

 

 @Test
    public void testFind2() {
        try {
//        创建SqlSessionFactory
           
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader("mybatis.xml"));
//            得到session对象
           
SqlSession session = sqlSessionFactory.openSession();
            //得到mapper接口的实现对象
           
UserMapper mapper = session.getMapper(UserMapper.class);
//            通过session对象操作数据库
           
User user = mapper.findById(1);
//            控制台打印user对象
           
System.out.println(user);
//            关闭session
           
session.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

 

==>  Preparing: SELECT * FROM user where id=?

==> Parameters: 1(Integer)

<==    Columns: id, password, username

<==        Row: 1, 123456, admin

<==      Total: 1

User{id=1, username='admin', password='123456'}

五、MyBatis分页插件PageHelper

<!--mybatis的分页插件-->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.1.9</version>
</dependency>

 

<plugins>

    <!-- com.github.pagehelperPageHelper类所在包名 -->

   
<plugin interceptor="com.github.pagehelper.PageInterceptor">

        <!-- 什么都不配,使用默认的配置 -->

   
</plugin>

</plugins>

 

 public void testFindAll2() {
        try {
//        创建SqlSessionFactory
           
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader("mybatis.xml"));
//            得到session对象
           
SqlSession session = sqlSessionFactory.openSession();
            //得到mapper接口的实现对象
           
UserMapper mapper = session.getMapper(UserMapper.class);
            PageHelper.startPage(2, 2);
//            通过session对象操作数据库
           
List<User> list = mapper.findAll();
            PageInfo<User> pageInfo = new PageInfo<>(list);
//            控制台打印list对象
           
for (User user : list) {
                System.out.println(user);
            }
//            关闭session
           
session.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

 

 

这篇关于MyBatis延迟加载、缓存、分页插件的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!