MyBatis入门基础

news2024/9/20 20:32:38

目录

1.什么是MyBatis

2.第一个MyBatis查询 

2.1 准备工作

2.2 实际操作

2.2.1 定义接口 

2.2.2 创建XML实现上述接口

2.3 单元测试 

 2.3.1 单元测试的优势

2.3.2 创建并使用单元测试 

2.3.3 有关断言

3.增删改的基本操作 

3.1 插入操作 

特殊的添加:返回自增 id

3.2 修改操作 

3.3 删除操作

4.查询操作

4.1 单表查询

4.1.1 参数占位符 #{} 和 ${}

4.1.2 ${} 优点

4.1.3 SQL注入问题 

4.1.4 like查询 

 4.2 多表查询

4.2.1 resultType 

4.2.2 resultMap

4.2.3 多表查询一对一关系 

4.2.4 多表查询一对多关系


1.什么是MyBatis

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

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

其实MyBatis本质上还是会生成SQL语句并去执行,但是它帮助我们省去了jdbc繁琐复杂的操作,而MyBatis帮助我们简化了这些操作,这也是我们学习MyBatis的一个重要原因

2.第一个MyBatis查询 

2.1 准备工作

在使用MyBatis之前,我们首先需要引入MyBatis的相关依赖

 之后我们需要在配置文件配置数据库的连接信息

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/mycnblog?characterEncoding=utf8
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
# 开启 MyBatis sql 打印
logging:
  level:
    com:
      example:
        mybatisdemo: debug
# 查看执行过程 这段语句可以帮助我们看到具体的sql语句
mybatis:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

配置MyBatis的XML保存路径

# 配置 mybatis xml 保存路径
mybatis:
  mapper-locations: classpath:mybatis/**Mapper.xml

2.2 实际操作

首先我们来了解一下MyBatis的模式图

其实MyBatis的模式只包含两个部分:

1.接口(定义方法的声明)

2.xml实现接口中的方法 

这两个部分共同完成生成sql并将执行结果映射到程序的对象中

2.2.1 定义接口 

 首先在我们定义的接口类中一定要写上@Mapper注解,否则MyBatis无法识别。其次对于传入的参数最好使用@Param注解去重命名,否则在某些系统上可能会出现一些奇怪的错误。

2.2.2 创建XML实现上述接口

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace 要设置是实现接口的具体包名加类名 -->
<mapper namespace="com.example.mybatisdemo.mapper.UserMapper">
    <!-- 根据 id 查询用户 -->
    <select id="getUserById" resultType="com.example.mybatisdemo.model.UserInfo">
        select * from userinfo where id=${id}
    </select>
</mapper>

除了select标签的语句其他都是固定写法,大家可以保存在代码片段中方便随时使用。

注意我们的namespace要设置是实现接口的具体包名加类名,并且select语句是需要设置返回类型resultType或者resultMap的 ,而其他语句不需要,这个我们在下面会详细讲。

2.3 单元测试 

我们写好了相关的代码后该如何去测试它的功能呢?最传统的方法就是我们去模拟生产环境来对其进行测试,但是对于一个spring boot项目来说,它需要至少实现controller,service和repository三个层面的代码,这对于我们测试来说未免有一些麻烦,所以在实际项目中我们通常会使用单元测试来测试相关功能。

 2.3.1 单元测试的优势

1、可以⾮常简单、直观、快速的测试某⼀个功能是否正确。

2、使⽤单元测试可以帮我们在打包的时候,发现⼀些问题,因为在打包之前,所以的单元测试必须通 过,否则不能打包成功。

3、使⽤单元测试,在测试功能的时候,可以不污染连接的数据库,也就是可以不对数据库进行任何改 变的情况下,测试功能。

2.3.2 创建并使用单元测试 

由于我们的spring boot是自带测试框架的,所以我们不需要去额外配置,可以直接使用。

1.生成单元测试类

我们要先在我们需要测试的类中生成单元测试类,直接在该类中右键->生成->测试即可

 2.配置单元测试类

 

 这里我们只关心框内的选项即可,我们需要测试哪一个方法在前面✔即可

3.运行单元测试类

 这里我们要注意@SpringBootTest这个注解需要我们手动添加,代表当前测试环境是SpringBoot,如果不添加则会报错

package com.example.mybatisdemo.mapper;

import com.example.mybatisdemo.model.UserInfo;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

import static org.junit.jupiter.api.Assertions.*;
@Slf4j
//表示当前单元测试运行在springboot环境下
@SpringBootTest
class UserMapperTest {

    @Autowired
    private UserMapper userMapper;
    @Test
    //添加此注解使得方法执行完后会自动回滚事务
    @Transactional
    void add() {
        UserInfo userInfo=new UserInfo();
        userInfo.setUsername("王五");
        userInfo.setPassword("123");
        userInfo.setPhoto("default.png");
        int result = userMapper.add(userInfo);
        System.out.println("添加的结果:" + result);
        //断言操作
        Assertions.assertEquals(1, result);
    }
}

最后我们点击左边的绿色小三角就可以运行测试并得到结果了

 

2.3.3 有关断言

断⾔:如果断⾔失败,则后⾯的代码都不会执行,并且会提示出错

   

3.增删改的基本操作 

简单的查询我们已经在上面讲过了,这里不再赘述,有关多表查询等比较复杂的部分我们将在下面讲解。此处只讲解简单的增删改操作。

对应使⽤ MyBatis 的标签如下:

<insert>标签:插入语句

<update>标签:修改语句

<delete>标签:删除语句

以下为测试数据

-- 创建数据库
drop database if exists mycnblog;
create database mycnblog DEFAULT CHARACTER SET utf8mb4;
-- 使⽤数据数据
use mycnblog;
-- 创建表[⽤户表]
drop table if exists userinfo;
create table userinfo(
 id int primary key auto_increment,
 username varchar(100) not null,
 password varchar(32) not null,
 photo varchar(500) default '',
 createtime datetime default now(),
 updatetime datetime default now(),
 `state` int default 1
) default charset 'utf8mb4';
-- 创建⽂章表
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';
-- 创建视频表
drop table if exists videoinfo;
create table videoinfo(
 vid int primary key,
 `title` varchar(250),
 `url` varchar(1000),
createtime datetime default now(),
updatetime datetime default now(),
 uid int
)default charset 'utf8mb4';
-- 添加⼀个⽤户信息
INSERT INTO `mycnblog`.`userinfo` (`id`, `username`, `password`, `photo`,
`createtime`, `updatetime`, `state`) VALUES
(1, 'admin', 'admin', '', '2022-11-16 17:10:48', '2022-11-16 17:10:48', 1)
;
-- ⽂章添加测试数据
insert into articleinfo(title,content,uid)
 values('Java','Java正⽂',1);

UserInfo类:

package com.example.mybatisdemo.model;

import lombok.Data;

import java.util.List;

@Data
public class UserInfo {
    private Integer id;
    private String username;
    private String password;
    private String photo;
    private String createtime;
    private String updatetime;
    private int state;
    private List<ArticleInfo> artlist;
}

ArticleInfo类:

package com.example.mybatisdemo.model;

import lombok.Data;

@Data
public class ArticleInfo {
    private int id;
    private String title;
    private String content;
    private String createtime;
    private String updatetime;
    private int uid;
    private int rcount;
    private int state;
    private UserInfo userInfo;
}

3.1 插入操作 

插入用户操作:

<insert id="add">
 insert into userinfo(username,password,photo,state)
 values(#{username},#{password},#{photo},1)
</insert>
@Mapper
public interface UserMapper {
    //添加用户
    public int add(UserInfo userInfo);
}

特殊的添加:返回自增 id

<!-- 获取自增id -->
<insert id="addGetId" useGeneratedKeys="true" keyProperty="id" keyColumn="id">
    insert into userinfo(username,password,photo)
    values(#{username},#{password},#{photo})
</insert>

useGeneratedKeys:这会令 MyBatis 使⽤ JDBC 的 getGeneratedKeys ⽅法来取出由数据 库内部⽣成的主键(比如:像 MySQL 和 SQL Server 这样的关系型数据库管理系统的⾃动 递增字段),默认值:false。

keyColumn:设置生成键值在表中的列名,在某些数据库(像 PostgreSQL)中,当主键列 不是表中的第⼀列的时候,是必须设置的。如果⽣成列不⽌⼀个,可以⽤逗号分隔多个属性 名称。

keyProperty:指定能够唯⼀识别对象的属性,MyBatis 会使⽤ getGeneratedKeys 的返回 值或 insert 语句的 selectKey ⼦元素设置它的值,默认值:未设置(unset)。如果⽣成列 不⽌⼀个,可以⽤逗号分隔多个属性名称。(比如在这里我们就可以通过userinfo.getId()来获取到自增的id)

3.2 修改操作 

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

3.3 删除操作

<!-- 根据用户 id 删除用户 -->
<delete id="del">
    delete from userinfo where id=#{id}
</delete>

4.查询操作

4.1 单表查询

下⾯我们来实现⼀下根据⽤户 id 查询⽤户信息的功能

<!-- 根据 id 查询用户 -->
<select id="getUserById" resultType="com.example.mybatisdemo.model.UserInfo">
    select * from userinfo where id=${id}
</select>

4.1.1 参数占位符 #{} 和 ${}

#{}:预编译处理

${}:字符直接替换

预编译处理是指:MyBatis 在处理#{}时,会将 SQL 中的 #{} 替换为?号,使⽤ PreparedStatement 的 set ⽅法来赋值。直接替换:是MyBatis 在处理 ${} 时,就是把 ${} 替换成变量的值。

这里我们可以观察一下具体的sql语句:

当我们上述代码使用#{}时,得到的sql如下

 

 可以看到id确实被?替代了,而当我们使用${}则会是以下的结果

id的数据直接被写在了sql中.虽然这种方式简单粗暴,但是他也可能会带来越权访问及sql注入的问题,我们下面会讲到。

4.1.2 ${} 优点

虽然${}是简单粗暴的替换可能会带来诸多意外,但是存在即合理。

我们看到下面的代码:

<select id="getAllBySort" parameterType="java.lang.String" resultType="com.
example.demo.model.User">
 select * from userinfo order by id ${sort}
</select>

使用 ${sort} 可以实现排序查询,⽽使⽤ #{sort} 就不能实现排序查询了,因为当使用 #{sort} 查询时, 如果传递的值为 String 则会加单引号,就会导致 sql 错误。因为此时传递的值是desc或者asc,控制升序或者降序。但是#{}会自动将它识别成String类型自动加上单引号导致sql语句出错。

4.1.3 SQL注入问题 

我们看到下面的查询语句

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

而假如我们用“' or 1='1”来去替代pwd中的参数,此时pwd恒为真值,所以无论密码正确与否我们都能通过这种方式来获取到用户的信息,这就是所谓的SQL注入

所以这就提醒我们如果迫不得已需要使用${},那么我们一定要在前端以及controller做好参数合法性校验,避免发生SQL注入的问题。 

4.1.4 like查询 

对于like我们如果直接使用#{}去传参数是会报错的

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

此时的SQL语句相当于:

select * from userinfo where username like '%'username'%';

注意%附近的两个单引号就是报错的根源,还是因为#{}将其识别成String类型自动加上了单引号导致的。但是在这里我们可以通过使用mysql 的内置函数 concat() 来处理,实现代码如下:

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

 4.2 多表查询

如果是增、删、改返回搜影响的⾏数,那么在 mapper.xml 中是可以不设置返回的类型的,然而即使是最简单查询⽤户的名称也要设置返回的类型,否则会出现错误。

对于 查询标签来说⾄少需要两个属性:

id 属性:⽤于标识实现接⼝中的那个⽅法;

结果映射属性:结果映射有两种实现标签:<resultType>和<resultMap>。

4.2.1 resultType 

绝⼤数查询场景可以使⽤ resultType 进⾏返回,如下代码所示:

<select id="getNameById" resultType="java.lang.String">
     select username from userinfo where id=#{id}
</select>

它的优点是使⽤⽅便,直接定义到某个实体类即可

4.2.2 resultMap

resultMap 使⽤场景:

字段名称和程序中的属性名不同的情况,可使⽤ resultMap 配置映射;

⼀对⼀和⼀对多关系可以使⽤ resultMap 映射并查询数据。

字段名和属性名不同的情况 :
有时我们数据库中的列名和类中的属性名并对应不上(比如列名叫name属性名叫username),此时如果我们的select语句仍然使用resultType作为结果映射属性会导致查询的值为null,此时我们就需要用到resultMap了。

<!-- 当数据库列名和类的属性名对不上时需要使用resultMap,否则使用resultType即可 -->
    <resultMap id="BaseMap" type="com.example.demo.model.UserInfo">
        <!-- 主键映射 -->
        <!-- column数据库代表列名,property代表对应类属性名 -->
        <id column="id" property="id"></id>
        <!-- 普通属性映射 -->
       <result column="username" property="username"></result>
        <result column="password" property="password"></result>
        <result column="photo" property="photo"></result>
        <result column="createtime" property="createtime"></result>
        <result column="updatetime" property="updatetime"></result>
        <result column="state" property="state"></result>
    </resultMap>

    <select id="getUserById" resultMap="com.example.demo.mapper.UserMapper.BaseMap">
         select * from userinfo where id=#{id}
    </select>

其中 id="BaseMap"是我们的标识,type代表要映射的实体类

4.2.3 多表查询一对一关系 

在多表查询时,如果使⽤ resultType 标签,在⼀个类中包含了另⼀个对象是查询不出来被包含的对象 的,比如以下实体类:

假如我们想要查询一篇文章所对应的作者,但是文章来自于文章表,而作者来自于用户表,此时就涉及到了我们的多表查询了。而又因为文章对应的作者是唯一的,所以是一对一的关系映射。但是看到这里你可能会说那之前在SQL中写多表查询不久好了吗,为什么还要特地拿出来说呢?因为我们如果只是简单地去使用多表查询语句是不行的,而resultMap只映射了一个类的属性,而多表查询设计了多个类的不同属性,所以没被映射到的那个类的属性就会为null。

 此时我们就需要使⽤特殊的⼿段来实现联表查询了。


⼀对⼀映射要使用<association>标签,具体实现如下(⼀篇文章只对应⼀个作者):

 <resultMap id="BaseMap" type="com.example.demo.model.ArticleInfo">
        <id property="id" column="id"></id>
        <result property="title" column="title"></result>
        <result property="content" column="content"></result>
        <result property="createtime" column="createtime"></result>
        <result property="updatetime" column="updatetime"></result>
        <result property="uid" column="uid"></result>
        <result property="rcount" column="rcount"></result>
        <result property="state" column="state"></result>
        <association property="user"
                     resultMap="com.example.demo.mapper.UserMapper.BaseMap"
                     columnPrefix="u_">
        </association>
    </resultMap>
    <select id="getAll" resultMap="BaseMap">
        select a.*,u.username u_username from articleinfo a
            left join userinfo u on a.uid=u.id
    </select>

此处关于<association>标签的具体属性说明:


property 属性:指定 Article 中对应的属性,即⽤户。

resultMap 属性:指定关联的结果集映射,将基于该映射配置来组织⽤户数据。

columnPrefix 属性:绑定⼀对⼀对象时,是通过columnPrefix+association.resultMap.column 来映射结果集字段。 association.resultMap.column是指 标签中 resultMap属性,对应的结果集映 射中,column字段。

注意:此处的columnPrefix 属性不能省略,如果省略当联表中如果有相同的字段,那么就会导致查询出错。 比如这里我们ArticleInfo类里有文章id,UserInfo类里也有一个用户id,他们都叫id,如果不加以区分那么这两个id就会被互相给覆盖导致查询结果错误。columnPrefix主要就是通过一个前缀来区分它们的不同。

4.2.4 多表查询一对多关系

比如我们要查询一个作者所写的文章,一个作者可以对应很多篇文章,很显然这是一个一对多的关系映射。实际上一对多关系的查询和一对一关系查询的区别仅仅就是将<association>标签换成了<collection>标签。具体代码如下:

    <resultMap id="BaseMap" type="com.example.demo.model.User">
        <id column="id" property="id" />
        <result column="username" property="username"></result>
        <result column="password" property="password"></result>
        <result column="photo" property="photo"></result>
        <collection property="alist" resultMap="com.example.demo.mapper.ArticleI
nfoMapper.BaseMap"
                    columnPrefix="a_">
        </collection>
    </resultMap>
    <select id="getUserById" resultMap="BaseMap">
        select u.*,a.title a_title from userinfo u
            left join articleinfo a on u.id=a.uid where u.id=#{id}
    </select>

5.动态SQL

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

这里放上官方的参考文档mybatis – MyBatis 3 | 动态 SQL

5.1 为什么需要动态SQL

想象一下一个网址在注册的时候总是有一些用户信息是必填的,而有些是非必填的。最后这些信息都将被存储在数据库中,那如果在添加⽤户的时候有不确定的字段传⼊,程序应该如何实现呢?如果我们仅仅是在前端或者后端进行参数判断不仅麻烦而且也不好处理sql语句,此时就需要用到我们的动态sql了。

5.2<if>标签

比如针对我们上面说的那种情况,我们就可以使用<if>标签去解决。比如添加的时候头像photo 为非必填字段,具体实现如下:

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

注意这里传入的是对象的属性而不是数据库中的字段。

5.3<trim>标签 

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

为什么不直接使用多个<if>标签呢?

假如我们都使用<if>标签的话就可能会出现多余的逗号之类导致sql语句错误

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

 比如上面的语句如果我们只使用<if>标签去判断,而此时我们只输入了username和password两个属性,那么password后面多余的一个逗号就会导致sql报错。而配合<trim>标签使用就可以帮助我们解决这个问题。

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

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

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

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

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


而对于我们上述sql来说,会将第⼀个 部分做如下处理:

基于 prefix 配置,开始部分加上 (

基于 suffix 配置,结束部分加上 )

多个<if>组织的语句都以 , 结尾,在最后拼接好的字符串还会以 , 结尾,会基于 suffixO verrides 配置去掉最后⼀个 , 。

5.4 <where>标签 

<where>标签就是用在条件查询语句中的,如果用户属性不为null,都为查询条件。

select id="getUserById" resultMap="BaseMap">
        select * from userinfo
        <where>
            <if test="id!=null">
                id=#{id}
            </if>
            <if test="name!=null">
                and username=#{name}
            </if>
        </where>
    </select>

使用<where>标签的好处就是它会自动帮我们处理多余的and前缀。

以上<where>标签也可以使⽤ <trim prefix="where" prefixOverrides="and">替换。

5.5 <set>标签

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

<update id="update">
        update userinfo
        <set>
            <if test="name!=null">
                username=#{name},
            </if>
            <if test="password!=null">
                password=#{password},
            </if>
            <if test="photo!=null">
                photo=#{photo}
            </if>
        </set>
        where id=#{id}
    </update>

它和<trim>标签类似,同样可以处理多余的逗号后缀,所以此处我们也可以使用<trim>标签。

5.6 <foreach>标签 

提到foreach我们主要会想到Java通过foreach来遍历集合,事实上在MyBatis中同样如此。

<foreach>标签的常见属性

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

item:遍历时的每⼀个对象

open:语句块开头的字符串

close:语句块结束的字符串

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

比如根据多个用户 id来删除用户数据。 

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

此处的ids我们可以理解成集合的名字,而id类似Java中我们使用foreach遍历集合的变量名。

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

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

相关文章

【PyTorch】Torchvision

文章目录三、Torchvision1、Dataset2、DataLoader2.1 test_data2.2 test_loader2.3 drop_last2.4 shuffle三、Torchvision PyTorch官网&#xff1a;https://pytorch.org 1、Dataset 数据集描述&#xff1a;https://www.cs.toronto.edu/~kriz/cifar.html 数据集使用说明&#…

SpringCloud案例day05

SpringCloud Gateway网关 案例1&#xff1a;环境搭建 可以复制 Service-A9001 改成Gateway-C9009 》1&#xff1a;创建工程导入依赖 <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artif…

汽车全景视频制作让企业营销传播无界长久

全景视频制作 全景VR视频&#xff0c;顾名思义就是能够使我们看到拍摄点周围360景致的视频。传统视频拍摄受限于镜头视角&#xff0c;所以我们只能看到镜头前方180内的景物。而全景VR视频能够看到周围360以及上下180各个角度的一切景物&#xff0c;用户能够更加多方位的观赏视频…

可观测性-Event-指标事件采样策略总结

文章目录前言采样的几种策略简单随机采样周期性完整采样基于条件的采样前言 在默认情况下&#xff0c;系统会采集所有追踪&#xff08;Tracing&#xff09;的数据。但是如果系统比较复杂&#xff0c;采集的端点比较多的时候&#xff0c;对存储压力比较大&#xff0c;这个时候我…

Educational Codeforces Round 135 (Rated for Div. 2)

A:思维 题意&#xff1a;箱子里有N个颜色的球&#xff08;用下标代表不同的颜色&#xff09;&#xff0c;每个颜色的球对应一定的数量&#xff0c;你会进行多次拿球的操作&#xff0c;当箱子里的球颜色一致时&#xff0c;你就不能再拿球了&#xff0c;问最后颜色一致的球是哪个…

免疫抑制作用的转录抑制分子

科研人员发现&#xff1a;社交失败导致了小鼠焦虑行为&#xff0c;进而抑制了化疗药物的治疗过程&#xff0c;加速了肿瘤的发展。在正常小鼠中&#xff0c;人为给予外源性糖皮质激素会使得 Tsc22d3(一个具有很强免疫抑制作用的转录抑制分子)在肿瘤浸润树突细胞(TIDC)的表达上调…

Q3营收同比翻三倍,踩猛“油门”零跑必将领跑?

近日&#xff0c;零跑汽车公布三季度财报。从财报成绩来看&#xff0c;有喜有忧。喜的是销量营收同比环比均出现大幅度增长&#xff0c;忧的是亏损同比扩大86.11%。 营收同比猛增398.5%&#xff0c;销量能否穿越迷雾&#xff1f; 详细来看&#xff0c;三季度零跑汽车销量3.56万…

【附源码】计算机毕业设计JAVA旅行指南网站

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; Springboot mybatis Maven Vue 等等组成&#xff0c;B/…

HOOPS学习笔记

Hoops简介 HOOPS Visualize由一套集成的组件组成&#xff0c;可快速开发高性能设计、可视化和工程应用程序。HOOPS 3DF在每个组件中保持平台独立性&#xff0c;在Windows、Linux和Mac OS X上提供跨平台解决方案。 HOOPS Visualize包含如下组件&#xff1a; 1.HOOPS/3dGS - HO…

vue+express+mysql+elementUI实现前后端交互增删改查

简介&#xff1a;使用 Vue koa koa2-router mySql elementUI实现前后端交互 页面整体效果图&#xff1a; 编辑页面&#xff1a; 添加页面&#xff1a; 删除操作&#xff1a; 数据库客户端使用Navicat for mysql 当然你需要下载安装 在数据库新建 stuInfo 表和表结构 st…

Github点赞接近 100k 的Spring Boot学习教程+实战项目推荐

很明显的一个现象&#xff0c;除了一些老项目&#xff0c;现在 Java 后端项目基本都是基于 Spring Boot 进行开发&#xff0c;毕竟它这么好用以及天然微服务友好。不夸张的说&#xff0c;Spring Boot 是 Java 后端领域最最最重要的技术之一&#xff0c;熟练掌握它对于 Java 程序…

SpringBoot-快速入门

目录 SpringBoot 概念 Spring缺点 SpringBoot 功能 快速入门案例&#xff1a;需求 案例&#xff1a;实现步骤 示例 小结 快速构建SpringBoot工程 SpringBoot 概念 SpringBoot提供了一种快速使用Spring的方式基于约定优于配置的思想&#xff0c;可以让开发人员不必在配置…

QML地图Map中使用QPainterPath,并显示任意点经纬度位置

QML地图Map中提供了供绘制图形的组件&#xff0c;例如MapPolyline&#xff0c;MapCircle等&#xff0c;但是这些组件在绘制复杂轨迹时就显得功能不够全面&#xff0c;因此我将QPainterPath在Map中进行使用并进行绘制&#xff0c;并使用C和Qml中的函数进行相互调用计算获取点屏幕…

macOS 的「预览」有几种用法

如果要评选 macOS 最低调的 App &#xff0c;「预览」可能就会位列其中之一。「预览」和 macOS 结合异常紧密&#xff0c;甚至让人都会遗忘它还是个 App。毕竟大多数的时候&#xff0c;提到「预览」就会想到操作系统的一部分。而实际上&#xff0c;「预览」那「瘦小」的身躯之内…

Unity3D赛车游戏+脚本基础

前言 游戏对象实例化 Scenes游戏场景 GameObject游戏对象 Component组件 Component使用方法 预制体 Unity事件函数 Instantiate():实例化对象 什么是Time.deltaTime Transform的移动&#xff0c;旋转和缩放 实战:赛车游戏 运行演示 具体步骤 游戏打包流程 前言 …

智慧教育解决方案-最新全套文件

智慧教育解决方案-最新全套文件一、建设背景二、思路架构三、建设方案四、获取 - 智慧教育全套最新解决方案合集一、建设背景 目前数字化校园现状&#xff1a; 各自为政 分散建设 各个学校组成的业务系统、网站五花八门&#xff0c;形式多样。形式单一 功能简单 学校网站主要…

为什么同一表情‘‘.length==5但‘‘.length==4?本文带你深入理解 String Unicode UTF8 UTF16

背景 为什么同样是男人&#xff0c;但有的男人&#x1f9d4;‍♂️.length 5&#xff0c;有的男人&#x1f9d4;‍♂.length 4呢&#xff1f; 这二者都是JS中的字符串&#xff0c;要理解本质原因&#xff0c;你需要明白JS中字符串的本质&#xff0c;你需要理解 String Unic…

vlan trunk stp攻防

目录 一、VLAN、Trunk面临的安全风险 trunk干道攻击DTP攻击&#xff08;思科特有&#xff09; VLAN跳跃攻击 STP根桥攻击 二、攻击防护 一、VLAN、Trunk面临的安全风险 trunk干道攻击DTP攻击&#xff08;思科特有&#xff09; 在华为设备中trunk链路是手工指定的&#xf…

A-LEVEL Chemistry考点分析

A-LEVEL化学其实不是一门很难的科目&#xff0c;并没有太多的内容&#xff0c;虽说包含十几章的内容但其实每章都是相互关联&#xff0c;一通百通&#xff0c;掌握元素周期表的实质&#xff0c;基本上就没有什么问题了&#xff0c;重在理解&#xff01;知识点多而不碎&#xff…

如何一站式管理固定资产的全生命周期?

目前很多企业还在沿用之前传统的Excel表格来管理和盘点固定资产&#xff0c;不但加大了企业固定资产管理员的工作量&#xff0c;而且在实际的操作过程中容易出现错登记、漏盘点、无法查询操作履历等各种小插曲。而且随着企业固定资产数量、种类、分支机构以及人员的不断增加&am…