第二章Mybatis进阶操作学习

news2024/11/17 5:41:55

文章目录

  • 特殊SQL的执行
      • 模糊查询
      • 批量操作
      • 动态设置表名
      • 添加功能获取自增的主键
  • 自定义映射resultMap
    • 处理字段和属性的映射关系
      • 在SQL语句中使用别名
      • 使用核心配置文件中的驼峰对应方法
      • 使用resultMap自定义映射处理
    • 一对一映射处理
      • 级联方式处理
      • association标签
      • 分步查询
    • 分步查询的优点:可以实现延迟加载
    • 一对多映射处理
      • collection
      • 分步查询
  • 动态SQL
    • if标签
      • 使用1=1解决and问题
      • 使用where标签解决问题
      • 利用trim解决问题
    • choose、when、otherwise
    • foreach
    • set标签
    • SQL片段
  • Mybatis缓存
    • 一级缓存
      • 使一级缓存失效的四种情况:
    • MyBatis的二级缓存
      • 二级缓存开启的条件
      • 二级缓存的相关配置
      • MyBatis缓存查询的顺序
    • 总结
  • Mybatis逆向工程
    • 创建逆向工程的步骤
      • 添加依赖和插件
      • 创建MyBatis的核心配置文件
      • 创建逆向工程的配置文件
      • 执行MBG插件的generate目标
      • 最后的效果
      • 功能测试
  • 分页插件
    • 分析分页需要哪些数据
    • 使用步骤
      • 添加依赖
      • 配置分页插件
      • 分页插件的使用

特殊SQL的执行

模糊查询

<mapper namespace="com.lsc.mybatis.Mapper.SpecialSQLMapper">
        <!--    List<User> getUserByLike(@Param("mohu") String mohu);-->
       <select id="getUserByLike" resultType="User">-->
          	select * from t_user where username like "%${mohu}%"-->
       </select>-->
       <select id="getUserByLike" resultType="User">-->
        	select * from t_user where username like "%"#{mohu}"%"-->
       </select>-->
     
       <select id="getUserByLike" resultType="User">
         	 select * from t_user where username like concat('%',#{mohu},'%')
       </select>
</mapper>
 /**
     * 通过用户名模糊查询用户信息
     * @param mohu
     * @return
     */
    List<User> getUserByLike(@Param("mohu") String mohu);
  • 最主要是对于我们这种%%和我们的参数进行拼接
    • 第一种直接使用${},不会自动加’’
    • 第二种"%" “%”
    • 第三种利用concat函数进行拼接

批量操作

<mapper namespace="com.lsc.mybatis.Mapper.SpecialSQLMapper">
            <delete id="deleteMoreUser">
                delete from t_user where id in (${ids})
            </delete>
</mapper>
  /**
     * 批量删除
     * @param ids
     */
    int  deleteMoreUser(@Param("ids") String ids);

动态设置表名

/**
     * 动态设置表名,查询用户信息
     * @param tableName
     * @return
     */
    List<User> getUserList(@Param("tableName") String tableName);
 <mapper namespace="com.lsc.mybatis.Mapper.SpecialSQLMapper">
 <!--    List<User> getUserList(@Param("tableName") String tableName);-->
            <select id="getUserList" resultType="User">
                select * from ${tableName}
            </select>
</mapper>

添加功能获取自增的主键

场景模拟:
t_clazz(clazz_id,clazz_name)
t_student(student_id,student_name,clazz_id)
1、添加班级信息
2、获取新添加的班级的id
3、为班级分配学生,即将某学的班级id修改为新添加的班级的id

  • 所以有时候对新增数据的主键是很有必要的
 /**
     * 添加用户信息并获取自增的主键
     * @param user
     */
    void insertUser(User user);
 <mapper namespace="com.lsc.mybatis.Mapper.SpecialSQLMapper">
 <!--    void insertUser(User user);-->
            <insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
                insert into t_user values(null,#{username},#{pwd},#{age},#{gender},#{email})
            </insert>
</mapper>
  • useGeneratedKeys:设置使用自增的主键
  • keyProperty:因为增删改有统一的返回值是受影响的行数,因此只能将获取的自增的主键放在传输的参数user对象的某个属性中
 @Test
    public void insertUserTest(){
        SqlSession sqlSession= SqlSessionUtil.getSqlSession();
        SpecialSQLMapper specialSQLMapper=sqlSession.getMapper(SpecialSQLMapper.class);
        User user=new User(null,"小寿司","123",1,"男","xss@163.com");
        specialSQLMapper.insertUser(user);
        System.out.println(user);
    }
    //输出结果
    //User{id=13, username='小寿司', pwd='123', age=1, gender='男', email='xss@163.com'}
  • 我们进行执行的User的id是null.但是执行insert后的User中有了对应的id属性的值

自定义映射resultMap

resultMap:设置自定义的映射关系

  • ​ id:唯一标识
  • ​ type:处理映射关系的实体类的类型
  • ​ 常用的标签:
    • id:处理主键和实体类中属性的映射关系
      • column:设置映射关系中的字段名,必须是sql查询出的某个字段
      • property:设置映射关系中的属性的属性名,必须是处理的实体类类型中的属性名
    • result:处理普通字段和实体类中属性的映射关系
      • column:设置映射关系中的字段名,必须是sql查询出的某个字段
      • property:设置映射关系中的属性的属性名,必须是处理的实体类类型中的属性名
    • association:处理一对一的映射关系(处理实体类类型的属性)
      • property:设置需要处理映射关系的属性的属性名
      • javaType:设置要处理的属性的类型
    • collection:处理一对多的映射关系(处理集合类型的属性)
      • property:设置需要处理映射关系的属性的属性名
      • ofType:设置collection标签所处理的集合属性中存储数据的类型
  • 这里的一对一和一对多前面的表示属性,后面表示记录

处理字段和属性的映射关系

  • 在前面的例子中,我们的属性名称和字段名称是一样的,所以不需要特意设置,但是也会有属性名和字段名不一样的情况,就需要我们主动去设置

image-20230110165232648

/**
     * 通过Id查询对应的Emp数据
     * @param id
     * @return
     */
    Emp getEmpById(@Param("id")Integer id);

在SQL语句中使用别名

<mapper namespace="com.lsc.mybatis.mappers.EmpMapper">
    <!--   Emp getEmpById(@Param("id")Integer id);-->
    <select id="getEmpById" resultType="Emp">
        select emp_id as empId, emp_name as empName,age,gender,dept_id as deptId from t_emp where emp_id = #{id}
    </select>
</mapper>
  • 为查询的字段设置别名,和属性名保持一致

使用核心配置文件中的驼峰对应方法

mybatis对应的核心配置文件的设置

  <settings>
        <!--将下划线映射为驼峰-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>

    </settings>

EmpMapper.xml的配置

<mapper namespace="com.lsc.mybatis.mappers.EmpMapper">
    <!--   Emp getEmpById(@Param("id")Integer id);-->
    <select id="getEmpById" resultType="Emp">
        select * from  t_emp where emp_id= #{id}
    </select>
</mapper>
  • 当我们开启了这个配置,不需要取别名也能成功获取数据到对应的属性上
  • 字段符合MySQL的要求使用_,而属性符合java的要求使用驼峰
    • 此时可以在MyBatis的核心配置文件中设置一个全局配置,可以自动将下划线映射为驼峰
      • emp_id->empId , emp_name->empName dept_id->deptId

使用resultMap自定义映射处理

<mapper namespace="com.lsc.mybatis.mappers.EmpMapper">
    <resultMap id="empResultMap" type="Emp">
        <id column="emp_id" property="empId"></id>
        <result column="emp_name" property="empName"></result>
        <result column="age" property="age"></result>
        <result column="gender" property="gender"></result>
        <result column="dept_id" property="deptId"></result>
    </resultMap>
    <!-- Emp getEmpById(@Param("id")Integer id);-->
    <select id="getEmpById" resultMap="empResultMap">
        select * from  t_emp where emp_id= #{id}
    </select>
</mapper>
  • <resultMap id="empResultMap" type="Emp"> id表示resultMap的id标志 type表示表示返回的实体类型,Emp是别名
  • <id column="emp_id" property="empId"></id> id表示处理主键和实体类中属性的映射关系
    • column表示表中的主键名称, property表示实体类的属性名,表示对应
  • <result column="emp_name" property="empName"></result> result表示处理普通字段和实体类中属性的映射关系
    • column表示表中的字段名称, property表示实体类的属性名,表示对应

一对一映射处理

image-20230110211424156

public interface EmpMapper {
    /**
     * 通过Id查询对应的Emp数据
     * @param id
     * @return
     */
    Emp getEmpById(@Param("id")Integer id);
}

级联方式处理

<mapper namespace="com.lsc.mybatis.mappers.EmpMapper">
    <resultMap id="empResultMap" type="Emp">
        <id column="emp_id" property="empId"></id>
        <result column="emp_name" property="empName"></result>
        <result column="age" property="age"></result>
        <result column="gender" property="gender"></result>
        <result column="dept_id" property="dept.deptId"></result>
        <result column="dept_name" property="dept.deptName"></result>
    </resultMap>
    <!--  Emp getEmpById(@Param("id")Integer id);-->
    <select id="getEmpById" resultMap="empResultMap">
        select emp.*,dept.* from t_emp as emp left join t_dept as dept on emp.emp_id=dept.dept_id where emp_id=#{id}
    </select>
</mapper>
  • 利用联表查询到两张表中的所有数据 ,然后利用级联方式进行对应数据
    • <result column="dept_id" property="dept.deptId"></result>
    • <result column="dept_name" property="dept.deptName"></result>

association标签

 <resultMap id="empResultMapTwo" type="Emp">
        <id column="emp_id" property="empId"></id>
        <result column="emp_name" property="empName"></result>
        <result column="age" property="age"></result>
        <result column="gender" property="gender"></result>
       <association  javaType="Dept" property="dept">
           <id column="dept_id" property="deptId"></id>
           <result column="dept_name" property="deptName"></result>
       </association>
    </resultMap>
        <!--    Emp getEmpById(@Param("id")Integer id);-->
        <select id="getEmpById" resultMap="empResultMapTwo">
            select emp.*,dept.* from t_emp as emp left join t_dept as dept on emp.emp_id=dept.dept_id where emp_id=#{id}
        </select>
  • assocation标签就是专门处理这种一个属性对应表中一列记录的情况
    • <association javaType="Dept" property="dept">
      • javaType表示转换成的Java对象的类型 Dept是别名
      • property表示对应的属性名称

分步查询

EmpMapper

public interface EmpMapper {
    /**
     * 通过Id分步查询出对应的Emp数据
     * @param empId
     * @return
     */
    Emp getEmpAndDeptByStepOne(@Param("empId") Integer empId);
}

DeptMapper

public interface DeptMapper {
    /**
     * 根据DeptId查询出对应Dept数据
     * @param DeptId
     * @return
     */
    Dept getDept(@Param("DeptId") Integer DeptId);
}

DeptMapper.xml

<mapper namespace="com.lsc.mybatis.mappers.DeptMapper">
    <!--    Dept getDept(@Param("id") Integer id);-->
    <select id="getDept" resultType="Dept">
        select * from t_dept where dept_id=#{id}
    </select>
</mapper>

EmpMapper.xml

<mapper namespace="com.lsc.mybatis.mappers.EmpMapper">

    <resultMap id="empResultMapThree" type="Emp">
        <id column="emp_id" property="empId"></id>
        <result column="emp_name" property="empName"></result>
        <result column="age" property="age"></result>
        <result column="gender" property="gender"></result>
        <association   property="dept" fetchType="lazy"
        select="com.lsc.mybatis.mappers.DeptMapper.getDept" column="dept_id">
        </association>
    </resultMap>
    <!--Emp getEmpAndDeptByStepOne(@Param("empId") Integer empId);-->
    <select id="getEmpAndDeptByStepOne" resultMap="empResultMapThree">
        select * from t_emp where emp_id = #{empId}
    </select>
</mapper>
  • property:设置需要处理映射关系的属性的属性名

  • select:设置分步查询的sql的唯一标识 也就是这个方法的全名成(包名+类名+方法名)

  • column:将查询出的某个字段作为分步查询的sql的条件

  • fetchType:在开启了延迟加载的环境中,通过该属性设置当前的分步查询是否使用延迟加载

    • fetchType=“eager(立即加载)|lazy(延迟加载)”

在核心配置文件中的设置

 <settings>
        <!--开启延迟加载-->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!--按需加载-->
        <setting name="aggressiveLazyLoading" value="false"/>
    </settings>

分步查询的优点:可以实现延迟加载

但是必须在核心配置文件中设置全局配置信息:

  • lazyLoadingEnabled:延迟加载的全局开关。当开启时,所有关联对象都会延迟加载
  • aggressiveLazyLoading:当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载
  • 两个属性这样设置,此时就可以实现按需加载,获取的数据是什么,就只会执行相应的sql

Mybatis仅支持association关联对象,和collection关联集合对象的延迟加载,association指的是一对一,collection指的是一对多(这里是指对象和表中记录的关系)

原理

  • 使用CGLIB创建目标对象的代理对象,当调用目标方法时候,进入拦截器方法,比如调用a.getB().getName()方法,拦截器的invoke方法发现a.getB()是null值,那么就会单独发送事先保存好的关联B对象的sql,把B查询上来,然后调用a.setB(b),,于是a的对象b属性有值了,接着完成a.getB().getName()方法的调用
  • 几乎所有延迟加载的原理都是这样

假设我们使用测试方法测试延迟加载,先调用一个查询,查询得出结果赋给对象a,里面包含未加载的null值项,假设这一项为类B的引用——b,并且假设类B拥有get方法getName()。当我们调用a.getB().getName()时,此时对象a中的成员对象b为null,无法调用其的getName()方法,此时Mybatis意识到要使用延迟加载来使此次调用不出Bug。

Mybatis使用CGLib生成目标对象a的代理对象,当我们在测试方法里调用a.getB()方法时,结果为null,不可行。于是,Mybatis调用拦截器方法,使用事先在resultMap中的association或collection标签里设定好的select查询语句来获取数据库中的数据并映射到对象b上,此时b不再为null,a.getB().getName()顺利调用,实现了按需加载、延迟加载,节省了宝贵的计算资源。

一对多映射处理

image-20230110211526982

/**
 * 获得部门信息和部门对应的所有人的信息
 * @param deptId
 * @return
 */
Dept getEmpAndDeptResultMap(@Param("deptId") Integer deptId);

collection

<mapper namespace="com.lsc.mybatis.mappers.DeptMapper">
<resultMap id="deptAndEmpResultMap" type="Dept">
        <id column="dept_id" property="deptId"></id>
        <result column="dept_name" property="deptName"></result>
        <!--
            ofType:设置集合类型的属性中存储的数据的类型
        -->
       <collection property="empList" ofType="Emp">
           <id property="empId" column="emp_id"></id>
           <result property="empName" column="emp_name"></result>
           <result property="gender" column="gender"></result>
           <result property="age" column="age"></result>
       </collection>
    </resultMap>
<!--    Dept getEmpAndDeptResultMap(@Param("deptId") Integer deptId);-->
     <select id="getEmpAndDeptResultMap" resultMap="deptAndEmpResultMap">
         select *
         from t_dept
         left join t_emp
         on t_dept.dept_id=t_emp.dept_id
         where t_dept.dept_id=#{deptId}
     </select>
</mapper>
  • connection处理一对多的这种映射关系,也就是说一个属性对应多条数据
    • ofType设置集合类型的属性中存储的数据的类型

分步查询

DeptMapper

 /**
     * 分步获得部门信息和部门对应的所有人的信息
     * @param deptId
     * @return
     */
    Dept getEmpAndDeptTwoStep(@Param("deptId") Integer deptId);

EmpMapper

  /**
     * 根据deptId来获取所有的Emp
     * @param deptId
     * @return
     */
     List<Emp> getDeptAndEmpByStepTwo(@Param("deptId") Integer deptId);
<mapper namespace="com.lsc.mybatis.mappers.DeptMapper">
<resultMap id="deptAndEmpResultMapByStep" type="Dept">
        <id column="dept_id" property="deptId"></id>
        <result column="dept_name" property="deptName"></result>
        <collection property="empList"
        select="com.lsc.mybatis.mappers.EmpMapper.getDeptAndEmpByStepTwo"
        column="dept_id"></collection>
    </resultMap>
<!--    Dept getEmpAndDeptTwoStep(@Param("deptId") Integer deptId);-->
    <select id="getEmpAndDeptTwoStep" resultMap="deptAndEmpResultMapByStep">
        select * from t_dept where dept_id=#{deptId}
    </select>
</mapper>

动态SQL

动态 sql 是Mybatis的强大特性之一,能够完成不同条件下不同的 sql 拼接。

  • 如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦

  • 它存在的意义是为了 解决 拼接SQL语句字符串时的痛点问题。

if标签

if标签可通过test属性的表达式进行判断,若表达式的结果为true,则标签中的内容会执行;反之标签中的内容不会执行

 <select id="getEmpByCondition" resultType="Emp">
        select * from t_emp where 
        <if test="empName!=null and empName !=''">
             emp_name=#{empName}
        </if>
        <if test="age != null and age != ''">
            and age=#{age}
        </if>
        <if test="gender != null and gender != ''">
            and gender=#{gender}
        </if>
    </select>
  • 如果empName为null,那么开头会多出来一个and
  • 如果都为null,那么会只出现一个where

使用1=1解决and问题

DynamicMapper

 /**
     * 根据条件查询员工信息
     * @param emp
     * @return
     */
    List<Emp> getEmpByCondition(Emp emp);
<mapper namespace="com.lsc.mybatis.mappers.DynamicMapper">
    <!--    List<Emp> getEmpByCondition(Emp emp);-->
    <select id="getEmpByCondition" resultType="Emp">
        select * from t_emp where 1=1
        <if test="empName!=null and empName !=''">
            and emp_name=#{empName}
        </if>
        <if test="age != null and age != ''">
            and age=#{age}
        </if>
        <if test="gender != null and gender != ''">
            and gender=#{gender}
        </if>
    </select>
</mapper>

1

使用where标签解决问题

<mapper namespace="com.lsc.mybatis.mappers.DynamicMapper">
<select id="getEmpByCondition" resultType="Emp">
        select * from t_emp
        <where>
            <if test="empName!=null and empName !=''">
                  emp_name=#{empName}
            </if>
            <if test="age != null and age != ''">
                  and age=#{age}
            </if>
            <if test="gender != null and gender != ''">
                  and gender=#{gender}
            </if>
        </where>
    </select>
</mapper>    
    

where和if一般结合使用:

  • 若where标签中的if条件都不满足,则where标签没有任何功能,即不会添加where关键字
  • 若where标签中的if条件满足,则where标签会自动添加where关键字,并将条件最前方多余的and去掉
  • 注意:where标签不能去掉条件最后多余的and

利用trim解决问题

trim用于去掉或添加标签中的内容
常用属性:

  • prefix:表示整个语句块,以prefix的值作为前缀
  • suffix:表示整个语句块,以suffix的值作为后缀
  • prefixOverrides:表示整个语句块要去除掉的前缀
  • suffixOverrides:表示整个语句块要去除掉的后缀

如果为内容为空串,那么都不会生效

<mapper namespace="com.lsc.mybatis.mappers.DynamicMapper">
<select id="getEmpByCondition" resultType="Emp">
        select * from t_emp
        <trim prefix="where" suffixOverrides="and">
            <if test="empName != null and empName != ''">
                emp_name = #{empName} and
            </if>
            <if test="age != null and age != ''">
                age = #{age} and
            </if>
            <if test="gender != null and gender != ''">
                gender = #{gender}
            </if>
        </trim>
    </select>
</mapper>
  • 在前面加一个 prefix=“where”

choose、when、otherwise

相当于java中的if…else if…else

DynamicMapper

 /**
     * 使用choose查询员工信息
     * @param emp
     * @return
     */
    List<Emp> getEmpByChoose(Emp emp);
<mapper namespace="com.lsc.mybatis.mappers.DynamicMapper">
<!--    List<Emp> getEmpByChoose(Emp emp);-->
    <select id="getEmpByChoose" resultType="Emp">
        select * from t_emp
        <where>
            <choose>
                <when test="empName != null and empName != ''">
                    emp_name=#{empName}
                </when>
                <when test="age !=null and age!=''">
                    age=#{age}
                </when>
                <when test="gender != null and gender !=''">
                    gender= #{gender}
                </when>
            </choose>
        </where>
    </select>
 </mapper>   

在这里插入图片描述

  • choose是when和otherwise的父标签
  • when只要满足了一个,后面的就不会执行,如果when都不满足,就执行ontherwise,很类似我们swtich case带break的结构

foreach

用于迭代的标签

/**
     * 批量添加员工信息
     * @param emps
     */
    void insertMoreEmp(@Param("emps") List<Emp> emps);

    /**
     * 批量删除
     * @param emplds
     */
    void deleteMoreEmp(@Param("emplds") Integer[] emplds);
<mapper namespace="com.lsc.mybatis.mappers.DynamicMapper">
<!--    void insertMoreEmp(@Param("emps") List<Emp> emps);-->
    <insert id="insertMoreEmp" >
        insert into t_emp values
        <foreach collection="emps" item="emp" separator=",">
            (null,#{emp.empName},#{emp.age},#{emp.gender},null)
        </foreach>
    </insert>
     <!--    void deleteMoreEmp(@Param("empIds") Integer[] empIds);-->
	<delete id="deleteMoreEmp">
        delete from t_emp where emp_id in
        <foreach collection="emplds" item="empId" separator="," open="(" close=")">
            #{empId}
        </foreach>
    </delete>
  <!--     一样的效果-->
   <!--    void deleteMoreEmp(@Param("empIds") Integer[] empIds);-->
    <delete id="deleteMoreEmp">
        delete from t_emp where
        <foreach collection="empIds" item="empId" separator="or">
            emp_id = #{empId}
        </foreach>
    </delete>
</mapper>
    

在这里插入图片描述

  • collection:设置要循环的数组或集合
  • item:用一个字符串表示数组或集合中的每一个数据
  • separator:设置每次循环的数据之间的分隔符
  • open:循环的所有内容以什么开始
  • close:循环的所有内容以什么结束

set标签

根据传入的用户对象属性来更新用户数据,可以使用标签来指定动态内容。

/**
     * 利用set标签更新
     * @param emp
     */
    void updateEmpBySet( Emp emp);
<mapper namespace="com.lsc.mybatis.mappers.DynamicMapper">
<!--    void updateEmpBySet(@Param("emp") Emp emp);-->
    <update id="updateEmpBySet">
        update t_emp
        <set>
            <if test="empName!=null and empName !=''">
                 emp_name=#{empName}
            </if>
            <if test="age != null and age != ''">
                  ,age=#{age}
            </if>
            <if test="gender != null and gender != ''">
                 ,gender=#{gender}
            </if>
        </set>
        where emp_id= #{empId}
    </update>
</mapper>

在这里插入图片描述

  • 跟where标签很相似

SQL片段

sql片段,可以记录一段公共sql片段,在使用的地方通过include标签进行引入

 <sql id="empColumns">
         emp_id,emp_name,age,gender,dept_id
</sql>
select <include refid="empColumns"></include> from t_emp

Mybatis缓存

  • 我们的浏览器也有缓存,当我们访问一些web资源,会把一些资源存储在本地,下次再次访问这些资源,就不需要从服务器获取,直接从本地获取
  • Mybatis查询的数据会被缓存,下次查询相同的数据,就会从缓存中直接获取,不会从数据库重新访问

CacheMapper接口

public interface CacheMapper {
    /**
     * 根据员工Id查询员工信息
     * @param empId
     * @return
     */
    Emp getEmpById(@Param("empId") Integer empId);

    /**
     * 添加员工信息
     * @param emp
     */
    void insertEmp(Emp emp);
}

Cachemapper.xml文件

<mapper namespace="com.lsc.mybatis.mappers.CacheMapper">
        <!--    Emp getEmpById(@Param("empId") Integer empId);-->
        <select id="getEmpById" resultType="Emp">
            select * from t_Emp where emp_id=#{empId}
        </select>
        <!--    void insertEmp(Emp emp);-->
        <insert id="insertEmp" >
            insert into t_emp values(null,#{empName},#{age},#{gender},null)
        </insert>
</mapper>

一级缓存

一级缓存是SqlSession级别的

 public void getEmpByIdTest() throws IOException {
        InputStream is= Resources.getResourceAsStream("mybatis_config.xml");
        SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(is);
        SqlSession sqlSession=sqlSessionFactory.openSession();
        CacheMapper cacheMapper=sqlSession.getMapper(CacheMapper.class);
        Emp emp1=cacheMapper.getEmpById(1);
        Emp emp2=cacheMapper.getEmpById(1);
        System.out.println(emp1);
        System.out.println(emp2);
//        SqlSession sqlSession1=sqlSessionFactory.openSession();

 }
DEBUG 01-11 23:01:39,518 ==>  Preparing: select * from t_Emp where emp_id=?  (BaseJdbcLogger.java:137) 
DEBUG 01-11 23:01:39,545 ==> Parameters: 1(Integer)  (BaseJdbcLogger.java:137) 
DEBUG 01-11 23:01:39,563 <==      Total: 1  (BaseJdbcLogger.java:137) 
Emp{empId=1, empName='lsc', gender='男', age=22, deptId=1}
Emp{empId=1, empName='lsc', gender='男', age=22, deptId=1}
  • 我们发现我们通过同一个sqlSession来执行了两次getEmpById,但是从结果中我们看出来的只从数据库中查询了一次
    • 说明了第二次查询是从缓存中获得数据

image-20230112030929050

  • 每个SqlSession中都持有Excutor,每个Excutor中有一个LocalCache。当用户发起询问时,MyBatis根据当前执行的语句生成MappedStatement,在Local Cache进行查询,如果缓存命中的话,直接返回结果给用户,如果缓存没有命中的话,查询数据库,结果写入Local Cache,最后返回结果给用户。
  • Mybatis一级缓存内部设计简单,只是一个没有容量限定的HashMap,在缓存的功能性上有所欠缺

使一级缓存失效的四种情况:

  • 不同的SqlSession对应不同的一级缓存
  • 同一个SqlSession但是查询条件不同
    • 缓存也是从数据库查出来的,如果缓存中都没有数据,那么肯定用不了缓存,需要从数据库查
  • 同一个SqlSession两次查询期间执行了任何一次增删改操作
    • 因为发生增删改,可能会导致缓存中的数据跟我们的数据库中不一样
  • 同一个SqlSession两次查询期间手动清空了缓存
   @Test
    public void getEmpByIdTest() throws IOException {
        InputStream is= Resources.getResourceAsStream("mybatis_config.xml");
        SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(is);

        SqlSession sqlSession1=sqlSessionFactory.openSession(true);
        CacheMapper cacheMapper1=sqlSession1.getMapper(CacheMapper.class);
        Emp emp1=cacheMapper1.getEmpById(1);

        SqlSession sqlSession2=sqlSessionFactory.openSession(true);
        CacheMapper cacheMapper2=sqlSession2.getMapper(CacheMapper.class);
        Emp emp2=cacheMapper2.getEmpById(1);

        System.out.println(emp1);
        System.out.println(emp2);
//        SqlSession sqlSession1=sqlSessionFactory.openSession();

}
//输出结果
DEBUG 01-11 23:13:45,519 ==>  Preparing: select * from t_Emp where emp_id=?  (BaseJdbcLogger.java:137) 
DEBUG 01-11 23:13:45,547 ==> Parameters: 1(Integer)  (BaseJdbcLogger.java:137) 
DEBUG 01-11 23:13:45,564 <==      Total: 1  (BaseJdbcLogger.java:137) 

DEBUG 01-11 23:13:45,600 ==>  Preparing: select * from t_Emp where emp_id=?  (BaseJdbcLogger.java:137) 
DEBUG 01-11 23:13:45,600 ==> Parameters: 1(Integer)  (BaseJdbcLogger.java:137) 
DEBUG 01-11 23:13:45,603 <==      Total: 1  (BaseJdbcLogger.java:137) 
    
Emp{empId=1, empName='lsc', gender='男', age=22, deptId=1}
Emp{empId=1, empName='lsc', gender='男', age=22, deptId=1}
  • 我们发现从不同的SqlSession中查询,一级缓存失效了
 @Test
    public void getEmpByIdTest() throws IOException {
        InputStream is= Resources.getResourceAsStream("mybatis_config.xml");
        SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(is);

        SqlSession sqlSession1=sqlSessionFactory.openSession(true);
        CacheMapper cacheMapper1=sqlSession1.getMapper(CacheMapper.class);
        Emp emp1=cacheMapper1.getEmpById(1);
        cacheMapper1.insertEmp(new Emp(null,"ABC",null,null,null));
        Emp emp2=cacheMapper1.getEmpById(1);
        System.out.println(emp1);
        System.out.println(emp2);
        sqlSession1.close();
        sqlSession2.close();
    }
DEBUG 01-11 23:20:30,168 ==>  Preparing: select * from t_Emp where emp_id=?  (BaseJdbcLogger.java:137) 
DEBUG 01-11 23:20:30,191 ==> Parameters: 1(Integer)  (BaseJdbcLogger.java:137) 
DEBUG 01-11 23:20:30,209 <==      Total: 1  (BaseJdbcLogger.java:137) 
    
DEBUG 01-11 23:20:30,211 ==>  Preparing: insert into t_emp values(null,?,?,?,null)  (BaseJdbcLogger.java:137) 
DEBUG 01-11 23:20:30,213 ==> Parameters: ABC(String), null, null  (BaseJdbcLogger.java:137) 
DEBUG 01-11 23:20:30,218 <==    Updates: 1  (BaseJdbcLogger.java:137) 
    
DEBUG 01-11 23:20:30,219 ==>  Preparing: select * from t_Emp where emp_id=?  (BaseJdbcLogger.java:137) 
DEBUG 01-11 23:20:30,219 ==> Parameters: 1(Integer)  (BaseJdbcLogger.java:137) 
DEBUG 01-11 23:20:30,221 <==      Total: 1  (BaseJdbcLogger.java:137) 
Emp{empId=1, empName='lsc', gender='男', age=22, deptId=1}
Emp{empId=1, empName='lsc', gender='男', age=22, deptId=1}
  • 我们在同一个sqlSession下,查询相同的数据,因为中间有一个插入操作,就导致了一级缓存失效

MyBatis的二级缓存

  • 二级缓存是SqlSessionFactory级别,通过同一个SqlSessionFactory创建的SqlSession查询的结果会被缓存;此后若再次执行相同的查询语句,结果就会从缓存中获取
  • 我们的一级缓存是默认开启的,但是我们的二级缓存需要我们手动配置

image-20230112031401956

二级缓存开启后,同一个namespace下的所有操作语句,都影响着同一个Cache,即二级缓存被多个SqlSession共享,是一个全局的变量。
当开启缓存后,数据的查询执行的流程就是 二级缓存 -> 一级缓存 -> 数据库。

二级缓存开启的条件

  • 在核心配置文件中,设置全局配置属性cacheEnabled=“true”,默认为true,不需要设置
  • 在映射文件中设置标签<cache/>
  • 二级缓存必须在SqlSession关闭或提交之后有效
  • 查询的数据所转换的实体类类型必须实现序列化的接口
  public void TestCacheTwo() throws IOException {
        InputStream is = Resources.getResourceAsStream("mybatis_config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);

        SqlSession sqlSession1 = sqlSessionFactory.openSession(true);
        CacheMapper mapper1 = sqlSession1.getMapper(CacheMapper.class);
        Emp emp1 = mapper1.getEmpById(1);
        System.out.println(emp1);
        sqlSession1.close();

        SqlSession sqlSession2 = sqlSessionFactory.openSession(true);
        CacheMapper mapper2 = sqlSession2.getMapper(CacheMapper.class);
        Emp emp2 = mapper2.getEmpById(1);
        System.out.println(emp2);
        sqlSession2.close();
    }
DEBUG 01-11 23:45:06,117 ==>  Preparing: select * from t_Emp where emp_id=?  (BaseJdbcLogger.java:137) 
DEBUG 01-11 23:45:06,143 ==> Parameters: 1(Integer)  (BaseJdbcLogger.java:137) 
DEBUG 01-11 23:45:06,161 <==      Total: 1  (BaseJdbcLogger.java:137) 
Emp{empId=1, empName='lsc', gender='男', age=22, deptId=1}

DEBUG 01-11 23:45:06,178 Cache Hit Ratio [com.lsc.mybatis.mappers.CacheMapper]: 0.5  (LoggingCache.java:60) 
Emp{empId=1, empName='lsc', gender='男', age=22, deptId=1}

二级缓存的相关配置

在mapper配置文件中添加的cache标签可以设置一些属性:
①eviction属性:缓存回收策略,默认的是 LRU。

  • LRU(Least Recently Used) – 最近最少使用的:移除最长时间不被使用的对象。
  • FIFO(First in First out) – 先进先出:按对象进入缓存的顺序来移除它们。
  • SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
  • WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。

②flushInterval属性:刷新间隔,单位毫秒

  • 默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新

③size属性:引用数目,正整数

  • 代表缓存最多可以存储多少个对象,太大容易导致内存溢出

④readOnly属性:只读, true/false

  • true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了 很重要的性能优势。

  • false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是false

MyBatis缓存查询的顺序

先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用。
如果二级缓存没有命中,再查询一级缓存
如果一级缓存也没有命中,则查询数据库
SqlSession关闭之后,一级缓存中的数据会写入二级缓存

总结

  1. MyBatis的二级缓存相对于一级缓存来说,实现了SqlSession之间缓存数据的共享,同时粒度更加的细,能够到namespace级别,通过Cache接口实现类不同的组合,对Cache的可控性也更强。
  2. MyBatis在多表查询时,极大可能会出现脏数据,有设计上的缺陷,安全使用二级缓存的条件比较苛刻。
  3. 在分布式环境下,由于默认的MyBatis Cache实现都是基于本地的,分布式环境下必然会出现读取到脏数据,需要使用集中式缓存将MyBatis的Cache接口实现,有一定的开发成本,直接使用Redis,Memcached等分布式缓存可能成本更低,安全性也更高。

Mybatis逆向工程

正向工程:先创建Java实体类,由框架负责根据实体类生成数据库表。 Hibernate是支持正向工程的。
逆向工程:先创建数据库表,由框架负责根据数据库表,反向生成如下资源:

  • Java实体类
  • Mapper接口
  • Mapper映射文件

说白了就是自动生成代码,主要生成一些CURD的代码

创建逆向工程的步骤

添加依赖和插件

  <!--    打包方式-->
    <packaging>jar</packaging>
    <dependencies>
        <!-- Mybatis核心 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.7</version>
        </dependency>
        <!-- junit测试 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <!-- MySQL驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.49</version>
        </dependency>
        <!-- log4j日志 -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
    </dependencies>
    <!-- 控制Maven在构建过程中相关配置 -->
    <build>
    <!-- 构建过程中用到的插件 -->
    <plugins>
        <!-- 具体插件,逆向工程的操作是以构建过程中插件形式出现的 -->
        <plugin>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-maven-plugin</artifactId>
            <version>1.3.0</version>
            <!-- 插件的依赖 -->
            <dependencies>
                <!-- 逆向工程的核心依赖 -->
                <dependency>
                    <groupId>org.mybatis.generator</groupId>
                    <artifactId>mybatis-generator-core</artifactId>
                    <version>1.3.2</version>
                </dependency>
                <!-- MySQL驱动 -->
                <dependency>
                    <groupId>mysql</groupId>
                    <artifactId>mysql-connector-java</artifactId>
                    <version>8.0.16</version>
                </dependency>
            </dependencies>
        </plugin>
    </plugins>
    </build>

创建MyBatis的核心配置文件

创建逆向工程的配置文件

文件名必须是:generatorConfig.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
    <!--
    targetRuntime: 执行生成的逆向工程的版本
    MyBatis3Simple: 生成基本的CRUD(清新简洁版)
    MyBatis3: 生成带条件的CRUD(奢华尊享版)
    -->
    <context id="DB2Tables" targetRuntime="MyBatis3">
        <!-- 数据库的连接信息 -->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/ssm"
                        userId="root"
                        password="123456">
        </jdbcConnection>
        <!-- javaBean的生成策略-->
        <javaModelGenerator targetPackage="com.lsc.mybatis.pojo"
                            targetProject=".\src\main\java">
            <property name="enableSubPackages" value="true" />
            <property name="trimStrings" value="true" />
        </javaModelGenerator>
        <!-- SQL映射文件的生成策略 -->
        <sqlMapGenerator targetPackage="com.lsc.mybatis.mapper"
                         targetProject=".\src\main\resources">
            <property name="enableSubPackages" value="true" />
        </sqlMapGenerator>
        <!-- Mapper接口的生成策略 -->
        <javaClientGenerator type="XMLMAPPER"
                             targetPackage="com.lsc.mybatis.mappers" targetProject=".\src\main\java">
            <property name="enableSubPackages" value="true" />
        </javaClientGenerator>
        <!-- 逆向分析的表 -->
        <!-- tableName设置为*号,可以对应所有表,此时不写domainObjectName -->
        <!-- domainObjectName属性指定生成出来的实体类的类名 -->
        <table tableName="t_emp" domainObjectName="Emp"/>
        <table tableName="t_dept" domainObjectName="Dept"/>
    </context>
</generatorConfiguration>
  • 配置数据库连接的信息
    • 驱动信息
    • URL
    • 用户名
    • 密码
  • 配置JavaBean的信息
    • 配置JavaBean生成下的目录
  • SQLXML文件配置的信息
  • Mapper接口的生成的配置信息
  • 配置表名和对应的类名的映射关系

执行MBG插件的generate目标

image-20230112002137441

最后的效果

image-20230112002255957

功能测试

public class EmpMapperTest {
    @Test
    public void mbgTest(){
        SqlSession sqlSession= SqlSessionUtil.getSqlSession();
        EmpMapper empMapper=sqlSession.getMapper(EmpMapper.class);
//        根据Id查询数据
        Emp emp=empMapper.selectByPrimaryKey(1);
        //select emp_id, emp_name, age, gender, dept_id from t_emp where emp_id = ?  
        System.out.println(emp);
//        查询所有数据
        List<Emp> empList = empMapper.selectByExample(null);
        empList.forEach(System.out::println);
        // select emp_id, emp_name, age, gender, dept_id from t_emp 
//        根据条件查询数据
        EmpExample example=new EmpExample();
        example.createCriteria().andEmpNameEqualTo("刘颂成").andAgeEqualTo(22);
        example.or().andGenderEqualTo("男");
        List<Emp> empList1 = empMapper.selectByExample(example);
        //select emp_id, emp_name, age, gender, dept_id from t_emp WHERE ( emp_name = ? and age = ? ) or( gender = ? ) 
        empList.forEach(System.out::println);
        
        Emp emp1 = new Emp(1,"小成",23,"男",null);
        //测试普通修改功能
        empMapper.updateByPrimaryKey(emp);
        //update t_emp set emp_name = ?, age = ?, gender = ?, dept_id = ? where emp_id = ?
        //测试选择性修改
        empMapper.updateByPrimaryKeySelective(emp);
        //update t_emp SET emp_name = ?, age = ?, gender = ? where emp_id = ?
    }
}

分页插件

分析分页需要哪些数据

当前在第一页

首页 上一页 2 3 4 5 6 下一页 末页

  • 假如上面是我们实际的需求

  • 我们要确定每页显示的条数,和当前页面的页码,这些数据是从前端传过来的

    • pageSize:每页显示的条数
    • pageNum:当前页的页码
  • 我们还需要知道总页面数,因为需要判断当前是不是最后一页,如果是最后一页,那么末页和下一页都不能使用

    • totalPage:总页数
    totalPage = count / pageSize;
    if(count % pageSize != 0){
    	totalPage += 1;
    }
    
  • 总页数=总记录数/pageSize

    • count:总记录数
  • 当前页的起始索引,因为我们要通过limit index,pageSize来查询当前页面需要的数据

    • index=(pageNum-1)*pageSize

例子
pageSize=4,pageNum=1,index=0 limit 0,4
pageSize=4,pageNum=3,index=8 limit 8,4
pageSize=4,pageNum=6,index=20 limit 8,4

使用步骤

添加依赖

<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.0</version>
</dependency>

配置分页插件

在MyBatis的核心配置文件中配置插件

<plugins>
<!--设置分页插件-->
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>

分页插件的使用

  @Test
    public void Test(){
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        //查询功能之前开启分页功能
        Page<Object> page= PageHelper.startPage(5,4);//第一个参数表第几页 第二个参数表示每页有几条数据
        List<Emp> empList = mapper.selectByExample(null);
        PageInfo<Emp> pageInfo = new PageInfo<>(empList, 5);//这个5表示当前页面应该显示的页面导航页的集合的大小
        empList.forEach(System.out::println);
        System.out.println(pageInfo);
    }
PageInfo{pageNum=5, pageSize=4, size=4, startRow=17, endRow=20, total=37, pages=10, 
         
list=Page{count=true, pageNum=5, pageSize=4, startRow=16, endRow=20, total=37, pages=10, reasonable=false, pageSizeZero=false}
         [	Emp{empId=17, empName='a', age=null, gender='null', deptId=null},
          	Emp{empId=18, empName='a', age=null, gender='null', deptId=null},
          	Emp{empId=19, empName='aa', age=null, gender='null', deptId=null}, 
          	Emp{empId=20, empName='a', age=null, gender='null', deptId=null}
         ], 
prePage=4, nextPage=6, isFirstPage=false, isLastPage=false, hasPreviousPage=true, hasNextPage=true, navigatePages=5, navigateFirstPage=3, navigateLastPage=7, navigatepageNums=[3, 4, 5, 6, 7]}

  • ​ pageInfo里有我们分页需要的信息
  • pageNum表示这是第几页 pageSize表示每页有几条数据 表示这一页的起始记录行数和结尾记录行数 startRow=17, endRow=20 total表示总记录行数,pages表示总共有多少页
  • 上一页的页数和下一页的页数 prePage=4, nextPage=6,
  • isFirstPage=false, isLastPage=false表示是不是最后一页 是不是第一页
  • hasPreviousPage=true, hasNextPage=true 是否有上一页 是否有下一页
  • 表示应该显示的页数 navigatepageNums=[3, 4, 5, 6, 7]

没有开启分页插件

DEBUG 01-12 02:12:19,579 ==>  Preparing: select emp_id, emp_name, age, gender, dept_id from t_emp  (BaseJdbcLogger.java:137) 
DEBUG 01-12 02:12:19,615 ==> Parameters:   (BaseJdbcLogger.java:137) 
DEBUG 01-12 02:12:19,639 <==      Total: 37  (BaseJdbcLogger.java:137) 

开启了分页插件

DEBUG 01-12 02:05:24,147 Cache Hit Ratio [SQL_CACHE]: 0.0  (LoggingCache.java:60) 
DEBUG 01-12 02:05:24,195 ==>  Preparing: SELECT count(0) FROM t_emp  (BaseJdbcLogger.java:137) 
DEBUG 01-12 02:05:24,219 ==> Parameters:   (BaseJdbcLogger.java:137) 
DEBUG 01-12 02:05:24,233 <==      Total: 1  (BaseJdbcLogger.java:137) 
DEBUG 01-12 02:05:24,235 ==>  Preparing: select emp_id, emp_name, age, gender, dept_id from t_emp LIMIT ?, ?  (BaseJdbcLogger.java:137) 
DEBUG 01-12 02:05:24,236 ==> Parameters: 16(Long), 4(Integer)  (BaseJdbcLogger.java:137) 
DEBUG 01-12 02:05:24,237 <==      Total: 4  (BaseJdbcLogger.java:137)
  • 我们发现开启了分页插件,我们的sql语句发生了变化
  • 分页插件的基本原理就是使用Mybatis提供的插件接口,实现了自定义插件,在插件的拦截方法内拦截主执行是SQL,然后重写SQL,添加对应的物理分页语句和物理分页参数

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

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

相关文章

【网安神器篇】——Sqlmap详解

作者名&#xff1a;白昼安全主页面链接&#xff1a;主页传送门创作初心&#xff1a;舞台再大&#xff0c;你不上台&#xff0c;永远是观众&#xff0c;没人会关心你努不努力&#xff0c;摔的痛不痛&#xff0c;他们只会看你最后站在什么位置&#xff0c;然后羡慕或鄙夷座右铭&a…

C语言 自定义类型 之 【联合体(共用体)】

文章目录前言联合体(UNION)类型的定义联合体的特点联合体大小的计算联合体使用的典型例题 — 判断机器的大小端写在最后前言 联合体&#xff08;union&#xff09;在C语言中是不常用的&#xff0c;不过存在就必然有其有用之处&#xff0c;这里不深入探讨联合体用在何处有什么用…

R语言实现牛顿插值

文章目录4 差商与牛顿插值差商牛顿插值4 差商与牛顿插值 差商 如果采取间隔不等的采样&#xff0c;差商会变得稍显复杂&#xff0c;对于x0,x1,…,xnx_0,x_1,\ldots,x_nx0​,x1​,…,xn​&#xff0c;若与y0,y1,…,yny_0,y_1,\ldots,y_ny0​,y1​,…,yn​通过映射fff一一对应&…

ASEMI整流桥KBP310电路设计注意事项

编辑-Z 型号&#xff1a;KBP310 最大重复峰值反向电压&#xff08;VRRM&#xff09;&#xff1a;1000V 最大平均正向整流输出电流&#xff08;IF&#xff09;&#xff1a;3.0A 峰值正向浪涌电流&#xff08;IFSM&#xff09;&#xff1a;60A 每个元件的典型热阻&#xff0…

MySQL LOAD VS DM8 dmfldr

MySQL LOAD VS DM8 dmfldr 背景 某业务系统从MySQL迁移至达梦后&#xff0c;有导入业务文件的功能使用MySQL的LOAD方式将csv文件导入到指定的表中。迁移到达梦后&#xff0c;该功能需要进行对应的调整&#xff08;因为达梦没有LOAD功能&#xff09;&#xff0c;但达梦提供了dm…

【自定义类型】-结构体,枚举,联合

&#x1f387;作者&#xff1a;小树苗渴望变成参天大树 &#x1f496;作者宣言&#xff1a;认真写好每一篇博客 &#x1f9e8;作者gitee:link 如 果 你 喜 欢 作 者 的 文 章 &#xff0c;就 给 作 者 点 点 关 注 吧&#xff01; &#x1f38a;自定义类型&#x1f389;前言&a…

osg fbo(二),将颜色缓冲区图片铺在全屏

其实&#xff0c;需要两个摄像机&#xff0c;一个是采样摄像机&#xff0c;一个是最终显示的摄像机。 osg fbo(一&#xff09;中&#xff0c;passRoot直接加的面片&#xff0c;现在通过最终显示摄像机来代替&#xff0c;passRoot加上这两个摄像机即可。 //passRoot->addCh…

jmeter编写压测脚本规范

一、压测时长 压测时长&#xff0c;一般为10分钟或者15分钟。 设置时长&#xff1a; 勾选 永远--持续时间&#xff08;秒&#xff09; 二、脚本编写规范 脚本越简单越好&#xff0c;多余的监听会影响jmeter的性能&#xff0c;继而影响到压测结果。 一个基础的脚本&#xf…

Web前端axios从入门

1 axios是什么 前端最流行的 ajax请求库&#xff0c; https://gitcode.net/mirrors/axios/axios?utm_sourcecsdn_github_accelerator 基于promise的异步ajax请求库浏览器端/node端都可以使用支持请求/响应拦截器支持请求取消请求/响应数据转换批量发送多个请求 2 json-serv…

论文解读OTA: Optimal Transport Assignment for Object Detection

CSDN优秀解读&#xff1a;https://blog.csdn.net/jiaoyangwm/article/details/126638775 2021 https://arxiv.org/pdf/2103.14259.pdf 关键解读 在目标检测中标签分配的最新进展主要寻求为每个GT对象独立定义正/负训练样本。在本文中&#xff0c;我们创新性地从全局的角度重…

SpringCloudAlibaba商城实战项目(day01)

前言 闲来无事在B站找了一个项目&#xff0c;是谷粒商城的项目&#xff0c;于是乎照着在敲这个项目&#xff0c;特此记录一下。会持续更新到这个项目敲完。这个记录偏向小白向&#xff0c;确保你照着敲也可以完成所有项目的搭建。 一、简介 1.1、项目架构图 1.2、服务列表 …

CAN Bus cable simulation

REF&#xff1a;CAN总线标准接口与布线规范 https://zhuanlan.zhihu.com/p/34333969高速CAN总线物理层对线束的要求 https://www.suncve.com/the-requirement-of-physical-layer-of-can-bus-for-wiring-harness/利用LTSPICE 做仿真&#xff0c; 选用的是 ADI的 CAN transceiver…

RabbitMQ快速入门之进阶

RabbitMQ快速入门之进阶 进阶RabbitMQ快速入门之进阶1、confirm 模式的设置2、return 退回模式的处理3、消费者 Ack&#xff0c;手动确认4、消费端限流 (流量削缝)5、TTL存活时间过期时间6、死信队列DLX7、延迟队列 &#xff08;TTL DLX&#xff09;1、confirm 模式的设置 *c…

VSCode使用Clangd

前言 在使用微软的C/C插件时&#xff0c;遇到较大项目时&#xff0c;代码提示速度非常的慢&#xff0c;这时可以使用clangd 1、系统安装clangd 版本选择&#xff1a;Linux github仓库: https://github.com/clangd/clangd/releases 解压下载好的安装包&#xff1a; unzip cla…

Python实现小米蓝牙温湿度计2 Home Assistant 自定义组件源码

小米 米家蓝牙温湿度计2 这是一个Home Assistant自定义组件&#xff0c;用于 Home Assistant 通过 蓝牙适配器 直接集成 小米 米家蓝牙温湿度计 (LYWSDCGQ/01ZM) 和 米家蓝牙温湿度计2 (LYWSD03MMC)。 v0.2.0-dev版本以后&#xff0c;已经支持自动发现功能&#xff0c;不需要…

Leetcode:501. 二叉搜索树中的众数(C++)

目录 问题描述&#xff1a; 实现代码与解析&#xff1a; 通用写法&#xff08;递归&#xff09;&#xff1a; 原理思路&#xff1a; 依据二叉搜索树特性写法&#xff08;递归&#xff09;&#xff1a; 原理思路&#xff1a; 迭代&#xff1a; 原理思路&#xff1a; 问题…

Android Compose——一个简单的新闻APP

Owl简述效果视频导航导航结点路线图底部导航栏使用标签页状态切换FeaturePage构建CoursePage实现搜索ViewModelView详情页DetailDescribeLesson尾Gitte简述 此Demo是参考Google Github其中一个Demo而完成&#xff0c;涉及的内容并不复杂&#xff0c;主要是为了熟悉Compose编码…

2022爱分析・出海数字化系列报告之“出海实时互动与通信”厂商全景报告 | 爱分析报告

报告编委 张扬 爱分析联合创始人&首席分析师 文鸿伟 爱分析高级分析师 王鹏 爱分析分析师 目录 研究范围定义厂商全景地图市场分析与厂商评估入选厂商列表研究范围定义 研究范围 改革开放四十多年来&#xff0c;中国企业经历了自商品出海到当前的品牌出海&#xff0c;出海…

Servlet的使用

作者&#xff1a;~小明学编程 文章专栏&#xff1a;JavaEE 格言&#xff1a;热爱编程的&#xff0c;终将被编程所厚爱。 目录 什么是Servlet&#xff1f; 创建一个Servlet程序 1.创建一个Maven项目 2.引入依赖 3.创建目录 4.编写代码 5.打包 6.部署程序 7.验证程序 …

Rust如何进行模块化开发?

类似es6的模块化&#xff0c;Rust通过package、create、module来实现代码的模块化管理 Rust如何进行模块化开发&#xff1f; Rust的代码组织包括&#xff1a;哪些细节可以暴露&#xff0c;哪些细节是私有的&#xff0c;作用域内哪些名称有效等等。 而这些功能被统称为模块系统…