MyBatis学习总结(四) MyBatis 延迟加载策略MyBatis 一级缓存、二级缓存MyBatis注解开发

news2024/11/25 16:35:42

MyBatis学习总结(四) MyBatis 延迟加载策略/MyBatis 一级缓存、二级缓存/MyBatis注解开发

一、 MyBatis 延迟加载策略

通过前面的学习,我们已经掌握了 MyBatis 中一对一(多对一)、一对多、多对多关系的配置及实现,可以实现对象的关联查询。实际开发过程中很多时候我们并不需要总是将关联的对象的信息也一并加载出来,此时就是我们所说的延迟加载

举例:用户和账户
一个账户只能属于一个用户(多个账户也可以属于同一个用户):一对一(多对一)
一个用户可以有多个账户:一对多

在查询用户时,可能我就只是想查询用户信息,并不关心其账户,因此用户下的账户信息应该是什么时候使用什么时候才加载出来(延迟加载)。
在查询账户时,光是查出账户信息而不知道此账户属于哪个用户会很不直观,因此账户的所属用户信息应该是随着账户查询时一起立即查询出来(立即加载)。

(一)什么是延迟加载

延迟加载:就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载。

  • 好处:先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。
  • 坏处:因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗时间,所以可能造成用户等待时间变长,造成用户体验下降。

在对应的四种表关系中:

  • 一对一,多对一:通常情况下我们都是采用立即加载
  • 一对多,多对多:通常情况下我们都是采用延迟加载

(二)延迟加载的实现

在实现延迟加载之前,我们必须做一些配置。

进入 MyBatis 的官方文档,找到 settings 的说明信息:

设置名描述默认值
lazyLoadingEnabled延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。false
aggressiveLazyLoading开启时,任一方法的调用都会加载该对象的所有延迟加载属性。 否则,每个延迟加载属性会按需加载(参考 lazyLoadTriggerMethods)。false (在 MyBatis 3.4.1 及之前的版本中默认为 true)

我们需要在 MyBatis 的核心配置文件中添加延迟加载的配置:

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

如果MyBatis版本是3.4.1以后就可以不添加aggressiveLazyLoading,因为默认是false。

1.一对一(多对一)实现延迟加载

案例:查询所有账户及其所属用户信息
用户表、账户表、用户实体类、账户实体类,可在前篇博客查看。

IAccountDao:

import com.fox.pojo.Account;
import java.util.List;

public interface IAccountDao {
    //查询所有账户及其所属用户信息
    List<Account> findAll();
}

IAccountDao.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">

<!--namespace对应接口的全限定类名-->
<mapper namespace="com.fox.dao.IAccountDao">
    <resultMap id="accountUser" type="com.fox.pojo.Account">
        <id property="id" column="id"></id>
        <result property="uid" column="uid"></result>
        <result property="money" column="money"></result>
        <!-- 一对一的关系映射:配置封装user的内容
        select属性指定的内容:查询用户的唯一标识:
        column属性指定的内容:用户根据id查询时,指定使用哪个字段的值作为条件查询-->
        <association property="user" column="uid" javaType="com.fox.pojo.User" select="com.fox.dao.IUserDao.findUserById"></association>
    </resultMap>
    <!--查询所有账户及其所属用户信息-->
    <select id="findAll" resultMap="accountUser">
        select * from account;
    </select>
</mapper>

IUserDao:

import com.fox.pojo.User;
import java.util.List;

public interface IUserDao {

    //根据id查询用户以及所包含的账户信息
    User findUserById(Integer id);
}

IUserDao.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">

<!--namespace对应接口的全限定类名-->
<mapper namespace="com.fox.dao.IUserDao">

    <!--根据id查询用户以及所包含的账户信息-->
    <select id="findUserById" parameterType="Integer" resultType="com.fox.pojo.User">
        select * from user where id=#{id};
    </select>
</mapper>

测试类:

import com.fox.dao.IAccountDao;
import com.fox.pojo.Account;
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 AccountTest {
    private InputStream in = null;
    private SqlSession sqlSession = null;
    private IAccountDao accountDao = null;

    @Before //标记在非静态方法上。在@Test方法前面执行,而且是在每一个@Test方法前面都执行。
    public void init() throws IOException {
        //1.读取配置文件
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.使用构建者创建工厂对象 SqlSessionFactory
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        //3.使用 SqlSessionFactory 生产 SqlSession 对象
        sqlSession = factory.openSession();
        //4.使用 SqlSession 创建 dao 接口的代理对象
        accountDao = sqlSession.getMapper(IAccountDao.class);
    }

    @After //标记在非静态方法上。在@Test方法后面执行,而且是在每一个@Test方法后面都执行。
    public void destroy() throws IOException {
        sqlSession.commit();
        //释放资源
        sqlSession.close();
        in.close();
    }

    @Test
    public void testFindAll() {
        List<Account> accounts = accountDao.findAll();
    }
}

在这里插入图片描述
我们发现,因为本次只是将 Account对象查询出来放入 List 集合中,并没有涉及到 User对象,所以就没有发出 SQL 语句查询账户所关联的 User 对象的查询。

(三)一对多实现延迟加载

案例:查询所有用户及其所包含的账户信息
用户表、账户表、用户实体类、账户实体类,可在前篇博客查看。

IUserDao:

import com.fox.pojo.User;
import java.util.List;

public interface IUserDao {

    //查询所有用户以及所包含的账户信息
    List<User> findAll();
}

IUserDao.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">

<!--namespace对应接口的全限定类名-->
<mapper namespace="com.fox.dao.IUserDao">
    <!-- 定义User的resultMap-->
    <resultMap id="userAccountMap" type="com.fox.pojo.User">
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="birthday" column="birthday"></result>
        <result property="sex" column="sex"></result>
        <result property="address" column="address"></result>
        <!--select属性指定的内容:查询账户的唯一标识:
        column属性指定的内容:根据用户id查询账户时,指定使用哪个字段的值作为条件查询-->
        <collection property="accounts" ofType="com.fox.pojo.Account" column="id" select="com.fox.dao.IAccountDao.findAccountByUid"></collection>
    </resultMap>

    <!-- 查询所有用户以及所包含的账户信息 -->
    <select id="findAll" resultMap="userAccountMap">
        select * from user;
    </select>
</mapper>

IAccountDao:

import com.fox.pojo.Account;

public interface IAccountDao {

    //根据用户id查询账户及用户信息
    List<Account> findAccountByUid(Integer uid);
}

IAccountDao.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">
<!--namespace对应接口的全限定类名-->
<mapper namespace="com.fox.dao.IAccountDao">

    <!--根据用户id查询账户及用户信息-->
    <select id="findAccountByUid" parameterType="Integer" resultType="com.fox.pojo.Account">
        select * from account where uid=#{uid};
    </select>
</mapper>

测试类:

import com.fox.dao.IUserDao;
import com.fox.pojo.User;
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 UserTest {

    private InputStream in = null;
    private SqlSession sqlSession =null;
    private IUserDao userDao = null;

    @Before //标记在非静态方法上。在@Test方法前面执行,而且是在每一个@Test方法前面都执行。
    public void init() throws IOException {
        //1.读取配置文件
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.使用构建者创建工厂对象 SqlSessionFactory
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        //3.使用 SqlSessionFactory 生产 SqlSession 对象
        sqlSession = factory.openSession();
        //4.使用 SqlSession 创建 dao 接口的代理对象
        userDao = sqlSession.getMapper(IUserDao.class);
    }

    @After //标记在非静态方法上。在@Test方法后面执行,而且是在每一个@Test方法后面都执行。
    public void destroy() throws IOException {
        sqlSession.commit();
        //释放资源
        sqlSession.close();
        in.close();
    }

    @Test
    public void testFindAll(){
        List<User> users = userDao.findAll();
    }
}

在这里插入图片描述
同样的,我们发现并没有加载 Account 账户信息。

二、MyBatis 缓存

  • 什么是缓存?
    存在于内存中的临时数据。
  • 为什么使用缓存?
    减少和数据库的交互次数,提高执行效率。
  • 什么样的数据能使用缓存,什么样的数据不能使用?
    • 适用于缓存:
      (1)经常查询并且不经常改变的。
      (2)数据的正确与否对最终结果影响不大的。
    • 不适用于缓存:
      (1)经常改变的数据
      (2)数据的正确与否对最终结果影响很大的。
      例如:商品的库存,银行的汇率,股市的牌价。

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

MyBatis 中缓存分为一级缓存,二级缓存。

在这里插入图片描述

(一)MyBatis一级缓存

1.证明一级缓存的存在

User实体类:

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

public class User implements Serializable {

    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String 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;
    }

}

IUserDao:

import com.fox.pojo.User;

public interface IUserDao {

    //根据id查询用户以及所包含的账户信息
    User findUserById(Integer id);
}

IUserDao.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">

<!--namespace对应接口的全限定类名-->
<mapper namespace="com.fox.dao.IUserDao">

    <!--根据id查询用户-->
    <select id="findUserById" parameterType="Integer" resultType="com.fox.pojo.User">
        select * from user where id=#{id};
    </select>
</mapper>

测试类:

import com.fox.dao.IUserDao;
import com.fox.pojo.User;
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 UserTest {

    private InputStream in = null;
    private SqlSession sqlSession =null;
    private IUserDao userDao = null;

    @Before //标记在非静态方法上。在@Test方法前面执行,而且是在每一个@Test方法前面都执行。
    public void init() throws IOException {
        //1.读取配置文件
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.使用构建者创建工厂对象 SqlSessionFactory
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        //3.使用 SqlSessionFactory 生产 SqlSession 对象
        sqlSession = factory.openSession();
        //4.使用 SqlSession 创建 dao 接口的代理对象
        userDao = sqlSession.getMapper(IUserDao.class);
    }

    @After //标记在非静态方法上。在@Test方法后面执行,而且是在每一个@Test方法后面都执行。
    public void destroy() throws IOException {
        sqlSession.commit();
        //释放资源
        sqlSession.close();
        in.close();
    }

    @Test
    public void testFindAll(){
        User user1 = userDao.findUserById(41);
        System.out.println(user1);
        User user2 = userDao.findUserById(41);
        System.out.println(user2);
        System.out.println(user1==user2);
    }
}

在这里插入图片描述
我们可以发现,虽然在上面的代码中我们查询了两次,但最后只执行了一次数据库操作,这就是 MyBatis 提供给我们的一级缓存在起作用了。因为一级缓存的存在,导致第二次查询 id 为 41 的记录时,并没有发出 sql 语句从数据库中查询数据,而是从一级缓存中查询。

2.一级缓存的分析

  • 一级缓存指的是MyBatis中SqlSession对象的缓存。
  • 当我们执行查询之后,查询的结果会同时存入到SqlSession为我们提供的一块区域中,该区域的结构是一个Map。
  • 当我们再次查询同样的数据,MyBatis会先去SqlSession中查询是否有,有的话直接拿出来用。当SqlSession对象消失时,MyBatis的一级缓存也就消失了。
  • 一级缓存是 SqlSession 范围的缓存,当调用修改、添加、删除、sqlSession.commit()sqlSession.close()等方法时,就会清空一级缓存。

在这里插入图片描述

  • 第一次发起查询用户 id 为 1 的用户信息,先去找缓存中是否有 id 为 1 的用户信息,如果没有,从数据库查询用户信息。
  • 得到用户信息,将用户信息存储到一级缓存中。
  • 如果 sqlSession 去执行 commit 操作(执行插入、更新、删除),清空 SqlSession 中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
  • 第二次发起查询用户 id 为 1 的用户信息,先去找缓存中是否有 id 为 1 的用户信息,缓存中有,直接从缓存中获取用户信息。

(1)测试一级缓存的清空

①sqlSession.close();
import com.fox.dao.IUserDao;
import com.fox.pojo.User;
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 UserTest {

    private InputStream in = null;
    private SqlSession sqlSession =null;
    private IUserDao userDao = null;
    private SqlSessionFactory factory =null;

    @Before //标记在非静态方法上。在@Test方法前面执行,而且是在每一个@Test方法前面都执行。
    public void init() throws IOException {
        //1.读取配置文件
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.使用构建者创建工厂对象 SqlSessionFactory
        factory = new SqlSessionFactoryBuilder().build(in);
        //3.使用 SqlSessionFactory 生产 SqlSession 对象
        sqlSession = factory.openSession();
        //4.使用 SqlSession 创建 dao 接口的代理对象
        userDao = sqlSession.getMapper(IUserDao.class);
    }

    @After //标记在非静态方法上。在@Test方法后面执行,而且是在每一个@Test方法后面都执行。
    public void destroy() throws IOException {
        sqlSession.commit();
        //释放资源
        sqlSession.close();
        in.close();
    }

    @Test
    public void testFindAll(){
        User user1 = userDao.findUserById(41);
        System.out.println(user1);

        sqlSession.close();
        //再次获取SqlSession对象
        sqlSession = factory.openSession();
        userDao = sqlSession.getMapper(IUserDao.class);

        User user2 = userDao.findUserById(41);
        System.out.println(user2);
        System.out.println(user1==user2);
    }
}

在这里插入图片描述

②sqlSession.clearCache();
import com.fox.dao.IUserDao;
import com.fox.pojo.User;
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 UserTest {

    private InputStream in = null;
    private SqlSession sqlSession =null;
    private IUserDao userDao = null;

    @Before //标记在非静态方法上。在@Test方法前面执行,而且是在每一个@Test方法前面都执行。
    public void init() throws IOException {
        //1.读取配置文件
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.使用构建者创建工厂对象 SqlSessionFactory
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        //3.使用 SqlSessionFactory 生产 SqlSession 对象
        sqlSession = factory.openSession();
        //4.使用 SqlSession 创建 dao 接口的代理对象
        userDao = sqlSession.getMapper(IUserDao.class);
    }

    @After //标记在非静态方法上。在@Test方法后面执行,而且是在每一个@Test方法后面都执行。
    public void destroy() throws IOException {
        sqlSession.commit();
        //释放资源
        sqlSession.close();
        in.close();
    }

    @Test
    public void testFindAll(){
        User user1 = userDao.findUserById(41);
        System.out.println(user1);

        sqlSession.clearCache();

        User user2 = userDao.findUserById(41);
        System.out.println(user2);
        System.out.println(user1==user2);
    }
}

在这里插入图片描述

③sqlSession.commit();(增删改)

IUserDao:

import com.fox.pojo.User;

public interface IUserDao {

    //根据id查询用户以及所包含的账户信息
    User findUserById(Integer id);

    //更新用户
    void updateUser(User user);
}

IUserDao.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">

<!--namespace对应接口的全限定类名-->
<mapper namespace="com.fox.dao.IUserDao">

    <!--根据id查询用户-->
    <select id="findUserById" parameterType="Integer" resultType="com.fox.pojo.User">
        select * from user where id=#{id};
    </select>

    <update id="updateUser" parameterType="com.fox.pojo.User">
        update user set username=#{username},address=#{address} where id=#{id};
    </update>
</mapper>

测试类:

import com.fox.dao.IUserDao;
import com.fox.pojo.User;
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 UserTest {

    private InputStream in = null;
    private SqlSession sqlSession =null;
    private IUserDao userDao = null;

    @Before //标记在非静态方法上。在@Test方法前面执行,而且是在每一个@Test方法前面都执行。
    public void init() throws IOException {
        //1.读取配置文件
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.使用构建者创建工厂对象 SqlSessionFactory
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        //3.使用 SqlSessionFactory 生产 SqlSession 对象
        sqlSession = factory.openSession();
        //4.使用 SqlSession 创建 dao 接口的代理对象
        userDao = sqlSession.getMapper(IUserDao.class);
    }

    @After //标记在非静态方法上。在@Test方法后面执行,而且是在每一个@Test方法后面都执行。
    public void destroy() throws IOException {
        sqlSession.commit();
        //释放资源
        sqlSession.close();
        in.close();
    }

    @Test
    public void testFindAll(){
        //根据id查询用户
        User user1 = userDao.findUserById(41);
        System.out.println(user1);
        //更新用户信息
        user1.setUsername("小何");
        user1.setAddress("广东");
        userDao.updateUser(user1);
        //再次查询id为41的用户
        User user2 = userDao.findUserById(41);
        System.out.println(user2);
        System.out.println(user1==user2);
    }
}

在这里插入图片描述

(二)MyBatis二级缓存

  • 二级缓存是 Mapper 映射级别的缓存,多个 SqlSession 去操作同一个 Mapper 映射的 sql 语句,多个SqlSession 可以共用二级缓存,二级缓存是跨 SqlSession 的。
  • 二级缓存实际上指的是MyBatis中SqlSessionFactory对象的缓存。由同一个SqlSessionFactory对象创建的SqlSession共享其缓存。
  • 二级缓存的使用步骤:
    第一步:让MyBatis框架支持二级缓存(在MyBatis核心配置文件中配置)
    第二步:让当前的映射文件支持二级缓存(在IUserDao.xml中配置)
    第三步:让当前的操作支持二级缓存(在select标签中配置)

案例
让MyBatis框架支持二级缓存(在MyBatis核心配置文件中配置):

<settings>
	<!-- 开启二级缓存的支持 -->
    <setting name="cacheEnabled" value="true"/>
</settings>

因为 cacheEnabled 的默认值就为 true,所以这一步可以省略不配置。为 true 代表开启二级缓存;为false 代表不开启二级缓存。

让当前的映射文件支持二级缓存<cache></cache>(在IUserDao.xml中配置)并且让当前的操作支持二级缓存(在select标签中配置)useCache="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">

<!--namespace对应接口的全限定类名-->
<mapper namespace="com.fox.dao.IUserDao">

    <!-- 开启二级缓存的支持 -->
    <cache></cache>
    <!--根据id查询用户-->
    <select id="findUserById" parameterType="Integer" resultType="com.fox.pojo.User" useCache="true">
        select * from user where id=#{id};
    </select>

</mapper>

注意:针对每次查询都需要最新的数据的 sql,要设置成 useCache="false",不要用二级缓存。

测试类:

import com.fox.dao.IUserDao;
import com.fox.pojo.User;
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 UserTest {

    private InputStream in = null;
    private SqlSessionFactory factory =null;

    @Before //标记在非静态方法上。在@Test方法前面执行,而且是在每一个@Test方法前面都执行。
    public void init() throws IOException {
        //1.读取配置文件
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.使用构建者创建工厂对象 SqlSessionFactory
        factory = new SqlSessionFactoryBuilder().build(in);
    }

    @After //标记在非静态方法上。在@Test方法后面执行,而且是在每一个@Test方法后面都执行。
    public void destroy() throws IOException {
        in.close();
    }

    @Test
    public void testFindAll(){
        SqlSession sqlSession1 = factory.openSession();
        IUserDao dao1 = sqlSession1.getMapper(IUserDao.class);
        User user1 = dao1.findUserById(41);
        System.out.println(user1);
        sqlSession1.close();//一级缓存消失,不让一级缓存干扰二级缓存的结果

        SqlSession sqlSession2 = factory.openSession();
        IUserDao dao2 = sqlSession2.getMapper(IUserDao.class);
        User user2 = dao2.findUserById(41);
        System.out.println(user2);
        sqlSession2.close();//一级缓存消失,不让一级缓存干扰二级缓存的结果

        System.out.println(user1 == user2);
    }
}

在这里插入图片描述
经过上面的测试,我们发现执行了两次查询,并且在执行第一次查询后,我们关闭了一级缓存,再去执行第二次查询时,我们发现并没有对数据库再发出第二次的 sql 语句,所以此时的数据就只能是来自于我们所说的二级缓存。但是为什么还是false呢,这是因为在二级缓存中存放的内容是数据而不是对象。它会重新创建一个User对象,将缓存的数据再放入这个新User对象,因此user1!=user2
在这里插入图片描述
注意:当我们在使用二级缓存时,所缓存的类一定要实现 java.io.Serializable 接口,这种就可以使用序列化方式来保存对象。

三、MyBatis注解开发

MyBatis注解开发是用注解代替了映射的xml配置文件,但是MyBatis核心配置文件还是需要的。
注意:使用了MyBatis注解开发就不能同时写xml映射文件,不然会报错。

(一)注解实现基本 CRUD

案例
User类:

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

public class User implements Serializable {

    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String 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;
    }

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

MyBatis核心配置文件:
由于注解开发替代了Mapper映射文件,所以本例无Mapper映射文件,因此映射器的写法也有变动:

<?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">
<!-- mybatis的主配置文件 -->
<configuration>
    <properties resource="jdbc.properties">
    </properties>

    <!--使用typeAliases配置别名 -->
    <typeAliases>
    <!--typeAlias用于配置别名。type属性指定的是实体类全限定类名。alias属性指定别名-->
        <!-- 用于指定要配置别名的包,当指定之后,该包下的所有实体类都会注册别名,并且类名就是别名,不再区分大小写-->
        <package name="com.fox.pojo"></package>
    </typeAliases>
<!-- 配置环境 -->
    <environments default="mysql">
        <!-- 配置mysql的环境-->
        <environment id="mysql">
            <!-- 配置事务的类型-->
            <transactionManager type="JDBC"></transactionManager>
            <!-- 配置数据源(连接池) -->
            <dataSource type="POOLED">
                <!-- 配置连接数据库的4个基本信息 -->
                <property name="driver" value="${jdbc.driver}"/>
                <!--xml中不允许&符号直接出现,我们需要使用 &amp; 代替-->
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>

    <!-- 指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件 -->
    <mappers>
        <package name="com.fox.dao"/>
        <!--或者<mapper class="com.fox.dao.IUserDao"/>-->
    </mappers>
</configuration>

IUserDao:
注意:传入单个参数可以省略@Param ,传入多个参数必须使用@Param并用逗号隔开

import com.fox.pojo.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 IUserDao {
    //查询所有用户
    @Select("select * from user")
    List<User> findAll();

    //根据id查询用户
    @Select("select * from user where id=#{id}")
    User findUserById(Integer id);

    //根据用户名模糊查询用户
    @Select("select * from user where username like #{username}")
    List<User> findUserByName(String name);

    //根据id和用户名查询用户
    @Select("select * from user where id=#{id} and username=#{name}")
    User findUserByTwo(@Param("id") Integer id,@Param("name") String name);
    
    //查询总用户数量
    @Select("select count(*) from user")
    int findTotal();

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

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

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

测试类:

import com.fox.dao.IUserDao;
import com.fox.pojo.User;
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 UserTest {
    private InputStream in = null;
    private SqlSession sqlSession =null;
    private IUserDao userDao = null;

    @Before //标记在非静态方法上。在@Test方法前面执行,而且是在每一个@Test方法前面都执行。
    public void init() throws IOException {
        //1.读取配置文件
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.使用构建者创建工厂对象 SqlSessionFactory
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        //3.使用 SqlSessionFactory 生产 SqlSession 对象
        sqlSession = factory.openSession();
        //4.使用 SqlSession 创建 dao 接口的代理对象
        userDao = sqlSession.getMapper(IUserDao.class);
    }

    @After //标记在非静态方法上。在@Test方法后面执行,而且是在每一个@Test方法后面都执行。
    public void destroy() throws IOException {
        //提交事务
        sqlSession.commit();
        //释放资源
        sqlSession.close();
        in.close();
    }

    //查询所有
    @Test
    public void testFindAll(){
        List<User> users = userDao.findAll();
        for (User user : users) {
            System.out.println(user);
        }
    }

    //根据id查询
    @Test
    public void testFindUserById(){
        User user = userDao.findUserById(41);
        System.out.println(user);
    }
    //根据用户名模糊查询
    @Test
    public void testFindUserByName(){
        List<User> users = userDao.findUserByName("%王%");
        for (User user : users) {
            System.out.println(user);
        }
    }
    //根据id和用户名查询用户
    @Test
    public void testFindUserByTwo(){
        User user = userDao.findUserByTwo(41, "小何");
        System.out.println(user);
    }
    //查询总用户数
    @Test
    public void testFindTotal(){
        int total = userDao.findTotal();
        System.out.println("共有"+total+"个用户");
    }
    //添加用户
    @Test
    public void testAddUser(){
        User user = new User();
        user.setUsername("小张");
        user.setAddress("河南");
        user.setBirthday(new Date());
        user.setSex("女");
        userDao.addUser(user);
    }
    //更新用户
    @Test
    public void testUpdateUser(){
        User user = new User();
        user.setId(46);
        user.setUsername("小杰");
        user.setAddress("天津");
        user.setBirthday(new Date());
        user.setSex("男");
        userDao.updateUser(user);
    }
    //删除用户
    @Test
    public void testDeleteUser(){
        userDao.deleteUserById(49);
    }
}

(二)注解建立实体类属性和数据表中列的对应关系

假如数据表中的列名是这样:
在这里插入图片描述

User类是这样:

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

public class User implements Serializable {

    private Integer userId;
    private String userName;
    private Date userBirthday;
    private String userSex;
    private String userAddress;

    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public Date getUserBirthday() {
        return userBirthday;
    }

    public void setUserBirthday(Date userBirthday) {
        this.userBirthday = userBirthday;
    }

    public String getUserSex() {
        return userSex;
    }

    public void setUserSex(String userSex) {
        this.userSex = userSex;
    }

    public String getUserAddress() {
        return userAddress;
    }

    public void setUserAddress(String userAddress) {
        this.userAddress = userAddress;
    }

    @Override
    public String toString() {
        return "User{" +
                "userId=" + userId +
                ", userName='" + userName + '\'' +
                ", userBirthday=" + userBirthday +
                ", userSex='" + userSex + '\'' +
                ", userAddress='" + userAddress + '\'' +
                '}';
    }
}

属性名和列名不对应,之前我们可以在映射文件中通过配置<resultMap>来实现,在使用注解开发时我们需要借助 @Results 注解、@Result 注解。

  • @Results 注解:
    代替的是标签<resultMap>
    • @Results 中 属性介绍:
      • id:此resultMap的唯一标识
      • value:@Result集合
    • 当只有单个@Result,可以省略大括号。
      @Results({@Result(),@Result()})@Results(@Result())都是正确的。
  • @Result 注解:
    代替了<id>标签和<result>标签
    • @Result 中 属性介绍:
      • id:是否是主键字段
      • column:数据库的列名
      • property:需要装配的属性名
      • one:需要使用的@One 注解(@Result(one=@One())
      • many:需要使用的@Many 注解(@Result(many=@many())
  • @ResultMap 注解
    当这段@Results代码需要在多个方法用到时,为了提高代码复用性,我们可以使用@ResultMap注解来复用这段代码,@ResultMap注解的value就是这个@Results注解的id值。

案例

import com.fox.pojo.User;
import org.apache.ibatis.annotations.*;
import java.util.List;

public interface IUserDao {
    //查询所有用户
    @Select("select * from user")
    @Results(id = "userMap",value = {
            @Result(id = true,property = "userId",column = "id"),
            @Result(property = "userName",column = "username"),
            @Result(property = "userBirthday",column = "birthday"),
            @Result(property = "userAddress",column = "address"),
            @Result(property = "userSex",column = "sex")
    })
    List<User> findAll();

    //根据id查询用户
    @Select("select * from user where id=#{id}")
    @ResultMap("userMap")
    User findUserById(Integer id);

    //根据用户名模糊查询用户
    @Select("select * from user where username like #{username}")
    @ResultMap("userMap")
    List<User> findUserByName(String name);
}

(三)注解实现复杂关系映射及延迟加载

  • @One 注解

    (多对一、一对一)

    代替了

    <assocation>
    

    标签,是多表查询的关键,在注解中用来指定子查询返回单一对象。

    • @One 注解属性介绍:
      • select:指定用来多表查询的 sql 映射
      • fetchType:代表加载方式,会覆盖全局的配置参数 lazyLoadingEnabled。取值有:FetchType.LAZY(延迟加载)、FetchType.EAGER(立即加载)、FetchType.DEFAULT(默认)
    • 使用格式:@Result(property="",column="",one=@One(select="",fetchType=))
  • @Many 注解

    (一对多)

    代替了

    <Collection>
    

    标签,是多表查询的关键,在注解中用来指定子查询返回对象集合。

    • @Many注解属性介绍:
      • select :指定用来多表查询的 sql 映射
      • fetchType :代表加载方式,会覆盖全局的配置参数 lazyLoadingEnabled。取值有:FetchType.LAZY(延迟加载)、FetchType.EAGER(立即加载)、FetchType.DEFAULT(默认)
    • 使用格式:@Result(property="",column="",many=@Many(select="",fetchType=))

注意:xml方式需要指定映射的 Java 实体类的属性 ofType或 javaType,但是注解中可以不定义。

1.注解实现一对一复杂关系映射及延迟加载

案例:一个账户对应一个用户
Account类:

import java.io.Serializable;

public class Account implements Serializable {

    private Integer id;
    private Integer uid;
    private Double money;

    //一对一(多对一)的映射:从表实体应该包含一个主表实体的对象引用
    private 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 Integer getUid() {
        return uid;
    }

    public void setUid(Integer uid) {
        this.uid = uid;
    }

    public Double getMoney() {
        return money;
    }

    public void setMoney(Double money) {
        this.money = money;
    }

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", uid=" + uid +
                ", money=" + money +
                '}';
    }
}

User类:

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

public class User implements Serializable {

    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String 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;
    }

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

IAccountDao:

import com.fox.pojo.Account;
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 IAccountDao {
    //查询所有账户,并且获取每个账户所属的用户信息
    @Select("select * from account")
    @Results(id = "accountMap",value = {
            @Result(id=true,property = "id",column = "id"),
            @Result(property = "uid",column = "uid"),
            @Result(property = "money",column = "money"),
            @Result(property = "user",column = "uid",one = @One(select = "com.fox.dao.IUserDao.findUserById",fetchType = FetchType.EAGER))
    })
    List<Account> findAll();
}

IUserDao:

import com.fox.pojo.User;
import org.apache.ibatis.annotations.*;

public interface IUserDao {
    //根据id查询用户
    @Select("select * from user where id=#{id}")
    User findUserById(Integer id);
}

2.注解实现一对多复杂关系映射及延迟加载

案例:一个用户对应多个账户
User类:

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

public class User implements Serializable {

    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;
	//一对多关系映射:一个用户对应多个账户
    private List<Account> accounts;

    public List<Account> getAccounts() {
        return accounts;
    }

    public void setAccounts(List<Account> accounts) {
        this.accounts = accounts;
    }
    
    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;
    }

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

Account类:

import java.io.Serializable;

public class Account implements Serializable {

    private Integer id;
    private Integer uid;
    private Double money;

    public Integer getId() {
        return id;
    }

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

    public Integer getUid() {
        return uid;
    }

    public void setUid(Integer uid) {
        this.uid = uid;
    }

    public Double getMoney() {
        return money;
    }

    public void setMoney(Double money) {
        this.money = money;
    }

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", uid=" + uid +
                ", money=" + money +
                '}';
    }
}

IUserDao:

import com.fox.pojo.User;
import org.apache.ibatis.annotations.*;
import org.apache.ibatis.mapping.FetchType;
import java.util.List;

public interface IUserDao {
    //查询所有用户
    @Select("select * from user")
    @Results(id = "userMap",value = {
            @Result(id = true,property = "id",column = "id"),
            @Result(property = "username",column = "username"),
            @Result(property = "birthday",column = "birthday"),
            @Result(property = "address",column = "address"),
            @Result(property = "sex",column = "sex"),
            @Result(property = "accounts",column = "id",many = @Many(select = "com.fox.dao.IAccountDao.findAccountByUid",fetchType = FetchType.LAZY))
    })
    List<User> findAll();
}

IAccountDao:

import com.fox.pojo.Account;
import org.apache.ibatis.annotations.Select;
import java.util.List;

public interface IAccountDao {
    //根据用户id查询账户信息
    @Select("select * from account where uid=#{uid}")
    List<Account> findAccountByUid(Integer uid);
}

(三)注解开发使用二级缓存

步骤

  1. 在 MyBatis核心配置文件中开启二级缓存支持:
<!-- 配置二级缓存 --> 
<settings>
	<!-- 开启二级缓存的支持 --> 
	<setting name="cacheEnabled" value="true"/>
</settings>
  1. 在持久层接口中使用注解配置二级缓存:
@CacheNamespace(blocking=true)//mybatis 基于注解方式实现配置二级缓存
public interface IUserDao {
    //查询所有用户
    @Select("select * from user")
    @Results(id = "userMap",value = {
            @Result(id = true,property = "id",column = "id"),
            @Result(property = "username",column = "username"),
            @Result(property = "birthday",column = "birthday"),
            @Result(property = "address",column = "address"),
            @Result(property = "sex",column = "sex"),
            @Result(property = "accounts",column = "id",many = @Many(select = "com.fox.dao.IAccountDao.findAccountByUid",fetchType = FetchType.LAZY))
    })
    List<User> findAll();

    //根据id查询用户
    @Select("select * from user where id=#{id}")
    @ResultMap("userMap")
    User findUserById(Integer id);
}
  1. 测试:
import com.fox.dao.IUserDao;
import com.fox.pojo.User;
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 UserTest {
    private InputStream in = null;
    private SqlSessionFactory factory =null;

    @Before //标记在非静态方法上。在@Test方法前面执行,而且是在每一个@Test方法前面都执行。
    public void init() throws IOException {
        //1.读取配置文件
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.使用构建者创建工厂对象 SqlSessionFactory
        factory = new SqlSessionFactoryBuilder().build(in);
    }

    @After //标记在非静态方法上。在@Test方法后面执行,而且是在每一个@Test方法后面都执行。
    public void destroy() throws IOException {
        in.close();
    }

    //查询所有
    @Test
    public void testFindAll(){
        SqlSession sqlSession1=factory.openSession();
        IUserDao userDao1 = sqlSession1.getMapper(IUserDao.class);
        User user1 = userDao1.findUserById(41);
        System.out.println(user1);
        sqlSession1.close();

        SqlSession sqlSession2=factory.openSession();
        IUserDao userDao2 = sqlSession2.getMapper(IUserDao.class);
        User user2 = userDao2.findUserById(41);
        System.out.println(user2);
        sqlSession2.close();

        System.out.println(user1==user2);
    }
}

在这里插入图片描述
我们发现sql语句只执行了一次,且一级缓存已经关闭,证明使用了二级缓存。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/417578.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Android入门到项目实战-- 5.1】—— 广播(一):接收系统广播

目录 一、什么是广播&#xff1f; 二、广播的类型 标准广播 有序广播 三、接收系统广播 1、动态注册监听网络变化 如何注册广播接收器&#xff1f; 2、静态注册实现开机启动 使用快捷方式创建广播接收器 实现开机广播 一、什么是广播&#xff1f; android广播机制就是…

全网最详细,Jmeter性能测试-性能进阶, 多协议实战Websocket/Dubbo(七)

目录&#xff1a;导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;前言 Jmeter是目前主流的…

自行车及电动自行车出口美国GCC认证要求

所有进口商和美国本土制造商都必须签发普通合格证书&#xff08;GCC&#xff09;证明其产品符合具体规定要求。进口商和本土制造商应向认可的第三方实验室提交样品测试其产品是否符合相关安全法规。提供第三方实验室测试报告&#xff0c;合格证书由进口商和本土制造商提供。在外…

pytorch通过不同的维度提高cifar10准确率

各个维度通过模型通过优化器通过batchsize通过数据增强总结当前网络的博客上都是普遍采用某个迁移学习训练cifar10&#xff0c;无论是vgg&#xff0c;resnet还是其他变种模型&#xff0c;最后通过实例代码&#xff0c;将cifar的acc达到95以上&#xff0c;本篇博客将采用不同的维…

九龙证券|300亿空袭,港股吓懵了!

港股再度大幅回调&#xff0c;腾讯成了“导火索”。 当地时刻4月11日&#xff0c;腾讯大股东Prosus发布公告称拟再度进行回购&#xff0c;作为回购方案的一部分&#xff0c;Prosus本周将采纳行动&#xff0c;把9600万股腾讯股票以凭据方式移入香港中心结算系统&#xff0c;以便…

八、市场活动-创建

需求分析 用户在市场活动主页面,点击"创建"按钮,弹出创建市场活动的模态窗口; 用户在创建市场活动的模态窗口填写表单,点击"保存"按钮,完成创建市场活动的功能. *所有者是动态的(//在现实市场活动主页面时&#xff0c;就从数据库中查询出所有用户并且…

基于ChatGLM-6b+Streamlit+QDrant+DuckDuckGo搭建本地问答机器人及缓解时效性问题方案

本地部署chatglm及缓解时效性问题的思路&#xff1a; 模型使用chatglm-6b 4bit&#xff0c;推理使用hugging face&#xff0c;前端应用使用streamlit或者gradio。 微调对显存要求较高&#xff0c;还没试验。可以结合LoRA进行微调。 缓解时效性问题&#xff1a;通过本地数据库…

word页码从指定页开始,具体设置步骤

word页码从指定页开始&#xff0c;具体设置步骤1、在文档页面&#xff0c;以目录后第一页为页码开始页为例&#xff0c;也就是正文第一页&#xff0c;首先将鼠标光标定位到目录页最后的位置2、在【布局】选项下点击【分隔符】选项。3、在分隔符中选择【分节符】下的【下一页】。…

深度学习-第T4周——猴痘病识别

深度学习-第T4周——猴痘病识别深度学习-第T4周——猴痘病识别一、前言二、我的环境三、前期工作1、导入数据集2、查看图片数目3、查看数据四、数据预处理1、 加载数据1、设置图片格式2、划分训练集3、划分验证集4、查看标签2、数据可视化3、检查数据4、配置数据集五、搭建CNN网…

Day943.持续集成流水线 -系统重构实战

持续集成流水线 Hi&#xff0c;我是阿昌&#xff0c;今天学习记录的是关于持续集成流水线的内容。 从团队协作的角度上来看&#xff0c;在版本发布过程中&#xff0c;经常出现测试依赖开发手工生成制品、版本发布也从开发本地出版本的问题。而且项目架构如果从单体演进至组件…

MySQL-双主高可用

目录 &#x1f341;拓扑环境 &#x1f341;配置两台MySQL主主同步 &#x1f343;修改MySQL配置文件 &#x1f343;配置主从关系 &#x1f343;测试主主同步 &#x1f341;keepalived高可用 &#x1f343;keepalived的安装配置 &#x1f343;master配置 &#x1f343;slave配置 …

R -- 用psych包做因子分析

因子分析 因子分析又称为EFA&#xff0c;是一系列用来发现一组变量的潜在结构的办法。它通过寻找一组更小的&#xff0c;潜在的结构来解释已观测到的显式的变量间的关系。这些虚拟的、无法观测的变量称为因子&#xff08;每个因子被认为可以解释多个观测变量间共有的方差&…

【回眸】ChatGPT Plus(GPT4体验卡)

前言 没忍住诱惑&#xff0c;开了个GPT4.0的会员&#xff0c;给大家表演一波 开通成功 开始问问题 写一个CNN疲劳驾驶监测代码&#xff0c;要求{使用Python语言&#xff0c;使用包&#xff0c;能成功运行&#xff0c;需要调用电脑摄像头&#xff0c;要求GUI界面有一些参数…

Python爬虫框架的介绍

爬虫框架的介绍Scrapy框架Crawley框架Portia框架Newspaper框架Python-goose框架随着网络爬虫的应用越来越多&#xff0c;一些爬虫框架逐渐涌现&#xff0c;这些框架将爬虫的一些常用功能和业务逻辑进行封装。这些框架的基础上&#xff0c;根据自己的需求添加少量的代码&#xf…

【操作系统 os学习笔记】

操作系统一、第一章&#xff1a;操作系统概述Ⅰ. 操作系统的基本概念1.操作系统的概念1.1 什么是操作系统&#xff1f;1.2 计算机系统的构成1.3 OS是一种系统软件1.4 OS作为系统软件&#xff0c;做了哪些事情2.操作系统的目标和功能2.1 目标2.2 功能3.操作系统的特征3.1 并发3.…

C#基础学习--接口

目录 什么是接口 ​编辑声明接口 实现接口 接口是引用类型 接口和as运算符 实现多个接口 实现具有重复成员的接口 多个接口的引用 派生成员作为实现 ​编辑显式接口成员实现 接口可以继承接口 什么是接口 接口是指定一组函数成员而不实现它们的引用类型 要实现一个接口…

DevOps实践分享:4个实施步骤与6个关键设计

本文介绍了普元DevOps平台在金融行业实施落地的常用方法&#xff0c;以及在项目管理&#xff0c;代码管理&#xff0c;构建管理&#xff0c;制品管理&#xff0c;部署管理等模块针对一些典型客户场景的关键设计。目 录01 平台简介‍‍02 实施方法‍‍‍‍‍‍03 关键设计01平…

项目管理的误区之追着别人做监工

有的项目经理拿到一个项目&#xff0c;上来就开始各种努力&#xff0c;恨不得把十八般武艺都套上去。 有的项目经理急于推进项目&#xff1a;“活都分出去了&#xff0c;我要监督大家把事情做好吧。 我先在心里设定一个目标&#xff0c;然后费尽心力地把大家往一处赶&#xff…

( “树” 之 DFS) 617. 合并二叉树 ——【Leetcode每日一题】

617. 合并二叉树 给你两棵二叉树&#xff1a; root1 和 root2 。 想象一下&#xff0c;当你将其中一棵覆盖到另一棵之上时&#xff0c;两棵树上的一些节点将会重叠&#xff08;而另一些不会&#xff09;。你需要将这两棵树合并成一棵新二叉树。合并的规则是&#xff1a;如果两…

css实现缺角功能、渐变、旋转、clip-path属性、矩形、边框、折角

文章目录1、缺角矩形2、缺角边框3、折角矩形4、clip-path属性1、缺角矩形 使用css3渐变实现缺角矩形&#xff0c;关于linear-gradient属性的介绍请移步至MDN。 ☺☺☺html部分☺☺☺ <div class"rectangle_box"><div class"left_top"></d…