MyBatis使用教程详解<下>

回顾上一篇博文,我们讲了如何使用注解/XML的方式来操作数据库,实际上,一个Mapper接口的实现,这两种方式是可以并存的.

上一篇博文中,我们演示的都是比较简单的SQL语句,没有设计到复杂的逻辑,本篇博文会讲解复杂SQL的实现及一些细节处理.话不多说,让我们开始吧.

一. #{}和${}

1.1 ${}的使用

不论使用注解还是XML,我们通常使用#{}向SQL语句中传递参数.实际上,${}也起到了传递参数的效果.

先来传递一个Integer类型的参数.

这次我们使用的是user表和User实体类.

在UserMapper中定义一个updateAge方法. 

//使用${}传递参数 
@Update("update user set age=${age} where id=${id}")
 int updateAge(Integer age,Integer id);

针对该方法生成对应的测试方法.

    @Test
    void updateAge() {
        int result=userMapper.updateAge(28,21);
        log.info("更新条目: "+result);
    }

根据日志打印结果,可以发现更新成功了

但是我们这次的重点不是更新结果,而是生成的SQL语句.

同学们先回想一下使用#{}传递参数时的SQL语句...

接着我们使用${}来传递String类型的参数. 

在UserMapper接口中定义updateName方法.

   @Update("update user set name=${name} where id=${id}")
    int updateName(String name,Integer id);

 然后生成对应的测试方法.

    @Test
    void updateName() {
        int result=userMapper.updateName("马小跳",22);
        log.info("更新条目: "+result);
    }

运行测试方法,我们会发现这次代码抛出了异常.

如果还没有发现抛出异常的原因,不妨把打印的SQL语句复制到MySQL命令行上看看.

看到熟悉的界面,不知道童鞋们有没有想起来SQL语句中的字符串需要加上''或者"" 

所以我们可以手动给注解中的SQL语句加上''

    @Update("update user set name='${name}' where id=${id}")
    int updateName(String name,Integer id);

再次运行测试方法,可以正常进行更新.

1.2 #和$的区别

看到这个小标题,如果同学们觉得不过就是加不加引号的区别,那就太小看它们两个了.

#{}使用的是预编译的方式进行数据库操作,${}使用的是即时编译.

预编译会使用占位符?提前给要传入的参数占位,即时编译则是直接将传递的参数插入到SQL语句中. 

来看下二者SQL语句的打印,屏幕前的你或许就不那么懵逼了.

下面详细列举一下预编译和即时编译的区别: 

1. 预编译的性能更高.

一个SQL语句从编译到执行需要一下三步.

预编译SQL,编译一次之后会将编译后的SQL语句缓存起来,如果后面再次执行了该语句,不会再次编译(只是进行了传参操作),省去了前面的两个步骤.

而即时编译每执行一条SQL语句,都要进行上述的全部流程.

2. 预编译更加安全,可以防止SQL注入.

如果想根据查询指定姓名的user,在UserMapper中定义selectByName方法.

吸取上文的经验,如果使用${}传参,我们需要加上''

    @Select("select * from user where name='${name}'")
    List<User> selectByName(String name);

编写对应的测试方法并输入下面这条数据.

    @Test
    void selectByAge() {
        List<User> list=userMapper.selectByName("' or 1='1");
        log.info("根据name查询user:{}",list);
    }

观察打印的日志,我们会发现查询出来的是user表中的全部数据.

来看一下SQL语句.

这就相当于你提供了一个接口,客户输入姓名即可获取到自己的个人信息,然而hacker可以通过SQL注入的方式获取到全部客户的信息...


肯定有童鞋要问了,#{}的方式难道不是给字符串类型的参数加上''吗,这样不也有SQL注入的风险吗?

我们来试验一下.

    @Select("select * from user where name=#{name}")
    List<User> selectByName2(String name);

来看一下查询结果

实际上,通过#{}传递参数并不仅仅是判断参数类型,然后添上引号这样简单.{}中的参数只在占位符的位置起效,而不会作为SQL的部分语句执行.

举个栗子~就好像你买了一瓶可乐,然后去禁止自带饮料的餐厅就餐,手边的可乐是不能喝的.

1.3 $的使用场景

你们肯定会疑惑,既然$有这么多的缺点,以后的所有场景全部无脑使用#不就万事大吉了?

#{}也有解决不了的情况.

1.3.1 排序

来实现一个方法,根据用户指定的排序方式对查询结果按照Id排序并返回.

我们先来使用#{}进行传参.

    @Select("select * from user order by id #{order}")
    List<User> selectByIdOrder(String order);

在测试类中生成对应的测试方法.

    @Test
    void selectByIdOrder() {
        List<User> userList=userMapper.selectByIdOrder("desc");
        System.out.println(userList);
    }

我们会发现抛出了异常,是因为#{}偷偷加了引号. 

这种情况下就不得不使用${}的方式传入参数了.

    @Select("select * from user order by id ${order}")
    List<User> selectByIdOrder(String order);

 这次我们如愿拿到了排序后的结果.

但是在上文中提到,使用${}未免会有SQL注入的风险,如果不得不使用${},我们该如何规避呢?

这种情况下,我们就可以直接把代码写死,比如编写两个方法,根据用户输入的参数决定调用哪种方法进行查询.

1.3.2 模糊查询

 来回顾一下模糊查询的SQL语句.

%表示零个或多个字符,_表示一个字符.

这种情况下,如果我们使用#{}进行模糊查询 

    @Select("select * from user where name like '%#{name}%'")
    List<User> selectLikeName(String name);
    @Test
    void selectLikeName() {
        List<User> list=userMapper.selectLikeName("玉");
        System.out.println(list);
    }

这时候代码会抛出一个异常~

 如果换成${}+单引号的方式.

    @Select("select * from user where name like '%${name}%'")
    List<User> selectLikeName(String name);

 就可以拿到相应结果.

这种情况下,我们又该怎么防止SQL注入呢?实际上,SQL提供了一种连接字符串的方法.

使用concat函数连接字符串

于是乎,我们就可以在模糊查询中也使用#{}进行传参了.

    @Select("select * from user where name like concat('%',#{name},'%')")
    List<User> selectLikeName(String name);

 同样拿到了正确的结果.

二. 动态SQL的实现

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

2.1 什么是动态SQL

比如我们要填写下面这张基本信息表,其中昵称是必填项.

可以看到, 客户可能会选择不同的信息填写.如果程序猿要针对每个<input>标签都判断一下是否有填写内容的话,未免太过麻烦.在这种情况下该怎么插入客户的信息呢?

动态SQL就是根据传入参数的不同,编译不同的SQL语句.

MyBatis提供了几个标签帮助我们实现动态SQL.

因为使用XML的方式实现动态SQL比较简单,所以下面主要使用XML的方式进行展示,如何使用注解进行动态SQL将会在本文末尾稍加演示.

2.2 <if>标签

这次我们准备的是Article表及其对应的实体类.

如果用户没有输入title或者content等字段的信息,我们便不进行插入.

那么该如何使用动态SQL来实现呢?

 来看代码,首先定义一个ArticleMapper接口.

@Mapper
public interface ArticleMapper {
    int insertArticle(Article article);
}

接着创建一个对应的XML文件(配置文件中要设置好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.ArticleMapper">

    <!--对应insertArticle方法-->
    <insert id="insertArticle">
        insert into article(title,content,author_id,create_time,update_time)
                    values(#{title},#{content},#{authorId},#{createTime},#{updateTime});
    </insert>

</mapper>

一般情况下,我们应该写的SQL语句是下面这样式的.

但是如果用户没有输入对应的字段值,我们需要修改这个SQL语句.该如何动态删除其中的某些字段呢?

可以借助<if>标签这样实现insertArticle方法.

    <insert id="insertArticle">
        insert into article(
        <if test="title!=null">title,</if>
        <if test="content!=null">content,</if>
        <if test="authorId!=null">author_id,</if>
        <if test="createTime!=null">create_time,</if>
        <if test="updateTime!=null">update_time</if>)
        values(
        <if test="title!=null">#{title},</if>
        <if test="content!=null">#{content},</if>
        <if test="authorId!=null">#{authorId},</if>
        <if test="createTime!=null">#{createTime},</if>
        <if test="updateTime!=null">#{updateTime}</if>)
    </insert>

 <if>标签内的test相当于一个判断语句,其中判断的title,content等变量是java对象的属性名,如果返回的是false,<if>标签内的内容将不会出现在SQL语句中.

话不多说,演示一下就能明白了.按照之前的套路,需要先创建一个测试类,然后对insertArticle方法进行测试.

@SpringBootTest
@Slf4j
class ArticleMapperTest {

    @Autowired
    private ArticleMapper mapper;
    @Test
    void insertArticle() {
        Article article=new Article();
        article.setTitle("黑猫爱上了白猫");
        article.setContent("从前有一只黑猫...");
        article.setCreateTime(new Date());
        article.setUpdateTime(new Date());
        int result= mapper.insertArticle(article);
        log.info("插入条目: "+result);
    }
}

从打印结果上看到,我们确实插入成功了.

注意看预编译的SQL语句, 因为我们并没有对article的authorId等成员变量进行设置,所以动态生成的SQL语句中也没有出现这些字段.


但是这段代码有个Bug,童鞋们可以仔细思考一下,如果我没有设置插入文章的updateTime值会产生什么后果?

不妨来试试.

 @Test
    void insertArticle() {
        Article article=new Article();
        article.setTitle("白猫不喜欢黑猫");
        article.setContent("从前有一只白猫...");
        article.setCreateTime(new Date());

        int result= mapper.insertArticle(article);
        log.info("插入条目: "+result);
    }

可以看到,程序抛出了异常,因为()中多了两个','

肯定有聪明的小朋友会说,如果将','放在字段前面是不是就不会产生这种情况了?(见下图)

如果这样编写SQL语句,当用户没有输入title字段时,同样会产生多余的','

那么该如何去掉这个顽固的','呢?既然一个标签不够,我们就使用两个标签来解决~

2.3 <trim>标签

先来讲解一下<trim>标签的用法----<trim>标签有四个属性:

  1. prefix: 表示前缀,如果trim标签内有内容,编译的SQL语句就会自动加上这个前缀
  2. suffix: 表示后缀,如果trim标签内有内容,编译的SQL语句就会自动加上这个后缀
  3. prefixOverrides: 如果整个语句块的前缀为该属性的值,则将这个前缀去除
  4. suffixOverrides: 如果整个语句块的后缀为该属性的值,则将这个后缀去除

 结合代码,更好理解~

现在我们重新写一下insertArticle方法.

<insert id="insertArticle">
        insert into article
        <trim prefix="("  suffix=")"  suffixOverrides=",">
            <if test="title!=null">title</if>
            <if test="content!=null">content,</if>
            <if test="authorId!=null">author_id,</if>
            <if test="createTime!=null">create_time,</if>
            <if test="updateTime!=null">update_time,</if>
        </trim>
        values
        <trim prefix="(" suffix=")" suffixOverrides="," >
            <if test="title!=null">#{title},</if>
            <if test="content!=null">#{content},</if>
            <if test="authorId!=null">#{authorId},</if>
            <if test="createTime!=null">#{createTime},</if>
            <if test="updateTime!=null">#{updateTime}</if>
        </trim>
    </insert>

再来运行一下刚才的测试方法.

    @Test
    void insertArticle() {
        Article article=new Article();
        article.setTitle("白猫不喜欢黑猫");
        article.setContent("从前有一只白猫...");
        article.setCreateTime(new Date());
        int result= mapper.insertArticle(article);
        log.info("插入条目: "+result);
    }

可以看到,这次即使没有给updateTime变量赋值,我们也成功插入了.

下面来仔细对比一下xml文件和动态生成的SQL语句.

可以发现,即使我们没有设置updateTime的变量值,但是设置了createTime的值,createTime字段后面的 ',' 却被删除了,这就是suffixOverrides的功劳.

同样,每个trim语句块的前后都有(),这是因为我们设置了suffix和prefix.

那么,如果trim语句块中没有内容会怎样呢?我们来测试一下.

可以从打印的SQL语句中看到,trim标签中的prefix和suffix并没有添加.

2.4 <where>标签

 <where>标签有如下特性:

  1. 如果<where>标签中没有内容,where也不会在SQL语句中存在
  2. 如果<where>标签中有内容,会自动在该语句块的最前面加上where
  3. <where>内语句块会自动去掉最前面的and

现在在ArticleMapper中编写selectArticle方法.

 List<Article> selectArticles(Article article);

重点来了~来看看where标签是怎么使用的吧!

先来看看如果不使用动态SQL该怎么编写.(为了简便,这里的查询只设置了三个条件)

    <select id="selectArticles" resultType="com.example.demo.entity.Article">
        select * from article where title=#{title} and content=#{content} and id=#{id}
    </select>

现在我们根据原始的SQL语句编写带有<where>标签的动态SQL.

    <select id="selectArticles" resultType="com.example.demo.entity.Article">
        select * from article
        <where>
            <if test="title!=null">title=#{title}</if>
            <if test="content!=null">and content=#{content}</if>
            <if test="id!=null"> and id=#{id}</if>
        </where>
    </select>

生成对应的测试方法并运行

   @Test
    void selectArticles() {
        Article article=new Article();
        article.setContent("昨日11.27");
        List<Article> articleList=mapper.selectArticles(article);
        log.info("查询结果,ArticleList:{}",articleList);
    }

来对比一下xml中方法的实现和实际运行时编译的SQL语句.

 可以看到,where语句块自动删除了最前面的and.

同学们想一想,如果我们输入的参数中所有字段均为Null,是不是就相当于查询article表的全部内容?

来实践一下~

 可以看到,当<where>标签中没有任何内容时,where也不会出现在SQL语句中.


实际上,根据where标签的特性,我们可以使用<trim>标签来代替<where>标签,一起来看看是怎么实现的吧.

测试方法请读者自行编写.

2.5 <set>标签

 <set>标签用在uodate语句中,具有以下特性:

  1. 当<set>标签中没有内容时,set也不会存在于SQL语句中
  2. 如果<set>标签中有内容,会自动在该语句块的前面加上set
  3. set后面的语句块会自动去掉末尾的','

结合代码更好地理解一下~

现在ArticleMapper中定义一个updateArticle方法

int updateArticleById(Article article);

 在xml文件中实现这个方法,下面是不使用标签时的update语句.

如果换成<set>标签进行动态SQL的编写...

    <update id="updateArticleById">
        update article 
        <set>
            <if test="title!=null">title=#{title},</if>
            <if test="content!=null">content=#{content},</if>
            <if test="authorId!=null">author_id=#{authorId}</if>
        </set>
        where id=#{id}
    </update>

生成对应的测试方法并运行.

    @Test
    void updateArticleById() {
        Article article=new Article();
        article.setContent("今天开始努力敲代码");
        article.setId(15);
        log.info("更新条目: "+mapper.updateArticleById(article));
    }

对比一下生成的SQL语句和xml文件中方法的实现.

 可以看到,set标签自动删除了语句块中末尾的","

如果语句块为空,<set>标签也不会存在,我们来试验一下.

    @Test
    void updateArticleById() {
        Article article=new Article();
//        article.setContent("今天开始努力敲代码");
        article.setId(15);
        log.info("更新条目: "+mapper.updateArticleById(article));
    }

从打印的语句中看到,set也不存在,但实际上,这种情况是不可能发生的,如果我们要更新某个字段,就必然输入该字段更新后的值.

2.6 <foreach>标签

<foreach>标签通常用于批量操作,我们可以使用这个标签遍历java中的某个数据集合,进行指定操作.

下面列举该标签的四个属性:

  1. collection: 该属性的值为传入的参数(类型可以是List,Set,Map或者数组等)
  2. item: 给传入集合的每个元素进行统一命名,命名的值是item的值(相当于Java语法中的for-each,要给每个从集合中取出的元素命名才能拿到该对象)
  3. open: 标签内语句块要添加的前缀
  4. close: 标签内语句块要添加的后缀
  5. separator: 各个元素之间要添加的分隔符 

下面使用一个SQL语句来说明一下:

target: 查询id为13,14,15,16的article

可以看到,上图中的查询语句中,我们传入的数据集合是13,14,15,16,每个元素之间用","分割,in后面的语句块中,需要在数据集合前后加上().

下面来演示一下批量删除操作,先在ArticleMapper中定义一个方法

int deleteArticle(List<Integer> ids);

在xml文件中写具体实现,下面先来写如果没有使用<foreach>标签...我们需要在in后面的()中填入要删除的所有id

 下面是使用<foreach>的动态SQL语句.

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

生成对应的测试方法并进行测试.

    @Test
    void deleteArticle() {
        List<Integer> ids=new ArrayList<>();
        ids.add(14);
        ids.add(16);
        ids.add(18);
        int result= mapper.deleteArticle(ids);
        log.info("删除条目: "+result);
    }

来看一下动态生成的SQL语句.

我们不妨猜一下,如果传入的集合中没有值,<foreach>会不会存在?

结果是抛出了异常,因为我们的SQL语句有误.当然<foreach>整个语句块都会消失.不过此处只是好奇,这种操作在现实生活中当然也不会存在!

2.7 <include>标签

回顾一下我们已经编写的动态SQL语句,同学们不难发现有很多代码是十分冗余的,比如下面这个insertArticle方法

    <insert id="insertArticle">
        insert into article
        <trim prefix="("  suffix=")"  suffixOverrides=",">
            <if test="title!=null">title,</if>
            <if test="content!=null">content,</if>
            <if test="authorId!=null">author_id,</if>
            <if test="createTime!=null">create_time,</if>
            <if test="updateTime!=null">update_time,</if>
        </trim>
        values
        <trim prefix="(" suffix=")" suffixOverrides="," >
            <if test="title!=null">#{title},</if>
            <if test="content!=null">#{content},</if>
            <if test="authorId!=null">#{authorId},</if>
            <if test="createTime!=null">#{createTime},</if>
            <if test="updateTime!=null">#{updateTime}</if>
        </trim>
    </insert>
    

于是乎,我们就可以使用<include>标签实现代码的复用.

<sql>: 定义可重用的代码片段,里面的内容可以是语句,也可以是字段.

<include>: 通过属性refid,指定要包含的sql片段

下面我们就来修改一下这个insertArticle方法.可以看到,重复的语句是<trim>语句块,于是就可以把它扔到<sql>语句块中,并指定一个id

    <sql id="insert">
        <trim prefix="("  suffix=")"  suffixOverrides=",">
            <if test="title!=null">title,</if>
            <if test="content!=null">content,</if>
            <if test="authorId!=null">author_id,</if>
            <if test="createTime!=null">create_time,</if>
            <if test="updateTime!=null">update_time,</if>
        </trim>
    </sql>

接下来直接在需要使用该代码片段的地方添加一个<include>标签即可.

    <insert id="insertArticle">
        insert into article
            <include refid="insert"></include>
        values
            <include refid="insert"></include>
    </insert>

运行我们之前写过的测试方法.

    @Test
    void insertArticle() {
        Article article=new Article();
        for(int i=0;i<6;i++){
            article.setTitle("今日11.28");
            article.setContent("昨日11.27");
            int result= mapper.insertArticle(article);
            log.info("插入条目: "+result);
        }

    }

会发现没有任何不同~

2.8 使用注解进行动态SQL

在上一篇博文中,我们分别使用注解和XML的方式实现了Mapper层接口的方法,但是很容易发现,使用注解的方式会更简单,只需要在定义的方法上面编写SQL语句即可.

实际上,我们往往使用XML的方式编写动态SQL,使用注解编写简单的SQL.

 因为使用注解来编写动态SQL比较麻烦.

下面简单演示一下<where>标签在注解中的实现.

    @Select("<script>" +
            "select * from article" +
            "<where> " +
            "<if test='title!=null'>title=#{title}</if>" +
            "<if test='content!=null'>and content=#{content}</if>"+
            "<if test='id!=null'>and id=#{id}</if>"+
            "</where>"+
            "</script>")
    List<Article> selectArticles(Article article);

可以看到,需要在SQL语句的开头添加<script>标签,然后对照这xml文件copy一遍...

运行测试方法,可以正常运行,但是过程未免复杂了些~

其余标签不再进行演示,感兴趣的读者可自行实验.

三. 数据库连接池

在JDBC阶段,我们主要使用了四个对象----DataSource,Connection,PreStatement,ResultSet .其中DataSource指定一个数据源,我们可以从中获取数据库连接,即Connection对象.

简单回顾一下"池":当我们需要取对象时,从池中取即可,当我们不再需要这个对象时,把它重新放回池即可.

"池"帮助我们省去了创建/销毁对象的开销,尤其是频繁创建/销毁的场景下.

常见的数据库连接池有: C3P0,DBCP,Druid,HiKari...

其中HiKari是Spring默认使用的数据库连接池,我们可以从Spring打印的日志中发现它的痕迹.

当然了,如果我们想更换连接池,只需要引入相应的依赖即可.

比如把连接池换成Druid,我们需要在pom.xml文件中添加对应的依赖

然后再次运行启动Spring,就可以从日志中发现这次使用的数据库连接池是Druid了.

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

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

相关文章

【Qt之QSqlRelationalTableModel】描述及使用

描述 QSqlRelationalTableModel类为单个数据库表提供了一个可编辑的数据模型&#xff0c;并支持外键。 QSqlRelationalTableModel的行为类似于QSqlTableModel&#xff0c;但允许将列设置为其他数据库表的外键。 左边的屏幕截图显示了QTableView中一个普通的QSqlTableModel。外…

哈希思想应用【C++】(位图,布隆过滤器,海量数据处理面试题)

目录 一&#xff0c;位图 1. 位图概念 2.实现 3. 测试题 位图的优缺点 二&#xff0c;布隆过滤器 1). 布隆过滤器提出 2). 概念 3). 布隆过滤器的查找 4). 布隆过滤器删除(了解) 5). 布隆过滤器优点 6). 布隆过滤器缺陷 三&#xff0c;海量数据面试题 1&#xff…

C语言你爱我么?(ZZULIOJ 1205:你爱我么?)

题目描述 LCY买个n束花准备送给她暗恋的女生&#xff0c;但是他不知道这个女生是否喜欢他。这时候一个算命先生告诉他让他查花瓣数&#xff0c;第一个花瓣表示"爱"&#xff0c;第二个花瓣表示"不爱"&#xff0c;第三个花瓣表示"爱"..... 为了使最…

【Openstack Train安装】七、glance安装

Glance是为虚拟机的创建提供镜像的服务&#xff0c;我们基于Openstack是构建基本的IaaS平台对外提供虚拟机&#xff0c;而虚拟机在创建时必须为选择需要安装的操作系统&#xff0c;Glance服务就是为该选择提供不同的操作系统镜像。Glance提供Restful API可以查询虚拟机镜像的me…

计算机网络(超详解!) 第二节 物理层(上)

1.物理层的基本概念 物理层考虑的是怎样才能在连接各种计算机的传输媒体上传输数据比特流&#xff0c;而不是指具体的传输媒体。 物理层的作用是要尽可能地屏蔽掉不同传输媒体和通信手段的差异。 用于物理层的协议也常称为物理层规程(procedure)。 2.物理层的主要任务 主要…

Linux处理文本常见命令

目录 1 vim 2 echo 3 tee 4 cat 1 vim 编辑文本类的内容&#xff0c;使用的时候 vim [文件名]&#xff0c;比如 vim A.txt 进入vim界面后&#xff0c;按i可以开启编辑模式&#xff0c;按ESC可以关闭编辑模式&#xff0c;关闭编辑模式后:wq!保存并退出 2 echo ech…

PHP:处理数据库查询数据

注&#xff1a; DB_num_rows($result5)可以替换为mysqli_num_rows($result5) DB_fetch_array($result5)可以替换为mysqli_fetch_assoc($result5) 一、查询单个数据 代码解析 1、SQL语句 查询表www_users中当userid等于变量$_SESSION[UserID]时的depart_code值 $sql &qu…

【JavaEE初阶】 HTTP 请求 (Request)详解

文章目录 &#x1f340;序言&#x1f384;认识URL&#x1f6a9;URL 基本格式&#x1f6a9;query string&#x1f6a9;关于 URL encode &#x1f334;认识 "方法" (method)&#x1f6a9;GET方法&#x1f6a9;POST 方法&#x1f6a9; GET 和 POST 的区别 &#x1f38b;…

7 种 JVM 垃圾收集器详解

一、概述 如果说收集算法是内存回收的方法论&#xff0c;那么垃圾收集器就是内存回收的具体实现。Java虚拟机规范中对垃圾收集器应该如何实现并没有任何规定&#xff0c;因此不同的厂商、版本的虚拟机所提供的垃圾收集器都可能会有很大差别&#xff0c;并且一般都会提供参数供用…

如何利用软文打动消费者,媒介盒子支招

软文与一般文案的差别就在于它的目的性十分强烈&#xff0c;写软文不难&#xff0c;但是想要写出打动消费者的软文还需要一定的技巧。它需要根据目标受众来输出&#xff0c;接下来媒介盒子就为大家分享&#xff1a;如何用软文提升产品购买率。 一、 故事打动用户 没人会不爱看…

接口测试【加密解密攻防完整版】实战教程详解

一、对称加密 对称加密算法是共享密钥加密算法&#xff0c;在加密解密过程中&#xff0c;使用的密钥只有一个。发送和接收双方事先都知道加密的密钥&#xff0c;均使用这个密钥对数据进行加密和解密。 数据加密&#xff1a;在对称加密算法中&#xff0c;数据发送方将明文 (原…

1 NLP分类之:FastText

0 数据 https://download.csdn.net/download/qq_28611929/88580520?spm1001.2014.3001.5503 数据集合&#xff1a;0 NLP: 数据获取与EDA-CSDN博客 词嵌入向量文件&#xff1a; embedding_SougouNews.npz 词典文件&#xff1a;vocab.pkl 1 模型 基于fastText做词向量嵌入…

抖音、视频号流行的 Bokeh(虚化) 效果是怎么实现的?

未经作者(微信ID:Byte-Flow)允许,禁止转载 文章首发于公众号:字节流动 什么是 bokeh 效果? Bokeh 效果是指照片中背景模糊而主体清晰的一种摄影效果。这种效果是通过使用大光圈的镜头来实现的,使得光圈外的景物失去焦点,呈现出一种柔和、虚化的效果。 Bokeh 效果的质量…

30万起售的阿维塔12能卖的动吗?

作者 | 魏启扬 来源 | 洞见新研社 今年前十个月&#xff0c;累计交付1.76万辆&#xff0c;这就是阿维塔11交出的成绩单。 作为一个拥有长安汽车和宁德时代作为资源支撑&#xff0c;华为提供技术支持的品牌&#xff0c;阿维塔11平均每个月不到2000辆的销量水平显然有失水准。 …

科研绘图配色

01 配色的基本原则 颜色需要有自身的意义。不同的颜色表示不同的分组&#xff0c;相近的颜色表示同一个分组&#xff1b;配色需要展现数据逻辑关系&#xff0c;突出关键数据&#xff0c;比如重要的数据用深色或暖色表示&#xff0c;不重要的数据用浅色或冷色表示。 色彩种类两…

Redis 基础、字符串、哈希、有序集合、集合、列表以及与 Jedis 操作 Redis 和与 Spring 集成。

目录 1. 数据类型 1.1 字符串 1.2 hash 1.3 List 1.4 Set 1.5 sorted set 2. jedis操作redis 3. 与spring集成 1. 数据类型 1.1 字符串 String是最常用的数据格式&#xff0c;普通的kay-value都归结为此类&#xff0c; value值不仅可以是string&#xff0c;可以是数字…

【c语言:常用字符串函数与内存函数的使用与实现】

文章目录 1. strlen函数1.1使用1.2模拟实现 2.strcmp函数2.1使用2.2模拟实现 3.strncmp函数3.1使用3.2模拟实现 4.strcpy函数4.1 使用4.2模拟实现 5.strcncpy5.1使用5.2模拟实现 6.strcat函数6.1使用6.2模拟实现 7.strncat函数7.1使用7.2模拟实现 8.strstr函数8.1使用8.2模拟实…

ffmpeg 免安装,配置环境变量

1、下载ffmpeg https://download.csdn.net/download/qq284489030/88579595 2、解压 解压ffmpeg-4.4-essentials_build.zip到目标文件夹&#xff0c;比如 d:\apps下&#xff1b; 3、配置环境变量 &#xff08;1&#xff09;电脑桌面鼠标右键点击“此电脑”&#xff0c;弹出…

[带余除法寻找公共节点]二叉树

二叉树 题目描述 如上图所示&#xff0c;由正整数1, 2, 3, ...组成了一棵无限大的二叉树。从某一个结点到根结点&#xff08;编号是1的结点&#xff09;都有一条唯一的路径&#xff0c;比如从10到根结点的路径是(10, 5, 2, 1)&#xff0c;从4到根结点的路径是(4, 2, 1)&#x…

C++算法入门练习——数据流第K大元素

现有一个初始为空的序列S&#xff0c;对其执行n个操作&#xff0c;每个操作是以下两种操作之一&#xff1a; 往序列S中加入一个正整数x&#xff1b;输出当前序列S​中第k​大的数。 其中&#xff0c;第k大是指将序列从大到小排序后的第k个数。 利用stl里的priority_queue自动…