Mybatis入门

1. 导入MyBatis依赖

<dependencies>
	<!--mybatis依赖包-->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.3</version>
    </dependency>
    <!--mysql数据库连接驱动-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.47</version>
    </dependency>
    <!--单元测试Junit-->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
</dependencies>
  • 注意:在build中配置resources,来防止资源导出失败的问题
<build>
    <resources>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>false</filtering>
        </resource>
    </resources>
</build>

2. MyBatis 核心配置

  • 创建 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>
    <!--配置MyBatis运行环境-->
    <environments default="dev">
        <environment id="dev">
            <!--配置JDBC事务管理-->
            <transactionManager type="JDBC"/>
            <!--POOLED配置JDBC数据源连接池-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&amp;characterEncoding=utf-8&amp;useSSL=false"/>
                <property name="username" value="root"/>
                <property name="password" value="admin"/>
            </dataSource>
        </environment>
    </environments>
</configuration>

3. 基于SqlSession操作数据库

  • 新建数据表
CREATE TABLE `user` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `username` varchar(255) DEFAULT NULL,
  `password` varchar(255) DEFAULT NULL,
  `age` tinyint(1) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;
  • 关系映射实体类User
public class User {
    private int id;
    private String username;
    private String password;
    private int age;
	
	getter()...
	setter()...
    tostring()...
}
  • 创建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.yauyukbiu.demo.dao.UserDao">
    <select id="getAll" resultType="com.yauyukbiu.demo.entity.User">
    select * from user
    </select>
</mapper>
  • 在全局配置⽂件 mybatis-config.xml 中注册 UserMapper.xml
<!--注册mapper.xml-->
<mappers>
    <mapper resource="com/yauyukbiu/demo/dao/UserMapper.xml"/>
</mappers>
  • 从 XML 中构建 SqlSessionFactory,使用mybatis原生接口
public class MyBatisUtils {
    private static SqlSessionFactory sqlSessionFactory;

    static {
        String resource = "mybatis-config.xml";
        InputStream inputStream = null;
        try {
            //加载xml配置文件
            inputStream = Resources.getResourceAsStream(resource);
        } catch (IOException e) {
            e.printStackTrace();
        }
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    }
    //封装获取SqlSession
    public static SqlSession getSqlSession() {
        return sqlSessionFactory.openSession();
    }
}
  • 测试查询结果
public interface UserDao {
    
    List<User> getAll();
    
}
@Test
public void test() {
	//获得sqlSession连接
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    //使用和指定语句的参数和返回值相匹配的接口
    UserDao mapper = sqlSession.getMapper(UserDao.class);
    //执行查询方法
    List<User> userList = mapper.getAll();
    for (User user : userList) {
        System.out.println(user.toString());
    }
    //关闭SqlSession
    sqlSession.close();
}

在这里插入图片描述

4. 作用域(Scope)和生命周期

不同作用域和生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题。
在这里插入图片描述
SqlSessionFactoryBuilder

  • 可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。
  • 局部变量

SqlSessionFactory

  • 数据库连接创建工厂,也就是相当于数据库连接池
  • SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。
  • SqlSessionFactory 的最佳作用域是应用作用域。
  • 使用单例模式或者静态单例模式

SqlSession

  • 连接到连接池的请求
  • SqlSession 的实例不是线程安全的,因此是不能被共享的
  • 最佳的作用域是请求或方法作用域
  • 用完之后需要关闭连接,否则将一直占用数据库连接资源,造成资源浪费

5. #{ } 和 ${ }

  • #{}是预编译处理

Mybatis 在处理#{}时,会将 sql 中的#{}替换为?号,创建PreparedStatement 的参数占位符,并通过占位符安全地设置参数(就像使用 ? 一样)

  • ${}是字符串替换

Mybatis 在处理${}时,就是把${}替换成变量的值
注意: 使${}用这种方式接受用户的输入,并用作语句参数是不安全的,会导致潜在的 SQL 注入攻击;使用#{}可以有效的防止 SQL 注入,提高系统安全性

6. 级联查询(结果映射)

在这里插入图片描述
结果映射(resultMap)

  • constructor - 用于在实例化类时,注入结果到构造方法中
    • idArg - ID 参数;标记出作为 ID 的结果可以帮助提高整体性能
    • arg - 将被注入到构造方法的一个普通结果
  • id – 一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能
  • result – 注入到字段或 JavaBean 属性的普通结果
  • association – 一个复杂类型的关联;许多结果将包装成这种类型嵌套结果映射 – 关联可以是 resultMap 元素,或是对其它结果映射的引用
  • collection – 一个复杂类型的集合嵌套结果映射 – 集合可以是 resultMap 元素,或是对其它结果映射的引用
  • discriminator – 使用结果值来决定使用哪个
  • resultMapcase – 基于某些值的结果映射嵌套结果映射 – case 也是一个结果映射,因此具有相同的结构和元素;或者引用其它的结果映射ResultMap 的属性列表

6.1 一对一

  • association(关联)
@Data
@NoArgsConstructor
public class User {
    private int id;
    private String username;
    private String password;
    private Address address;//地址:关联对象

    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }
}

关联类

@Data
@NoArgsConstructor
public class Address {

    private int userId;
    private String country;
    private String city;
}
  • resultMap

关联对象:association 关联对象的类型:javaType

<resultMap id="userMap" type="com.yauyukbiu.demo.entity.User">
    <id property="id" column="id"/>
    <id property="username" column="username"/>
    <id property="password" column="password"/>
    <association property="address" javaType="com.yauyukbiu.demo.entity.Address">
        <id property="userId" column="user_id"/>
        <id property="country" column="country"/>
        <id property="city" column="city"/>
    </association>
</resultMap>
  • select语句
//获取所有用户信息
<select id="getAll" resultMap="userMap">
   select * from user left join address on id=user_id;
</select>

6.2 一对多

  • collection(集合)
@Data
@NoArgsConstructor
public class User {
    private int id;
    private String username;
    private String password;
    private List<PhoneNumber> phoneNumber;//多值属性
}

多值属性表

@Data
@NoArgsConstructor
public class PhoneNumber {

    private int userId;
    private int number;
}
  • resultMap

集合:collection 集合中的对象类型:ofType

<collection property="phoneNumber" ofType="com.yauyukbiu.demo.entity.PhoneNumber">
     <id property="userId" column="id"/>
     <id property="number" column="number"/>
</collection>
  • select语句
<select id="getAll" resultMap="userMap">
    select * from user left join phone_number on user.id=phone_number.id
</select>

7. 日志门面

  • 日志工厂

在这里插入图片描述
SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING

  • STDOUT_LOGGING ——>mybatis标准日志工厂
    在这里插入图片描述
    在这里插入图片描述
  • SLF4J

需要slf4j-api支持以及slf4j的日志实现(推荐使用logback

  • pom.xml添加日志依赖

在这里插入图片描述

  • mybatis设置日志类型

在这里插入图片描述

  • 日志打印结果

在这里插入图片描述

8. 分页

思考:为什么要分页?

节约资源优化查询性能,避免一次查询大量数据,增大数据库的压力

8.1 使用Limit分页

语法:

SELECT * FROM table_ LIMIT startIndex, pageSize

startIndex: 索引下标,mysql默认从第0个开始查询
pageSize: 查询的数据个数

//业务场景:分页查询用户
public interface UserDao {

    //分页demo
    List<User> getAll(@Param("CurrentPageOn") int CurrentPageOn,
                      @Param("pageSize") int pageSize);
}

UserMapper.xml:编写对应dao的业务sql语句

<select id="getAll" resultMap="userMap">
    select *
    from user u
             left join address a on u.id = a.user_id
    limit #{CurrentPageOn},#{pageSize};
</select>

test

@Test
public void test() {
    //获取SqlSession连接
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    //使用和指定语句的参数和返回值相匹配的接口
    UserDao mapper = sqlSession.getMapper(UserDao.class);
    //查询获取全部用户
    //分页---------------
    int pageOn = 1;//查询页数
    int pageSize = 5;//每页查询的数据个数
    int CurrentPageOn = ((pageOn) - 1) * pageSize;//当前页数,limit默认从0开始
    List<User> userList = mapper.getAll(CurrentPageOn,pageSize);
    for (User user : userList) {
        System.out.println(user.toString());
    }
    //关闭SqlSession
    sqlSession.close();
}

在这里插入图片描述

8.2 RowBounds

  • 通过java代码层面实现分页

mybatis提供的SqlSession接口查询方法selectList

sqlSession.selectList(String var1, Object var2, RowBounds var3);

返回值:list< Object >

sql语句不进行分页,由mybatis执行分页操作

<!--RowBounds分页-->
<select id="getAllByRowBounds" resultMap="userMap">
    select *
    from user u
             left join address a on u.id = a.user_id
</select>

test

//获取SqlSession连接
SqlSession sqlSession = MyBatisUtils.getSqlSession();
//RowBounds:从第几个开始,获取数据的总数
RowBounds rowBounds = new RowBounds(0, 5);

List<User> userList = sqlSession.selectList("com.yauyukbiu.demo.dao.UserDao.getAllByRowBounds", 
                                            null, rowBounds);
for (User user : userList) {
    System.out.println(user.toString());
}
//关闭SqlSession
sqlSession.close();

在这里插入图片描述

8.3 分页插件(PageHelper)

分页插件的原理:实现 Mybatis 提供的接口,实现自定义插件,在插件的拦截方法内拦截待执行的 sql,然后重写 sql。

举例:select * from student,拦截 sql 后重写为:select t.* from (select * from student)tlimit 0,10

PageHelper官方文档:https://pagehelper.github.io/docs/

9. 使用注解开发

在这里插入图片描述

10. MyBatis 动态 SQL

  • mybatis动态sql是做什么的?

Mybatis 动态 sql 可以让我们在 xml 映射文件内,以标签的形式编写动态sql,完成逻辑判断动态拼接sql的功能

  • 有哪些动态sql
    • if
    • choose (when, otherwise)
    • trim (where, set)
    • foreach
    • bind
  • 动态sql的执行原理

使用 OGNL 从 sql 参数对象中计算表达式的值,根据表达式的值动态拼接 sql,以此来完成动态 sql 的功能。

if

使用动态 SQL 最常见情景是根据条件包含 where子句的一部分

<select id="findUser" resultType="com.yauyukbiu.demo.entity.User">
    SELECT * FROM user
    WHERE id=#{id}
    <if test="username!=null">
          AND username=#{username}
    </if>
</select>

choose、when、otherwise

有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。

<select id="findUserByChoose" resultType="com.yauyukbiu.demo.entity.User">
    SELECT * FROM user
    <where>
        <choose>
            <when test="id!=0">
                id=#{id}
            </when>
            <when test="username!=null">
                username=#{username}
            </when>
            <otherwise>
                username='令狐冲' //当没有条件匹配时,执行查询username=‘令狐冲’的用户
            </otherwise>
        </choose>
    </where>
</select>
//id=0,username=null:不执行when语句,执行otherwise语句
List<User> userList = userDao.findUserByChoose(0 ,null);

在这里插入图片描述

//id=10
List<User> userList = userDao.findUserByChoose(10 ,null);

在这里插入图片描述
choose语句只执行第一个满足when的子条件

trim、where、set

  • trim

trim标签中的 prefixsuffix 属性会被⽤于⽣成实际的 SQL 语句,会和标签内部的语句进⾏拼接,如果语句前后出现了 prefixOverrides 或者 suffixOverrides 属性中指定的值,MyBatis 框架会⾃动将其删除。
prefix:前缀
suffix:后缀

select查询语句使用trim动态拼接

<select id="findByAccount" parameterType="com.demo.entity.Account"
resultType="com.southwind.entity.Account">
 select * from t_account
 <trim prefix="where" prefixOverrides="and">//prefixOverrides若下边拼接的sql语句前缀为and则将其删除
 	<if test="id!=0">
 		id = #{id}
 	</if>
 	<if test="username!=null">
 		and username = #{username}
 	</if>
 	<if test="password!=null">
 		and password = #{password}
 	</if>
 	<if test="age!=0">
 		and age = #{age}
 	</if>
 </trim>
</select>

insert插入语句使用trim动态拼接

<insert id="addUser" parameterType="User" useGeneratedKeys="true">
    INSERT INTO user
    <trim prefix="(" suffix=")" suffixOverrides=",">//suffixOverrides去掉最后一个”,“
        <if test="loginName != null">login_name,</if>
        <if test="email != null">email,</if>
        <if test="phone != null">phone,</if>
        <if test="password != null">`password`,</if>
        <if test="status != null">`status`,</if>
        <if test="icon != null">icon,</if>
        <if test="role != null">role,</if>
    </trim>
    <trim prefix="VALUES (" suffix=")" suffixOverrides=",">//suffixOverrides去掉最后一个”,“
        <if test="loginName != null">#{loginName},</if>
        <if test="email != null">#{email},</if>
        <if test="phone != null">#{phone},</if>
        <if test="password != null">#{password},</if>
        <if test="status != null">#{status},</if>
        <if test="icon != null">#{icon},</if>
        <if test="role != null">#{role},</if>
    </trim>
</insert>
  • where
  • 动态的匹配where条件语句
  • 假如第一条条件查询不匹配,则后面的若干条件语句智能去掉首个and连接符

where元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。

<select id="findUser" resultType="com.yauyukbiu.demo.entity.User">
SELECT * FROM user
<where>
    <if test="id!=0">
        id=#{id}
    </if>
    <if test="username!=null">
        AND username=#{username}
    </if>
</where>
</select>

使用普通where执行多条件查询时的问题↓
在这里插入图片描述

  • set
    set标签⽤于 update操作,会⾃动根据参数选择⽣成 SQL 语句
<update id="updateAuthorIfNecessary">
  update Author
    <set>
      <if test="username != null">username=#{username},</if>
      <if test="password != null">password=#{password},</if>
      <if test="email != null">email=#{email},</if>
      <if test="bio != null">bio=#{bio}</if>
    </set>
  where id=#{id}
</update>

foreach

动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)

foreach标签可以迭代⽣成⼀系列值,这个标签主要⽤于 SQL 的in 语句

可以将任何可迭代对象(如 ListSet 等)、Map 对象或者Array数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。

普通sql语句

SELECT * FROM `user` WHERE id in(1,5,7,9,11,16)

foreach动态拼接语句

<select id="findUserByIds" parameterType="_int" resultType="com.demo.entity.User">
    SELECT * FROM user
    <where>
        <foreach collection="array" item="id"
                 open="id in(" separator="," close=")">
            #{id}
        </foreach>
    </where>
</select>

test

List<User> findUserByIds(int[] ids);
//获取sqlSession连接
sqlSession = MyBatisUtils.getSqlSession();
UserDao userDao = sqlSession.getMapper(UserDao.class);

int[] ids = new int[]{5, 6, 8, 12, 15,};
List<User> userList = userDao.findUserByIds(ids);

if (!userList.isEmpty()) {
    for (User user : userList) {
        System.out.println(user.toString());
    }
}

在这里插入图片描述

SQL片段

将sql语句中重复的sql片段提取出来放置为公共的,便于复用该sql片段

<sql id="if-id-username">//sql片段id
    <if test="id!=0">
        id=#{id}
    </if>
    <if test="username!=null">
        AND username=#{username}
    </if>
</sql>

调用某一sql片段时,只需添加include标签即可
refid为sql片段映射id

<select id="findUserByWhere" resultType="com.demo.entity.User">
    SELECT * FROM user
    <where>
        <include refid="if-id-username"/>
    </where>
</select>

bind

bind元素允许你在 OGNL 表达式以外创建一个变量,并将其绑定到当前的上下文.


<select id="selectBlogsLike" resultType="Blog">
  <bind name="pattern" value="'%' + _parameter.getTitle() + '%'" />
  SELECT * FROM BLOG
  WHERE title LIKE #{pattern}
</select>

11. MyBatis 缓存

  1. 什么是缓存【Cache】?
    - 存在内存中的临时数据
    - 将用户经常查询的数据放在缓存[内存]中,当用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中直接获取存在的数据,从而提高查询效率,解决了高并发系统的性能问题。
  2. 为什么使用缓存?
    - 减少和数据库的交互次数,减少系统开销,提高系统效率
  3. 什么样的数据推荐使用缓存?
    - 经常查询并且不经常改变的数据,热点数据

11.1 MyBatis 缓存分类

1、⼀级缓存:SqlSession 级别,默认开启,并且不能关闭。
操作数据库时需要创建 SqlSession 对象,在对象中有⼀个 HashMap ⽤于存储缓存数据,不同的
SqlSession 之间缓存数据区域是互不影响的。
⼀级缓存的作⽤域是 SqlSession 范围的,当在同⼀个 SqlSession 中执⾏两次相同的 SQL 语句事,第⼀次执⾏完毕会将结果保存到缓存中,第⼆次查询时直接从缓存中获取。
需要注意的是,如果 SqlSession 执⾏了 DML 操作(insert、update、delete),MyBatis 必须将缓存
清空以保证数据的准确性。
2、⼆级缓存:Mapper 级别,默认关闭,可以开启。
使⽤⼆级缓存时,多个 SqlSession 使⽤同⼀个 Mapper 的 SQL 语句操作数据库,得到的数据会存在⼆级缓存区,同样是使⽤ HashMap 进⾏数据存储,相⽐较于⼀级缓存,⼆级缓存的范围更⼤,多个
SqlSession 可以共⽤⼆级缓存,⼆级缓存是跨 SqlSession 的。
⼆级缓存是多个 SqlSession 共享的,其作⽤域是 Mapper 的同⼀个 namespace,不同的 SqlSession两次执⾏相同的 namespace 下的 SQL 语句,参数也相等,则第⼀次执⾏成功之后会将数据保存到⼆级缓存中,第⼆次可直接从⼆级缓存中取出数据

11.2 一级缓存

每次创建sqlSession默认开启一级缓存,该缓存仅在当前会话内有效

  • 映射语句文件中的所有 select 语句的结果将会被缓存
  • 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存
  • 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
  • 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
  • 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
  • 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。

11.3 二级缓存

MyBatis ⾃带的⼆级缓存

  • mybatis-config,xml 配置开启二级缓存
<setting name="cacheEnabled" value="true"/>
  • Mapper.xml 中配置⼆级缓存
<cache
  eviction="FIFO"
  flushInterval="60000"
  size="512"
  readOnly="true"/>

这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。
可用的清除策略有:

LRU – 最近最少使用:移除最长时间不被使用的对象。
FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。
WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
默认的清除策略是 LRU

flushInterval(刷新间隔)属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量。 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。

size(引用数目)属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。

readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false。

提示: 二级缓存是事务性的。这意味着,当 SqlSession 完成并提交时,或是完成并回滚,但没有执行 flushCache=true 的 insert/delete/update 语句时,缓存会获得更新。

  • 需要实体类实现序列化接⼝
@Data
public class User implements Serializable {
    private int id;
    private String username;
    private String password;
    private Address address;
    private List<PhoneNumber> phoneNumber;//多值属性
}

自定义缓存——ehcache

  • pom.xml 添加相关依赖
<!--ehcache二级缓存-->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-ehcache</artifactId>
    <version>1.0.0</version>
</dependency>
<dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>2.10.5</version>
</dependency>
  • 添加 ehcache.xml
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
    <diskStore/>
    <defaultCache
            eternal="false"
            maxElementsInMemory="10000"
            maxElementsOnDisk="10000000"
            overflowToDisk="false"
            timeToIdleSeconds="1800"
            timeToLiveSeconds="259200"
            memoryStoreEvictionPolicy="LRU">
    </defaultCache>
</ehcache>
  • config.xml 配置开启⼆级缓存
<!--设置日志-->
<setting name="logImpl" value="SLF4J"/>
<!--开启二级缓存-->
<setting name="cacheEnabled" value="true"/>
  • Mapper.xml中设置二级缓存
<cache type="org.mybatis.caches.ehcache.EhcacheCache">
 <!-- 缓存创建之后,最后⼀次访问缓存的时间⾄缓存失效的时间间隔 -->
 <property name="timeToIdleSeconds" value="3600"/>
 <!-- 缓存⾃创建时间起⾄失效的时间间隔 -->
 <property name="timeToLiveSeconds" value="3600"/>
 <!-- 缓存回收策略,LRU表示移除近期使⽤最少的对象 -->
 <property name="memoryStoreEvictionPolicy" value="LRU"/>
</cache>
  • 实体类不需要实现序列化接⼝
@Data
public class User {
    private int id;
    private String username;
    private String password;
    private Address address;
    private List<PhoneNumber> phoneNumber;//多值属性
}

12. MyBatis 延迟加载

  • 什么是延迟加载?

延迟加载也叫懒加载惰性加载,使⽤延迟加载可以提⾼程序的运⾏效率,针对于数据持久层的操作,在某些特定的情况下去访问特定的数据库,在其他情况下可以不访问某些表,从⼀定程度上减少了 Java应⽤与数据库的交互次数

查询学⽣和班级的时,学⽣和班级是两张不同的表,如果当前需求只需要获取学⽣的信息,那么查询学⽣单表即可,如果需要通过学⽣获取对应的班级信息,则必须查询两张表。

不同的业务需求,需要查询不同的表,根据具体的业务需求来动态减少数据表查询的⼯作就是延迟加载

在 config.xml 中开启延迟加载

<settings>
    <!--设置日志-->
    <setting name="logImpl" value="SLF4J"/>
    <!--开启懒加载-->
    <setting name="lazyLoadingEnabled" value="true"/>
    <!--开启二级缓存-->
    <setting name="cacheEnabled" value="true"/>
</settings>

将多表关联查询拆分成多个单表查询

实体类User

@Data
public class User {
    private int id;
    private String username;
    private String password;
    private Address address;//与Address类关联
}

UserDao

User findUserByLazy(int id);

UserMapper.xml

<!--懒加载查询(多表嵌套查询)-->
<resultMap id="userMapLazy" type="com.demo.entity.User">
    <id property="id" column="id"/>
    <result property="username" column="username"/>
    <result property="password" column="password"/>
    <association property="address" javaType="com.demo.entity.Address"
                 select="com.demo.dao.AddressDao.findAddressByLazy" column="id"/>
</resultMap>

<select id="findUserByLazy" parameterType="int" resultMap="userMapLazy">
   SELECT * FROM user where id=#{id}
</select>

关联实体类Address

@Data
@NoArgsConstructor
public class Address {
    private int userId;
    private String country;
    private String city;
}

AddressDao

 Address findAddressByLazy(int id);

AddressMapper.xml

<select id="findAddressByLazy" parameterType="int" resultType="com.demo.entity.Address">
   select * from address where user_id=#{id}
</select>

懒加载测试

1、业务场景一:只需要查询用户的用户名

@Test
public void test() {
	//获取sqlSession连接
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    UserDao userDao = sqlSession.getMapper(UserDao.class);
    User user = userDao.findUserByLazy(5);
    System.out.println(user.getUsername());//只需要用户的username
}

结果
在这里插入图片描述
从结果看出:由于用户的username在user表中,并不需要关联表的信息;所以只查询了一张表

2、业务场景二:需要查询用户的所有信息,包括关联信息

@Test
public void test() {
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    UserDao userDao = sqlSession.getMapper(UserDao.class);
    User user = userDao.findUserByLazy(5);
    System.out.println(user.toString());//打印用户所有信息
}

结果
在这里插入图片描述
从结果得出:获取用户所有信息走了嵌套查询来获取关联类Address的信息

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值