MyBatis 用法详解

文章目录

  • 一、普通 SQL
    • 1.1 注解实现:
      • 1.1.1 参数传递:
      • 1.1.2 增(@Insert):
      • 1.1.3 删(@Delete):
      • 1.1.4 改(@Update):
      • 1.1.5 查(@Select):
        • 1.1.5.1 起别名:
        • 1.1.5.2 结果映射:
        • 1.1.5.3 开启驼峰命名(推荐):
    • 1.2 MyBatis XML 配置文件实现:
      • 1.2.1 添加 Mapper.xml
      • 1.2.2 增:
      • 1.2.3 删:
      • 1.2.4 改:
      • 1.2.5 查:
  • 二、#{} 和 ${} 的区别:
  • 三、动态 SQL:
    • 3.1 `<if> `标签:
    • 3.2 `<trim>`标签:
    • 3.3 `<where>`标签:
    • 3.4 `<set>`标签:
    • 3.5 `<foreach>`标签:
    • 3.6 `<include>`标签:

使用 MyBatis 需要导入依赖(MyBatis 和 MySQL(数据库都行)),和配置 yml 文件。导入对应的依赖比较简单,所以下面只给出 yml 文件的配置。

yml 文件配置如下。

下面用中文填写(除了注释)的都是要根据自己的情况来填写的。

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/对应数据库名称?characterEncoding=utf8&useSSL=false
    username: root
    password: 对应数据库的密码(如果是纯数字记得加上单引号)
    driver-class-name: com.mysql.cj.jdbc.Driver
  mvc:
    favicon:
      enable: false
  profiles:  #多平台配置
    active: dev
# 设置 Mybatis 的 xml 保存路径
mybatis:
  mapper-locations: classpath:mapper/*Mapper.xml
  configuration: # 配置打印 MyBatis 执行的 SQL
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    map-underscore-to-camel-case: true  #自动驼峰转换

MyBatis 有两种实现方式。(1)注解(2)xml 文件实现。

一、普通 SQL

1.1 注解实现:

注解实现,其实就是在对应的注解里面写上 SQL 语句即可。

1.1.1 参数传递:

需求:查找 id=4 的用户,对应的 SQL 就是:select * from userinfo where id=4。

@Select("select username, `password`, age, gender, phone from userinfo where id = 4")
UserInfo queryById();

但是这样的话,只能查找 id=4 的数据,所以 SQL 语句中的 id 值不能写成固定数值,需要变为动态的数值。

解决方法:在 queryById 方法中添加一个参数(id),将方法中的参数,传给 SQL 语句。

使用#{}的方式获取方法中的参数。注意:#{}里面的名称需要和方法参数名称一致(如果方法参数是一个对象,那么#{}里面的值,要和对象的属性名称一样。)

@Select("select username, `password`, age, gender, phone from userinfo where id= #{id} ")
UserInfo queryById(Integer id);

如果 mapper 接口方法形参只有一个普通类型的参数,#{…}里面的属性名可以随便写,如:#{id}、# {value}。建议和参数名保持一致。

添加测试用例:

@Test
void queryById() {
 UserInfo userInfo = userInfoMapper.queryById(4);
 System.out.println(userInfo);
}

运行结果:

image-20241007150941216

也可以通过 @Param ,设置参数的别名,如果使用 @Param 设置别名,#{…}里面的属性名必须和 @Param 设置的一样。

@Select("select username, `password`, age, gender, phone from userinfo where id = #{userid} ")
UserInfo queryById(@Param("userid") Integer id);

如果参数是对象,设置了 @Param 属性,#{…} 需要使用 别名.属性 来获取。

//插入数据
@Insert("insert into userinfo(username,`password`,age,gender,phone) " +
        "values(#{gobeyye.username},#{gobeyye.password}" +
        ",#{gobeyye.age},#{gobeyye.gender},#{gobeyye.phone})")
Integer insertUserInfo1(@Param("gobeyye") UserInfo userInfo);

1.1.2 增(@Insert):

SQL 语句:

insert into userinfo (username, `password`, age, gender, phone) values 
("zhaoliu","zhaoliu",19,1,"18700001234")

把 SQL 中的常量替换为动态的参数:

Mapper 接口的方法:

@Insert("insert into userinfo (username, `password`, age, gender, phone) values (#{username},#{password},#{age},#{gender},#{phone})")
Integer insert(UserInfo userInfo);

直接使用 UserInfo 对象的属性名来获取参数。

测试代码:

 @Test
    void insertUesrInfo1() {
        UserInfo userInfo = new UserInfo();
        userInfo.setUsername("zhaoliu");
        userInfo.setPassword("zhaoliu");
        userInfo.setAge(19);
        userInfo.setGender(1);
        userInfo.setPhone("18700001234");
        userInfoMapper.insertUesrInfo1(userInfo);
    }

运行结果:下面这个运行结果,需要导入日志配置。

image-20241007153531754

返回主键:

如果想要拿到自增 id(主键一般默认是 id),需要在 Mapper 接口的方法上添加一个 Options 的注解。

@Options(useGeneratedKeys = true, keyProperty = "id")
@Insert("insert into userinfo (username, age, gender, phone) values (#{userinfo.username},#{userinfo.age},#{userinfo.gender},#{userinfo.phone})")
Integer insert(@Param("userinfo") UserInfo userInfo);
  • useGeneratedKeys:这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系型数据库管理系统的自动递增字段),默认值:false。

  • keyProperty:指定能够唯一识别对象的属性,MyBatis 会使用 getGeneratedKeys 的返回值或 insert 语句的 selectKey 子元素,设置它的值,默认值:未设置(unset)。

注意:设置 useGeneratedKeys = true 之后,方法返回值依然是受影响的行数,自增 id 会设置在上述 keyProperty 指定的属性中。

1.1.3 删(@Delete):

SQL 语句:

问号是占位符的意思,方便下面写代码,不是 SQL 语法支持的。

delete from userinfo where id = ?

Mapper 接口的方法:

@Delete("delete from userinfo where id = #{id}")
void delete(Integer id);

1.1.4 改(@Update):

SQL 语句:

update userinfo set username="zhaoliu" where id=5

Mapper 接口的方法:

@Update("update userinfo set username=#{username} where id=#{id}")
void update(UserInfo userInfo);

1.1.5 查(@Select):

SQL 语句:

@Select("select id, username, `password`, age, gender, phone, delete_flag, 
create_time, update_time from userinfo")

Mapper 接口的方法:

@Select("select id, username, `password`, age, gender, phone, delete_flag, create_time, update_time from userinfo")
List<UserInfo> queryAllUser();

查询结果:

image-20241007160052250

从运行结果上可以看到,我们 SQL 语句中,查询了 delete_flag,create_time,update_time 但是这几个属性却没有赋值。

原因分析:

当自动映射查询结果时,MyBatis 会获取结果中返回的列名并在 Java 类中查找相同名字的属性(忽略大小写)。这意味着如果发现了 ID 列和 id 属性,MyBatis 会将列 ID 的值赋给 id 属性。

观察下图:

image-20241007160512525

我们可以发现,这是因为字段名和属性名称不一致导致的(命名规则不同导致的)。

解决方法:

  1. 起别名

  2. 结果映射

  3. 开启驼峰命名

1.1.5.1 起别名:

在 SQL 语句中,给列名起别名,保持别名和实体类属性名一样。

@Select("select id, username, `password`, age, gender, phone," +
        " delete_flag as deleteFlag, create_time as createTime, update_time as updateTime from userinfo")
public List<UserInfo> queryAllUser();
1.1.5.2 结果映射:
@Select("select id, username, `password`, age, gender, phone, delete_flag, create_time, update_time from userinfo")
@Results({
        @Result(column = "delete_flag", property = "deleteFlag"),
        @Result(column = "create_time", property = "createTime"),
        @Result(column = "update_time", property = "updateTime")
})
List<UserInfo> queryAllUser2();

如果其他 SQL,也希望可以复用这个映射关系,可以给这个 Results 定义一个名称。

@Select("select id, username, `password`, age, gender, phone, delete_flag, create_time, update_time from userinfo")
@Results(id="resultMap",value = {
    @Result(column = "delete_flag", property = "deleteFlag"),
    @Result(column = "create_time", property = "createTime"),
    @Result(column = "update_time", property = "updateTime")
})
List<UserInfo> queryAllUser2();

@Select("select id, username, `password`, age, gender, phone, delete_flag, create_time, update_time " +
        "from userinfo where id= #{userid} ")
@ResultMap(value = "resultMap")
UserInfo queryById(@Param("userid") Integer id);

使用 id 属性给该 Results 定义别名,使用 @ResultMap 注解来复用 Results。

1.1.5.3 开启驼峰命名(推荐):

通常数据库列使用蛇形命名法进行命名(下划线分割各个单词),而 Java 属性一般遵循驼峰命名法约定。为了在这两种命名方式之间启用自动映射,需要将 mapUnderscoreToCamelCase 设置为 true(设置 application.properties 或者 application.yml 文件)。

mybatis:
  configuration:
#    日志
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
#    自动驼峰转换
    map-underscore-to-camel-case: true

这时 Java 代码不做任何处理。字段就能全部正确赋值。

1.2 MyBatis XML 配置文件实现:

1.2.1 添加 Mapper.xml

首先根据 yml 文件中配置的 mapper 路径创建对应的 xml 文件。

image-20241007165055159

创建完成后,向里面填入 MyBatis 的固定 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.example.demo.mapper.UserInfoXMlMapper">
 <select id="queryAllUser" resultType="com.example.demo.model.UserInfo">
 select username,`password`, age, gender, phone from userinfo
 </select>
</mapper>

以下是对以上标签的说明:

  • <mapper>标签:需要指定 namespace 属性,标识命名空间,值为 mapper 接口的全限定名,包括全包名.类名。
  • <select>查询标签:是用来执行数据库的查询操作的。select 中的属性解释如下:
  1. id :是和 Interface(接口)中定义的方法名称一样的,表示对接口的具体实现方法。
  2. resultType:返回的数据类型。
image-20241007170155891

其实 xml 增删改查和注解实现增删改查,其实差不多,无非就是一个使用注解,另一个使用标签。

1.2.2 增:

UserInfoMapper 接口:

Integer deleteUser(Integer id);

UserInfoMapper.xml 实现:

<insert id="insertUser">
 insert into userinfo (username, `password`, age, gender, phone) values (#{username}, #{password}, #{age},#{gender},#{phone})
</insert>

测试代码:

@Test
void insertUser() {
    UserInfo userInfo = new UserInfo();
    userInfo.setUsername("gobeyye");
    userInfo.setPassword("123456");
    userInfo.setAge(18);
    userInfo.setGender(1);
    userInfo.setPhone("12345678");
    userInfoXMLMapper.insertUser(userInfo);
}

效果如下:

image-20241008094127079

  • 返回自增 id:

接口定义不变,Mapper.xml 实现设置 useGeneratedKeys 和 keyProperty 属性。代码如下:

<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
    insert into userinfo (username, `password`, age, gender, phone)
    values (#{username}, #{password}, #{age},#{gender},#{phone})
</insert>

1.2.3 删:

UserInfoMapper 接口:

Integer deleteUser(Integer id);

UserInfoMapper.xml 实现:

<delete id="deleteUser">
    delete from userinfo where id = #{id}
</delete>

1.2.4 改:

UserInfoMapper 接口:

Integer updateUser(UserInfo userInfo);

UserInfoMapper.xml 实现:

<update id="updateUser">
 update userinfo set username=#{username} where id=#{id}
</update>

1.2.5 查:

UserInfoMapper 接口:

List<UserInfo> queryAllUser();

UserInfoMapper.xml 实现:

<select id="queryAllUser" resultType="com.example.demo.model.UserInfo">
 select id, username,`password`, age, gender, phone, delete_flag, create_time, update_time from userinfo
</select>

运行结果:

image-20241008095014817

结果显示:deleteFlag,createTime,updateTime 也没有进行赋值。

解决办法和注解类似:

  1. 起别名

  2. 结果映射

  3. 开启驼峰命名

其中 1 和 3 方法和注解一样,下面重点讲如何使用 xml 写结果映射。

Mapper.xml:

<resultMap id="BaseMap" type="com.gobeyye.mybatis.moder.UserInfo">
    <id column="id" property="id"></id>
    <result column="delete_flag" property="deleteFlag"></result>
    <result column="create_time" property="createTime"></result>
    <result column="update_time" property="updateTime"></result>
</resultMap>

<select id="queryAllUser" resultMap="BaseMap">
    select id, username,`password`, age, gender, phone, delete_flag, create_time, update_time from userinfo
</select>
image-20241008100239710

多表查询和单表查询类似,只是 SQL 不同而已,不再多说。

二、#{} 和 ${} 的区别:

MyBatis 参数赋值有两种方式,文章前面使用了 #{} 进行赋值,接下来我们看下二者的区别。

#{} 和 ${} 的区别就是预编译SQL即时SQL的区别。

了解 SQL 语句的执行流程,有助于理解下面的区别。

SQL 语句的执行流程:

  1. SQL 语法校验和解析

  2. SQL 优化

  3. SQL 执行

  4. #{} 性能更高:

绝大多数情况下,某一条 SQL 语句可能会被反复调用执行,或者每次执行的时候只有个别的值不同(比如 select 的 where 子句值不同,update 的 set 子句值不同,insert 的 values 值不同)。如果每次都需要经过上面的语法解析,SQL 优化、SQL 编译等,则效率就明显不行了。

image-20241008101914520

预编译 SQL,编译一次之后会将编译后的 SQL 语句缓存起来,后面再次执行这条语句时,不会再次编译 (只是输入的参数不同),省去了解析优化等过程,以此来提高效率。

  1. #{} 更安全(防止 SQL 注入,最重要的一点):

SQL 注入:是通过操作输入的数据来修改事先定义好的 SQL 语句,以达到执行代码对服务器进行攻击的方法。

#{} 内部的值,只会被识别成参数,而 ${} 内部的值,既可以识别成参数也可以被识别成 SQL 语句。

  1. 特殊场景只能使用 ${}:

在排序功能中,我们可以选择升序或者降序。

Mapper 实现:

@Select("select id, username, age, gender, phone, delete_flag, create_time, update_time " +
        "from userinfo order by id ${sort} ")
List<UserInfo> queryAllUserBySort(String sort);

部分结果如下:

image-20241008103313736

如果将 ${} 换成 #{},就会出现如下结果,并报错:

image-20241008103424177

这是因为 #{} 会根据参数类型判断,是否拼接引号''。如果参数类型为 String,就会加上引号。

当使用 #{sort} 查询时,asc 前后自动给加了引号,导致 sql 错误。

除此之外,还有表名作为参数时,也只能使用 ${},但是一定要注意 SQL 注入问题。

综上,能使用 #{} 的场景就使用 #{},实在不行再使用 ${},但是一定要注意 SQL 注入问题。

三、动态 SQL:

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

可以参考官方文档:https://mybatis.net.cn/dynamic-sql.html

由于动态 SQL 会复杂一点,所以一般将动态 SQL 写在 xml 文件中(使用注解也行)。下面就都使用 xml 文件的形式,注解就不再演示。

3.1 <if> 标签:

接口定义:

Integer insertUserByCondition(UserInfo userInfo);

Mapper.xml 实现:

<insert id="insertUserByCondition">
    INSERT INTO userinfo (
    username, `password`, age,
    <if test="gender != null">
        gender,
    </if>
    phone)
    VALUES (#{username},#{password}, #{age},
    <if test="gender != null">
        #{gender},
    </if>
    #{phone})
</insert>

测试代码如下:

@Test
void insertUserByCondition() {
    UserInfo userInfo = new UserInfo();
    userInfo.setUsername("gobeyye");
    userInfo.setPassword("123123");
    userInfo.setAge(18);
    userInfo.setPhone("999999");
    userInfoXMLMapper.insertUserByCondition(userInfo);
}

效果如下:可以看到 gender 由于属性没有值,所以该字段被去除了。

image-20241008105642300

这里有的友友可能会有疑问:把全部字段都加上,不进行赋值的字段,参数传为 null 不就行了?

答:不行,因为 MySQL 中,有的字段如果没有赋值,会有默认值(可以自己设定),如果传值为 null,就会把默认值覆盖掉,这是我们不希望看到的,所以只能使用 if 标签。

3.2 <trim>标签:

之前的插入用户功能,只是有一个 gender 字段可能是选填项,如果有多个字段,一般考虑使用标签结合标签,对多个字段都采取动态生成的方式。

<trim>标签中有如下属性:

  • prefix:表示整个语句块,以 prefix 的值作为前缀。

  • suffix:表示整个语句块,以 suffix 的值作为后缀。

  • prefixOverrides:表示整个语句块要去除掉的前缀。

  • suffixOverrides:表示整个语句块要去除掉的后缀。

调整 Mapper.xml 的插入语句为:

<insert id="insertUserByCondition">
    insert into userinfo
    <trim prefix="(" suffix=")" suffixOverrides=",">
        <if test="username !=null">
            username,
        </if>
        <if test="password !=null">
            `password`,
        </if>
        <if test="age != null">
            age,
        </if>
        <if test="gender != null">
            gender,
        </if>
        <if test="phone != null">
            phone,
        </if>
    </trim>
    values
    <trim prefix="(" suffix=")" suffixOverrides=",">
        <if test="username !=null">
            #{username},
        </if>
        <if test="password !=null">
            #{password},
        </if>
        <if test="age != null">
            #{age},
        </if>
        <if test="gender != null">
            #{gender},
        </if>
        <if test="phone != null">
            #{phone}
        </if>
    </trim>
</insert>

在上面的这段代码中,就可以使用 <trim>标签起到解决,多余后缀,的问题。

3.3 <where>标签:

where 标签解决的是,如果所有条件都加上了 if 标签,且都没有进行传值,这时 where 就会多出来,这时 SQL 语法就出错了,使用 where 标签就可以很好的解决上面的问题。

<where>只会在子元素有内容的情况下才插入 where 字句,而且会自动去除字句开头的 AND 或 OR。

<select id="queryByCondition" resultType="com.example.demo.model.UserInfo">
    select id, username, age, gender, phone, delete_flag, create_time,
    update_time
    from userinfo
    <where>
        <if test="age != null">
            and age = #{age}
        </if>
        <if test="gender != null">
            and gender = #{gender}
        </if>
        <if test="deleteFlag != null">
            and delete_flag = #{deleteFlag}
        </if>
    </where>
</select>

3.4 <set>标签:

<set> :动态的在 SQL 语句中插入 set 关键字,并会删掉额外的逗号。(用于 update 语句中)。

Mapper.xml:

<update id="updateUserByCondition">
    update userinfo
    <set>
        <if test="username != null">
            username = #{username},
        </if>
        <if test="age != null">
            age = #{age},
        </if>
        <if test="deleteFlag != null">
            delete_flag = #{deleteFlag},
        </if>
    </set>
    where id = #{id}
</update>

3.5 <foreach>标签:

对集合进行遍历时可以使用该标签。标签有如下属性:

  • collection:绑定方法参数中的集合,如 List,Set,Map 或数组对象。

  • item:遍历时的每一个对象。

  • open:语句块开头的字符串。

  • close:语句块结束的字符串。

  • separator:每次遍历之间间隔的字符串。

使用演示:

接口方法:

void deleteByIds(List<Integer> ids);

Mapper.xml:

<delete id="deleteByIds">
    delete from userinfo
    where id in
    <foreach collection="ids" item="id" separator="," open="(" close=")">
        #{id}
    </foreach>
</delete>

3.6 <include>标签:

在 xml 映射文件中配置的 SQL,有时可能会存在很多重复的片段,此时就会存在很多冗余的代码。

image-20241008121124855

我们可以对重复的代码片段进行抽取,将其通过标签封装到一个 SQL 片段,然后再通过标签进行引用。

  • <sql>:定义可重用的 SQL 片段。
  • <include>:通过属性 refid,指定包含的 SQL 片段。

例如 SQL片段:

<sql id="allColumn">
 id, username, age, gender, phone, delete_flag, create_time, update_time
</sql>

通过 include 标签进行引用。操作如下:

<select id="queryById" resultType="com.example.demo.model.UserInfo">
    select
    <include refid="allColumn"></include>
    from userinfo where id= #{id}
</select>

结语:
其实写博客不仅仅是为了教大家,同时这也有利于我巩固知识点,和做一个学习的总结,由于作者水平有限,对文章有任何问题还请指出,非常感谢。如果大家有所收获的话,还请不要吝啬你们的点赞收藏和关注,这可以激励我写出更加优秀的文章。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

OpenCV高级图形用户界面(15)注册一个回调函数来处理鼠标事件的函数setMouseCallback()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 为指定的窗口设置鼠标处理器。 setMouseCallback 是 OpenCV 中的一个功能&#xff0c;允许开发者注册一个回调函数来处理鼠标事件。当用户在窗口…

自监督学习:引领机器学习的新革命

引言 自监督学习&#xff08;Self-Supervised Learning&#xff09;近年来在机器学习领域取得了显著进展&#xff0c;成为人工智能研究的热门话题。不同于传统的监督学习和无监督学习&#xff0c;自监督学习通过利用未标注数据生成标签&#xff0c;从而大幅降低对人工标注数据…

champ模型部署指南

一、介绍 champ是由阿里巴巴、复旦大学和南京大学的研究人员共同提出的一种基于3D的将人物图片转换为视频动画的模型&#xff0c;该方法结合了3D参数化模型(特别是SMPL模型)和潜在扩散模型&#xff0c;能够精确地捕捉和再现人体的3D形状和动态&#xff0c;同时保持动画的时间一…

用SVM做时间序列预测真绝!最新思路无敌了,卷不动的进来看!

时间序列预测算法如今也算是百花齐放了&#xff0c;不过最近大家都在卷爆火的Transformer-based&#xff0c;卷不动的盆友其实也可以考虑从传统方法下手找创新&#xff0c;比如用SVM做时间序列预测。 SVM是一种经典的机器学习算法&#xff0c;在处理非线性及高维模式识别方面很…

k8s中的微服务

一、什么是微服务 用控制器来完成集群的工作负载&#xff0c;那么应用如何暴漏出去&#xff1f;需要通过微服务暴漏出去后才能被访问 Service是一组提供相同服务的Pod对外开放的接口。 借助Service&#xff0c;应用可以实现服务发现和负载均衡。 service默认只支持4层负载均…

黑马程序员-redis项目实践笔记2

目录 三、 Redis实现全局唯一ID 实现优惠卷秒杀下单 超卖问题 一人一单&#xff08;单例项目线程安全问题&#xff09; 一人一单&#xff08;集群环境下的并发问题&#xff09; 分布式锁的基本原理和实现方式对比 Redis分布式锁 实现核心思路 实现代码 Redis分布式锁…

供应商管理是什么?

你是一家制造业的老板&#xff0c;在需求旺盛的时段&#xff0c;却找不到一家合适的供应商&#xff0c;出现供应商突然退出合作&#xff1b;杀熟&#xff0c;供应的产品质量不过关等情况&#xff0c;企业的利益空间瞬间被压榨&#xff0c;急需一套管理系统来帮助选择供应商&…

《七度荒域:混沌之树》风灵月影二十二项游戏辅助:上帝模式/无限HP和EP/金币不减

《七度荒域:混沌之树》是款国产Roguelike银河恶魔城横版动作游戏&#xff0c;融合刷宝玩法。玩家将扮演修补世界的命运之子&#xff0c;探寻碎裂世界的秘密&#xff0c;在战斗轮回中成长&#xff0c;挑战未知与隐秘力量。风灵月影版修改器提供更多自定义和游戏体验调整选项&…

关于MyBatis的一些面试题

mybatis的执行流程 MyBatis 的执行流程主要包括 SQL 解析、参数绑定、执行查询/更新、结果映射等几个步骤。下面详细解释每个步骤的执行流程&#xff1a; 1. 加载配置文件和映射文件 加载 MyBatis 配置文件&#xff1a;启动时&#xff0c;MyBatis 通过 SqlSessionFactoryBui…

Transformer图解以及相关的概念

前言 transformer是目前NLP甚至是整个深度学习领域不能不提到的框架&#xff0c;同时大部分LLM也是使用其进行训练生成模型&#xff0c;所以transformer几乎是目前每一个机器人开发者或者人工智能开发者不能越过的一个框架。接下来本文将从顶层往下去一步步掀开transformer的面…

2018年计算机网络408真题解析

第一题&#xff1a; 解析&#xff1a;TCP/IP体系结构应用层常用协议及其相应的运输层协议 TCP协议是面向连接可靠数据传输服务&#xff0c;UDP无连接不可靠的数据传输服务&#xff0c;IP无连接不可靠的数据连接服务。 FTP协议&#xff0c;SMTP协议和HTTP协议使用TCP协议提供的面…

SaaS架构:中央库存系统架构设计

大家好&#xff0c;我是汤师爷~ 近年来&#xff0c;越来越多的零售企业大力发展全渠道业务。在销售额增长上&#xff0c;通过线上的小程序、直播、平台渠道等方式&#xff0c;拓展流量变现渠道。在会员增长方面&#xff0c;通过多样的互动方式&#xff0c;全渠道触达消费者&am…

Power BI - 设置Waterfall Chart第一个Pillar的颜色

1.简单介绍 有的用户可能会单独设置Column Chart&#xff08;条形图&#xff09;的第一个柱子的颜色&#xff0c;如下图所示&#xff0c; 这种其实可以通过Column Chart的Conditional formating进行设置&#xff0c; - SWICH SELECTEDVALUE 或者也可以直接对单独的Column进行…

深入拆解TomcatJetty(一)

深入拆解Tomcat&Jetty&#xff08;一&#xff09; 专栏地址&#xff1a;https://time.geekbang.org/column/intro/100027701 1、Web容器是什么 早期的 Web 应用主要用于浏览新闻等静态页面&#xff0c;HTTP 服务器&#xff08;比如 Apache、Nginx&#xff09;向浏览器返…

Google play开发者账号被封,申诉就有机会,别不信

在谷歌上架&#xff0c;开发者账号被封对很多开发者来说已经是家常便饭了&#xff0c;虽说一直都有在流传申诉没有用。别灰心啊&#xff0c;申诉就有机会&#xff0c;不少开发者都申诉成功了。 尤其是用一个少一个、价值好几个w的老号&#xff0c;不申诉就认栽实在是太亏了&…

【黑马点评优化】之使用Caffeine+Redis实现应用级二层缓存

【黑马点评优化】之使用CaffeineRedis实现应用级二层缓存 1 缓存雪崩定义及解决方案2 为什么要使用多级缓存3 RedisCaffeine实现应用层二级缓存原理4 利用CaffeineRedis解决Redis突然宕机导致的缓存雪崩问题4.1 pom.xml文件引入相关依赖4.2 本地缓存配置类4.3 修改ShopServiceI…

大有期货携手云轴科技ZStack 获“鼎新杯”数字化转型典型案例二等奖

近日&#xff0c;由中国通信标准化协会主办、中国信息通信研究院&#xff08;简称“中国信通院”&#xff09;承办、中国通信企业协会支持的“2024数字化转型发展大会”在北京召开。本届大会以“拥抱数智化无限可能”为主题&#xff0c;会上公布了第三届“鼎新杯”数字化转型应…

Centos 7.9NFS搭建

原创作者&#xff1a;运维工程师 谢晋 Centos 7.9NFS搭建 NFS服务端安装客户机访问共享配置 NFS服务端安装 SSH连接系统登录到服务端安装nfs服务 # yum -y install nfs-utils2. 安装完成后&#xff0c;查看需要共享的目录&#xff0c;这边共享的是/home目录&#xff0c;如…

Selenium - 用这个力量做任何你想做的事情

Chrome DevTools 简介 Chrome DevTools 是一组直接内置在基于 Chromium 的浏览器&#xff08;如 Chrome、Opera 和 Microsoft Edge&#xff09;中的工具&#xff0c;用于帮助开发人员调试和研究网站。 借助 Chrome DevTools&#xff0c;开发人员可以更深入地访问网站&#xf…

苹果正式宣布:iPhone全面开放近场通信(Near Field Communication,简称NFC)【使用安全元件提供app内NFC数据交换功能】

文章目录 引言I iPhone的NFC功能开发者用户数据交换的体验革新安全与隐私II 知识扩展:近场通信(NFC)技术钱包NFC开关打开读取NFC标签(NFC tags )权限demo引言 2014年iPhone 6开始,苹果首次引入了NFC功能,但最初只允许自家的Apple Pay进行移动支付。慢慢地适配了交通卡,增…