javaEE高阶---MyBatis

news2025/1/18 6:50:04

一 : 什么是MyBatis

MyBatis是更简单完成程序和数据库交互的工具,也就是更简单的操作和读取数据库的工具.

MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 去除了几乎所有的 JDBC 代码以及设置参数和获取结果集的动作 . MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通 式 Java 对象)为数据库中的记录 .

二 : MyBatis的作用

在这里插入图片描述

JDBC的工作流程非常繁琐 , 主要包括 :

1.创建数据库连接池DataSource
2.通过DataSource获取数据库连接Connection
3. 编写要执行带 ? 占位符的 SQL 语句
4. 通过 Connection 及 SQL 创建操作命令对象 Statement
5. 替换占位符:指定要替换的数据库字段类型,占位符索引及要替换的值
6. 使用 Statement 执行 SQL 语句
7. 查询操作:返回结果集 ResultSet,更新操作:返回更新的数量
8. 处理结果集
9. 释放资源

而MyBatis可以帮助我们更方便、更快速的操作数据库 !

MyBatis组成

在这里插入图片描述

为什么要这样实现呢?

MyBatis要想实现增删改查,就必须要写SQL。写SQL就有两种选择:

  1. 写到Java的类里面;
  2. 放在xml里面。

如果选择第一种方式,那么你只能将SQL语句放在双引号中,如果你需要进行比较复杂的查询,比如联表查询,此时双引号中的字符串内容非常长,且里面包含大量的表名,字段名等等,这种做法出错的概率很高;

此时目光转到第二种方式。

在这里插入图片描述

三 : 学习重点

MyBatis 学习分为两部分:

  1. 配置 MyBatis 开发环境;
  2. 使用MyBatis模式和语法操作数据库 .

四 : 配置MyBatis开发环境

4.1 创建数据库和表

要使用MyBatis 的模式来读取用户表中的所有用户 , 需要先建库和建表 , 示范SQL语句如下 :

-- 创建数据库
drop database if exists myblog;
create database myblog DEFAULT CHARACTER SET utf8mb4;

-- 使用数据数据
use myblog;

-- 创建表[用户表]
drop table if exists  userinfo;
create table userinfo(
    id int primary key auto_increment,
    username varchar(100) not null,
    password varchar(50) not null,
    photo varchar(500) default '',
    createtime datetime default now(),
    updatetime datetime default now(),
    `state` int default 1
) default charset 'utf8mb4';

-- 添加一个用户信息
INSERT INTO `myblog`.`userinfo` (`id`, `username`, `password`, `photo`, `createtime`, `updatetime`, `state`) VALUES 
(1, 'admin', 'admin', '', '2021-12-06 17:10:48', '2021-12-06 17:10:48', 1);

查询数据库中用户表信息 :

在这里插入图片描述

添加成功 !!!

4.2 创建项目

创建一个Spring MVC项目,并添加MyBatis框架支持.

在这里插入图片描述

4.3 设置数据库和MyBatis配置

4.3.1 配置数据库的连接信息(连接哪台数据库)

在resources文件夹下创建application.yml文件。
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/myblog?characterEncoding=utf8
    username: root
    password: 111111
    driver-class-name: com.mysql.cj.jdbc.Driver

在这里插入图片描述

在这里插入图片描述

4.3.2 配置MyBatis XML文件存放位置和命名规则

仍然在application.yml中进行操作。

在这里插入图片描述

此时同时需要在resources路径下创建mapper文件夹,如下图所示:

在这里插入图片描述

五:使用MyBatis实现增删改查功能

5.1 查询所有信息

首先回顾一下我们数据库中的内容 :

在这里插入图片描述

下面我们来实现,使用MyBatis进行查询。

实现步骤很简单,只要你理解了MyBatis的设计思路,其实不在话下。

步骤:
1.创建一个接口;
2.创建与上面接口对应的xml文件。

1.创建一个接口。

注意:一定要添加@Mapper注解,表示这是mybatis的接口!!!!!!

在这里插入图片描述

package com.example.demo.mapper;
import com.example.demo.model.UserInfo;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

@Mapper //mybatis 接口
public interface UserMapper {
    //查询所有的信息
    public List<UserInfo> getAll();
}

2.创建与上面接口对应的xml文件。

第一个问题,这个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="">
   
</mapper>

针对当前的测试项目,完整内容如下:

<?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.UserMapper">
    <select id="getAll" resultType="com.example.demo.model.UserInfo">
        select * from userinfo
    </select>
</mapper>

在这里插入图片描述

到此为止,我们已经实现了MyBatis查询数据的功能。

那我们这个实现到底对不对呢?这里我们插入“单元测试”的内容,以检验代码的正确性。

有关单元测试的内容,详见这篇文章:

SpringBoot单元测试

5.2 传参查询

package com.example.demo.mapper;
import com.example.demo.model.UserInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

@Mapper //mybatis 接口
public interface UserMapper {
    //查询所有的信息
    List<UserInfo> getAll();
    //传参查询
    UserInfo getUserById(@Param("uid") Integer id);
}


此处需要使用一个注解:@Param

<?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.UserMapper">
    <select id="getAll" resultType="com.example.demo.model.UserInfo">
        select * from userinfo
    </select>

    <select id="getUserById" resultType="com.example.demo.model.UserInfo">
        select * from userinfo where id=#{uid}
    </select>
</mapper>

在这里插入图片描述

生成单元测试:

package com.example.demo.mapper;
import com.example.demo.model.UserInfo;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;

@SpringBootTest//当前测试的上下文环境为springboot
class UserMapperTest {

    @Autowired
    private UserMapper userMapper;

    @Test
    void getAll() {
        List<UserInfo> list = userMapper.getAll();
        for (UserInfo user : list) {
            System.out.println(user.toString());
        }
    }

    @Test
    void getUserById() {
        UserInfo userInfo = userMapper.getUserById(1);
        System.out.println(userInfo.toString());
    }
}


在这里插入图片描述

运行结果:

在这里插入图片描述

因为当前确实有id为1的一条记录,所以就成功查询出来了。

类比传参查询,我们就可以实现登录操作了,无非是传参时传递用户名和密码两个参数。

5.3 添加数据

前置工作,先创建一张文章表,代码如下:

-- 创建文章表
drop table if exists  articleinfo;
create table articleinfo(
    id int primary key auto_increment,
    title varchar(100) not null,
    content text not null,
    createtime datetime default now(),
    updatetime datetime default now(),
    uid int not null,
    rcount int not null default 1,
    `state` int default 1
)default charset 'utf8mb4';

在这里插入图片描述

创建文章的实体类

package com.example.demo.model;

import lombok.Data;

import java.util.Date;

@Data
public class ArticInfo {
    private int id;
    private String title;
    private String content;
    private Date createtime;
    private Date updatetime;
    private int uid;
    private int rcount; // 访问量
    private int state; // 状态(预览字段)
    //..
}

注意创建位置:

在这里插入图片描述

在查询数据时,如果你创建的实体类的对象名和你表中的字段名不一致,那么查询会失败;而在插入数据时,即使你创建的实体类的对象名和表中的字段名不一致,比如表中是titile,你写成了private String t,此时在插入数据时,我们只需要在xml中将插入的对象名写为t,即可成功插入。

基于规范性,我们要求:实体类的对象名和表中的字段名要一一对应!

添加接口及xml实现

此处有2中方法,一种加注解,一种不加注解:

方法一:加注解

package com.example.demo.mapper;

import com.example.demo.model.ArticleInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

@Mapper
public interface ArticleInfoMapper {
    // 添加方法
    public int add(@Param("articleInfo") ArticleInfo articleInfo);
}

在这里插入图片描述

<?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.ArticleInfoMapper">
    <insert id="add">
        insert into articleinfo(title,content,uid)
        values(#{articleinfo.title},#{articleinfo.content},#{articleinfo.uid});
    </insert>
</mapper>

在这里插入图片描述

进行单元测试:

在这里插入图片描述

在这里插入图片描述
方法二:不加注解

在这里插入图片描述
在这里插入图片描述

进行单元测试:

在这里插入图片描述

在这里插入图片描述

以上这两种写法下,控制台返回的都是受影响的行数:1。

在这里插入图片描述

5.4 添加得到用户的自增id

如何在添加同时得到用户的自增id呢?需要在xml的insert标签中添加两个键值对,具体操作方法如下:

在这里插入图片描述
在这里插入图片描述

单元测试:

在这里插入图片描述

运行结果:

在这里插入图片描述
在这里插入图片描述

你可能会疑惑,这里的自增id不应该是3吗 ?没错,事实是我在这中间测试时候添加了两条数据,之后又删除了它们,所以你会看到自增id是5。

5.5 删除数据

删除功能很简单,仍然沿袭前面的步骤:

  1. 添加方法;
  2. 修改xml文件;
  3. 编写单元测试。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

同理也可将id=2的文章删除。

5.6 修改数据

在这里插入图片描述
在这里插入图片描述

单元测试:

在这里插入图片描述
在这里插入图片描述

我们上面的代码都是MyBatis的操作,但是没有提供相应的入口。下面我简单介绍一下如何提供入口,即创建控制器层和服务器层。以查询用户信息为例:

在这里插入图片描述

package com.example.demo.controller;


import com.example.demo.model.UserInfo;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController//标识其为控制器层
@RequestMapping("/user")//访问路由
public class UserController {

    @Autowired
    private UserService userService;

    @RequestMapping("/getall")//访问路由
    public List<UserInfo> getUser(){
        return userService.getAll();//调用服务层的getAll方法
    }
}

package com.example.demo.service;

import com.example.demo.mapper.UserMapper;
import com.example.demo.model.UserInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.List;

@Service//标识其为服务层
public class UserService {

    @Autowired
    private UserMapper userMapper;

    public List<UserInfo> getAll() {
        return userMapper.getAll();//调用接口的getAll方法
    }
}

然后userMapper基于MyBatis,在数据库中查找相应的内容。启动DemoApplication类:

在这里插入图片描述

成功查询出所有信息!

六:参数占位符问题

前面在进行参数匹配时,我们使用了#{ } 。实际上我们还有另一个参数占位符,就是${ }。

  • #{}:预编译处理。
  • ${}:字符直接替换。

6.1 #{}

预编译处理是指:MyBatis 在处理#{}时,会将 SQL 中的 #{} 替换为?,使⽤ PreparedStatement的 set ⽅法来赋值。

#{}: 解析为SQL时,会将形参变量的值取出,并自动给其添加引号。 例如:当实参username="Amy"时,传入下Mapper映射文件后:

......
    <select id="findByName">
        SELECT * FROM user WHERE username=#{value}
    </select>
    ....

SQL将解析为:

SELECT * FROM user WHERE username="Amy"

6.2 ${}

直接替换是指:MyBatis 在处理 ${} 时,就是把 ${} 替换成变量的值。

${}: 解析为SQL时,将形参变量的值直接取出,直接拼接显示在SQL中

例如:当实参username="Amy"时,传入下Mapper映射文件后:

......
    <select id="findByName">
        SELECT * FROM user WHERE username=${value}
    </select>
    ....

SQL将解析如下:

SELECT * FROM user WHERE username=Amy

显然该SQL无法正常执行,故需要在mppaer映射文件中的${value}前后手动添加引号,如下所示:

......
    <select id="findByName" parameterType="String" resultMap="studentResultMap">
        SELECT * FROM user WHERE username='${value}'
    </select>
    ....

SQL将解析为:

SELECT * FROM user WHERE username='Amy'

6.3 SQL注入

${}方式是将形参和SQL语句直接拼接形成完整的SQL命令后,再进行编译,所以可以通过精心设计的形参变量的值,来改变原SQL语句的使用意图从而产生安全隐患,即为SQL注入攻击。现举例说明:

在这里插入图片描述

当前我们数据库中有一条数据,其用户名和密码都是"admin"。正常情况下,我们使用#{}作为参数占位符号,此时正确输入用户名和密码,查询可以成功,代码如下:

1.添加方法:

   //登录查询,需要提供用户名和密码
    UserInfo getUserToLogin(@Param("username") String username,@Param("password")String password);

2.配置xml文件

    <select id="getUserToLogin" resultType="com.example.demo.model.UserInfo">
        select * from userinfo where username=#{username} and password=#{password}
    </select>

3.进行单元测试

    @Test
    void getUserToLogin() {
        UserInfo userInfo = userMapper.getUserToLogin("admin","admin");
        System.out.println(userInfo);
    }

查询结果:

在这里插入图片描述

此时没有问题。

如果我们错误地输入了用户名或密码,当然不能查询出结果,如下所示:

在这里插入图片描述

现在我们使用${}作为参数占位符号,此时正确输入用户名和密码,查询可以成功,代码如下:

    <select id="getUserToLogin" resultType="com.example.demo.model.UserInfo">
        select * from userinfo where username='${username}' and password='${password}'
    </select>

查询结果:

在这里插入图片描述

如果我们错误地输入了用户名或密码,也不能查询出结果。但是,如果我们稍微设计一下输入的密码,那么即使用户名和密码都是错误地,仍然能够查询出结果,如下图所示:

    @Test
    void getUserToLogin() {
        UserInfo userInfo = userMapper.getUserToLogin("admin","' or 1='1");
        System.out.println(userInfo);
    }

在这里插入图片描述

这就是使用${}作为参数占位符所存在的最大的问题,它是不安全的,存在SQL注入的风险。

在这里插入图片描述
那么${}这个占位符是否一无是处呢?有一种场景下是需要使用到 ${} 的。比如我们要实现对数据库中的数据进行排序,但是需要以传参指定排序的方式。代码如下:

先向数据库中插入一条数据,以便于观察结果:

在这里插入图片描述

    // 查询所有的信息(根据排序条件进行排序)
    public List<UserInfo> getAllByOrder(@Param("order") String order);
    <select id="getAllByOrder" resultType="com.example.demo.model.UserInfo">
        select * from userinfo order id by ${order}
    </select>
    @Test
    void getAllByOrder() {
        List<UserInfo> list = userMapper.getAllByOrder("desc");
        for (UserInfo userInfo : list) {
            System.out.println(userInfo);
        }
    }

运行结果:

在这里插入图片描述

当需要传入关键字时,我们就要用到${},因为如果使用#{}的话,在进行预处理阶段,desc这样的关键字会被加上双引号,直接解析为字符串了。

6.4 like查询

like 使⽤ #{} 报错。

 <select id="findUserByName2" resultType="com.example.demo.model.User">
 select * from userinfo where username like '%#{username}%';
 </select>

相当于: select * from userinfo where username like ‘%‘username’%’;

此时可以考虑使用mysql的内置函数concat处理,代码如下:

<select id="findUserByName3" resultType="com.example.demo.model.User">
 select * from userinfo where username like concat('%',#{usernam
e},'%');
</select>

6.5 多表查询

例如在articleinfo表查询出某篇文章的信息后,在userinfo表中查询出这篇文章的作者。

步骤如下:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

单元测试:

在这里插入图片描述

查询此时数据库中表的信息:
在这里插入图片描述

查看单元测试结果:

在这里插入图片描述
在这里插入图片描述

6.6 字段重命名

程序中的属性和数据库中的字段名不一致时,可使用resultMap来解决。当你和数据库管理员分属于不同部门时,如果他使用了一些“魔法数字”作为字段名,比如将currentTime命名为ct,这显然不太符合规范。此时我们可以在程序中使用不同的字段名,并返回字典映射:resultMap。

在这里插入图片描述

package com.example.demo.model;

import lombok.Data;

import java.util.Date;

@Data
public class UserInfo {
    private int author_id;
    private String username;
    private String password;
    private String photo;
    private Date createtime;
    private Date updatetime;
    private int state;
}
    <resultMap id="BaseMap" type="com.example.demo.model.UserInfo">
        <id column="id" property="author_id"></id>
        <result column="username" property="username"></result>
        <result column="password" property="password"></result>
    </resultMap>

    <select id="getAll" resultMap="BaseMap">
        select * from userinfo
    </select>

在这里插入图片描述

单元测试:

    @Test
    void getAll() {
        List<UserInfo> list = userMapper.getAll();
        for (UserInfo user : list) {
            System.out.println(user.toString());
        }
    }

在这里插入图片描述

七:MyBatis动态SQL

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

你可以参阅MyBatis的官方文档,进行动态SQL的学习。

7.1 < if >标签

在注册⽤户的时候,通常存在一些必填项字段和选填项字段,例如手机号、用户名、密码通常都是必填字段,而年龄、性别、生日等则是选填项字段。那如果在添加⽤户的时候有不确定的字段传⼊,程序应该如何实现呢?此时就需要使用到MyBatis的动态标签< if >了。

在这里插入图片描述

例如userinfo表中的photo字段就是一个非必填字段,具体实现如下:

添加方法:

    //添加用户
    public int add(@Param("username") String username,
                   @Param("password") String password,
                   @Param("photo") String photo);

配置xml文件:

    <insert id="add">
        insert into userinfo(username,
        <if test="photo != null">
                photo,
        </if>
        password)
        values(#{username},
        <if test="photo != null">
            #{photo},
        </if>
        #{password})
    </insert>

进行单元测试:

    @Test
    void add() {
        int result = userMapper.add("zhangsan","123456",null);
        System.out.println("受影响行数 " + result);
    }

在这里插入图片描述

注意:

在这里插入图片描述

7.2 < trim >标签

之前的插⼊⽤户功能,只是有⼀个photo字段可能是选填项,如果所有字段都是⾮必填项,就考虑使⽤< trim >标签结合< if >标签,对多个字段都采取动态⽣成的⽅式。

在这里插入图片描述

调整 UserMapper.xml 的插⼊语句为:

 <insert id="add">
        insert into userinfo
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="username != null">
                username,
            </if>
            <if test="photo != null">
                photo,
            </if>
            <if test="password != null">
                password,
            </if>
        </trim>
        <trim prefix="values(" suffix=")" suffixOverrides=",">
            <if test="username != null">
                #{username},
            </if>
            <if test="photo != null">
                #{photo},
            </if>
            <if test="password != null">
                #{password},
            </if>
        </trim>
    </insert>

进行单元测试:

    @Test
    void add() {
        int result = userMapper.add("telier","156552","telier.png");
        System.out.println("受影响行数 " + result);
    }

在这里插入图片描述

解释:

在这里插入图片描述

7.3 < where >标签

传入的用户对象,根据属性做 where 条件查询,用户对象中属性不为 null 的,都为查询条件。

例如此根据用户名查询符合条件的用户,如果用户名不匹配,应该显示数据库中的所有用户:

    // 根据用户姓名匹配查询
    public List<UserInfo> getUserByName(@Param("username") String username);
    <select id="getUserByName" resultType="com.example.demo.model.UserInfo">
        select * from userinfo
        <where>
            <if test="username != null">
                and username=#{username}
            </if>
        </where>
    </select>

单元测试:

测试一:

    @Test
    void getUserByName() {
        List<UserInfo> list = userMapper.getUserByName(null);
        for (UserInfo user: list) {
            System.out.println(user);
        }
    }

运行结果:

在这里插入图片描述

测试二:

    void getUserByName() {
        List<UserInfo> list = userMapper.getUserByName("zhangsan");
        for (UserInfo user: list) {
            System.out.println(user);
        }
    }

运行结果:

在这里插入图片描述
在这里插入图片描述

7.4 < set >标签

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

示例:UserMapper 接口中修改用户方法:根据传入的用户 id 属性,修改其他不为 null 的属性。

    //根据用户id修改用户数据
    int updateById(UserInfo userInfo);
    <update id="updateById" parameterType="com.example.demo.model.UserInfo">
        update userInfo
        <set>
            <if test="username != null">
                username=#{username},
            </if>
            <if test="password != null">
                password=#{password},
            </if>
            <if test="photo != null">
                photo=#{photo},
            </if>
            <if test="createtime != null">
                createtime=#{createtime},
            </if>
            <if test="updatetime != null">
                updatetime=#{updatetime},
            </if>
            <if test="state != null">
                state=#{state},
            </if>
        </set>
        where id=#{id}
    </update>

进行单元测试:

    @Test
    void updateById() {
        UserInfo userInfo = userMapper.getUserById(7);
        userInfo.setUsername("caixukun");
        userInfo.setPhoto("jinitaimei.png");
        int result = userMapper.updateById(userInfo);
        System.out.println("受影响行数 " + result);
    }

测试前用户表中的数据:

在这里插入图片描述

测试后用户表中的数据:

在这里插入图片描述

在这里插入图片描述

7.5 < for each >标签

对集合进行遍历时可以使用该标签。

在这里插入图片描述
示例:根据多个用户 id 来删除用户数据。

    // 多条用户的删除
    public int delByIds(List<Integer> ids);
    <delete id="delByIds">
        delete from userinfo where id in
        <foreach collection="ids" item="item" open="(" close=")" separator=",">
            #{item}
        </foreach>
    </delete>

进行单元测试:

    @Test
    void delByIds() {
        List<Integer> list = new ArrayList<>();
        list.add(6);
        list.add(8);
        list.add(9);
        userMapper.delByIds(list);
    }

在这里插入图片描述

已经成功删除了id为6,8,9号的用户。

在这里插入图片描述

以上就是MyBatis的全部内容。

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

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

相关文章

[oeasy]python0037_终端_terminal_电传打字机_tty_shell_控制台_console_发展历史

换行回车 回忆上次内容 换行 和 回车 是两回事 换行 对应字节0x0ALine-Feed 水平 不动垂直 向上喂纸 所以是 feed 回车 对应字节0x0DCarriage-Return 垂直 不动水平 回到纸张左侧 可移动的打印头 运输字符 的 装置 (Carriage)回到 行首 所以是 Return tty、terminal、shell、…

【视觉SLAM】DM-VIO: Delayed Marginalization Visual-Inertial Odometry

L. v. Stumberg and D. Cremers, “DM-VIO: Delayed Marginalization Visual-Inertial Odometry,” in IEEE Robotics and Automation Letters, vol. 7, no. 2, pp. 1408-1415, April 2022, doi: 10.1109/LRA.2021.3140129. 论文阅读方法&#xff1a;Title&#xff0c;Abstract…

百趣代谢组学文献分享:学科交叉研究,微生物回收重金属机制研究

发表期刊&#xff1a;Environment International 影响因子&#xff1a;7.297 发表时间&#xff1a;2019年 合作单位&#xff1a;福建农林大学 百趣代谢组学文献分享&#xff0c;该文章是BIOTREE协助客户2019年发表在Environment International上的关于微生物回收重金属机制研…

Tomcat的Connector启动过程分析

一. 前言 前面分析了tomcat的整体架构和tomcat的启动过程&#xff0c;在分析启动过程的时候只讲了整体的启动过程&#xff0c;本篇来重点分析一下tomcat的Connector(连接器)组件的启动过程。 二.从Connector的构造开始 那么org.apache.catalina.connector.Connector是在什么…

文献学习06_利用句法指示符和句子上下文加强关系抽取

论文信息 Subjects: Computation and Language (cs.CL) &#xff08;1&#xff09;题目&#xff1a;Enhancing Relation Extraction Using Syntactic Indicators and Sentential Contexts &#xff08;利用句法指示符和句子上下文加强关系抽取&#xff09; &#xff08;2&…

论文精读:RPM-Net: Robust Point Matching using Learned Features

论文地址:https://arxiv.org/pdf/2003.13479.pdf 点云配准任务 点云配准可以当做一个基础的上游任务,根据从不同视角下获取的点云数据配准为完整的点云数据,下游任务众多 基本任务:求一个变换矩阵,使得两个具有未知点的点云数据重合。 刚性与非刚性: 刚性配准:旋转和平…

Leetcode 121买卖股票的最佳时机

题目描述&#xff1a; 给定一个数组 prices &#xff0c;它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择 某一天 买入这只股票&#xff0c;并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。 返回你可以从这笔…

solr集群配置(使用solr自带的Jetty实现集群配置)

看了很多的资料发现基本集群搭建都是通过tomcat的方式实现的&#xff0c;但是在高版本的solr中&#xff0c;可以通过solr自带的jetty实现集群的搭建 准备 1.虚拟机安装linux 2.安装jdk 3.下载solr并解压 步骤 1.进入到解压后solr的bin目录下&#xff0c;并执行 ./solr -e clo…

赛狐ERP | 如何高效管理亚马逊广告!用这款亚马逊ERP就够了!

亚马逊的广告管理是是每一位亚马逊运营的必修课&#xff0c;除自然流量外&#xff0c;广告来带的流量与转化占比都极高&#xff0c;广告做活了&#xff0c;就是打虎上山&#xff1b;广告搞砸了&#xff0c;就是骑虎难下&#xff1a;不开广告吧没有流量卖不动、开了广告吧财务账…

#B. 部落联盟

一,题目Description在草原上有N个部落&#xff0c;每个部落都有其坐标(xi,yi)每个部落都有个武力值&#xff0c;可正可负由于部落间只能通过马匹来传递信息于是只有当两个部落间的距离为1的时候&#xff0c;两个部落才有可能进行联系&#xff0c;距离计算公式为abs(xi-xj)abs(y…

人生的喜悦、不快与成长,都在那一篇篇的文字中得到记录 | 2022 年终总结

又是一年的总结&#xff0c;不知道自己今年又该写点什么。但提笔总是好的&#xff0c;也算对今年的一个交代和对未来的一份期许。窗外的阳光正好&#xff0c;对面楼的敲打声叮叮咚咚&#xff0c;窗台上的两只猫睡得依旧奔放和舒适。这样一个看似美好的下午&#xff0c;一个平凡…

Internet Download Manager2023最好用的HTTP下载神器

Internet Download Manager 介绍2023最佳下载利器。Internet Download Manager (简称IDM) 是一款Windows 平台功能强大的多线程下载工具&#xff0c;国外非常受欢迎。支持断点续传&#xff0c;支持嗅探视频音频&#xff0c;接管所有浏览器&#xff0c;具有站点抓取、批量下载队…

机器学习100天(二十六):026 k近邻分类算法-理论

机器学习100天,今天讲的是:K 近邻分类算法-理论。 《机器学习100天》完整目录:目录 一、什么是 K 近邻算法 K 近邻算法也叫 KNN(k-Nearest Neighbor)算法,它是一个比较成熟也是最简单的机器学习算法之一。K 近邻分类算法的思路是:如果一个样本在特征空间中与 K 个实例最…

FastAPI集成Socket.io坑点汇集和技术选型

背景 单纯的 websocket 通信方式存在大量的辅助性的工作需要处理&#xff0c;例如心跳机制、粘包处理、协议规范等&#xff0c;所以直接使用 websocket 开发&#xff0c;等于重复造轮子&#xff0c;毫无价值&#xff0c;而 socket.io 整理了一整套规范和机制&#xff0c;可以满…

DSP-频域中的离散时间信号

目录 连续和离散时间傅里叶变换: 四种常用的傅立叶变换: 连续时间傅立叶变换(FT): 离散时间傅里叶变换(DTFT): 对称关系: DTFT的收敛条件: 常用DTFT对&#xff1a; DTFT的性质&#xff1a; 线性&#xff1a; 时间反转&#xff1a; 时移&#xff1a; 频移&#xff1a…

鲁大师2022牛角尖颁奖盛典落幕,各大硬件厂商齐聚襄阳

1月4日&#xff0c;鲁大师2022年度牛角尖颁奖晚会在湖北襄阳成功举办。 鲁大师的”牛角尖”奖是由过去一年上亿用户通过鲁大师测试得到的真实数据&#xff0c;以及鲁大师实验室通过专业的测试规范共同缔造的硬件奖项。颁发给的都是各大PC、手机、电动车领域最优秀、最顶尖的产…

【自学Python】Python HelloWorld

Windows Python HelloWorld Windows Python HelloWorld 教程 使用记事本&#xff0c;新建一个 helloworld.py 文件&#xff0c;输入以下内容&#xff1a; print(嗨客网(www.haicoder.net))打开命令行&#xff0c;进入到 helloworld.py 文件所在目录&#xff0c;输入以下命令…

干货 | 移动端App自动化之App控件定位

客户端的页面通过 XML 来实现 UI 的布局&#xff0c;页面的 UI 布局作为一个树形结构&#xff0c;而树叶被定义为节点。这里的节点也就对应了我们要定位的元素&#xff0c;节点的上级节点&#xff0c;定义了元素的布局结构。在 XML 布局中可以使用 XPath 进行节点的定位。App的…

Spring项目

1.创建一个Java项目 名字为Test_SM_1 2.导包 Java项目&#xff0c;用快捷方式的方法导入 web项目用复制的方式 一共8个包&#xff0c;前两个是通用jar包&#xff0c;Spring相关的jar包有4个&#xff0c;mybatis的jar包有1个&#xff0c;mysql的jar包连接数据库 3. 复原一个MyB…

Rabbitmq消息队列详解(一)——基础介绍

安装rabbitmq 本机&#xff1a; http://erlang.org.download/otp_win64_21.3.exe 一直next https://dl.bintray.com/rabbitmq/all/rabbitmq-server/3.7.14/:rabbitmq-server-3.7.14.exe 全选&#xff0c;next 进入RabbitMQ安装目录下的sbin目录 rabbitmq-plugins enable …