MyBatis(该篇足已)

news2024/11/29 10:39:37

目录

一.MyBatis是什么?

二.为什么学习MyBatis呢?

三.MyBatis的学习

3.1MyBatis的开发流程

3.2MyBatis项目

四.MyBatis的增删改操作

五.参数占位符 #{} 和 ${}

六.映射返回

七.映射失败

八.数据库连接池

九.动态SQL

9.1<if>标签

9.2<trim>标签

9.3<where>标签

9.4<set>标签

9.5<foreach>标签

9.6<include>标签


一.MyBatis是什么?

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

ORM介绍:

        ORM代表对象关系映射(Object-Relational Mapping),它是一种编程技术,用于在关系数据库和面向对象编程语言之间建立一种映射关系,从而将数据库中的数据以对象的形式进行操作和访问。

半自动的原因:

  1. 手动编写SQL语句:与全自动的ORM框架不同,MyBatis需要开发者手动编写SQL语句或者使用XML配置文件来定义SQL映射。这使得开发者可以更精确地控制SQL查询的逻辑,包括优化查询性能、处理复杂的查询需求等。

  2. 灵活的映射配置:MyBatis允许开发者通过XML配置文件或者注解来定义对象与数据库表之间的映射关系,包括字段映射、关联关系等。这种灵活性使得开发者可以根据具体需求进行定制化配置。

  3. 不强制使用领域模型:MyBatis不强制要求开发者使用特定的领域模型,开发者可以自由选择使用普通的Java对象或者自定义的POJO(Plain Old Java Object)作为持久化对象。这种灵活性使得开发者可以更加灵活地设计应用程序的数据模型。

二.为什么学习MyBatis呢?

        对于后端人员,获取数据库的信息,是需要从后端程序当中获取,而如何获取,最常用的方法就是JDBC方法!

往常使用JDBC的步骤:

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

        从上述代码和操作流程可以看出,对于 JDBC 来说,整个操作⾮常的繁琐,我们不但要拼接每⼀个参 数,而且还要按照模板代码的⽅式,⼀步步的操作数据库,并且在每次操作完,还要⼿动关闭连接等, 而所有的这些操作步骤都需要在每个⽅法中重复书写。

因此我们需要学习更为简便的MyBatis操作数据库

注:如果你硬要说可以使用ComboPooledDataSource也可以简化代码,我就不想用MyBatis,那你就杠吧!

三.MyBatis的学习

3.1MyBatis的开发流程

        MyBatis 也是⼀个 ORM 框架, ORM(Object Relational Mapping),即对象关系映射。在⾯向对象编程语言中,将关系型数据库中的数据与对象建立起映射关系,进而自动的完成数据与对象的互相转换:
  1. 将输⼊数据(即传⼊对象)+SQL 映射成原生 SQL
  2. 将结果集映射为返回对象,即输出对象

所谓的映射(数据库<-->对象):

  • 数据库表(table)--> 类(class)
  • 记录(record,⾏数据)--> 对象(object)
  •  字段(field) --> 对象的属性(attribute)
流程图

        调用者向Controller层请求数据,因此会逐层调用到Mapper层,Mapper层通过接口和配置文件.xml来进行调用数据库的数据。因此很明显,我们需要配置的环境有两个地方,一个是xml配置文件,一个是接口的定义。

当然,我们需要在application.yml当中配置好数据库连接配置 ,后期在创建项目当中会有详细讲解。

3.2MyBatis项目

        第一步.我们创建一个SpringBoot项目。并且勾选好需要的依赖!

问:或许有人问,我都添加了MyBatis依赖,我为什么还要添加MySql驱动呢?   

答:MyBatis只是一个持久层框架,它本身并不包含数据库驱动,而是依赖于特定的数据库驱动来连接和操作数据库。MySQL数据库驱动是一个单独的库,它负责与MySQL数据库建立连接、执行SQL语句等数据库操作。因此,你需要将MySQL数据库驱动添加到项目的依赖中,以便MyBatis能够使用它来与MySQL数据库进行交互。

第二步:创建数据库 名MyBatis,在其中搞一个表

create database MyBatis 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';

自己插入一系列数据:

第三步:配置好数据库连接的yml文件和MyBatis的xml路径

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/库名?characterEncoding=utf8&useSSL=false
    username: root
    password: 密码
    driver-class-name: com.mysql.cj.jdbc.Driver

# 配置 mybatis xml 的⽂件路径,在 resources/mapper 创建所有表的 xml ⽂件
mybatis:
  mapper-locations: classpath:mapper/**Mapper.xml

解析:设置MyBatis的xml路径,是为了告诉MyBatis去指定的位置寻找mapper文件,以便读取其中的SQL语句和映射配置

第四步:添加实体类

添加的实体类当中的属性需要和我们创建的表字段名一致,这是一致性!

import lombok.Data;
import java.util.Date;
@Data
public class User {
     private Integer id;
     private String username;
     private String password;
     private String photo;
     private Date createTime;
     private Date updateTime;
}

第五步:添加mapper接口

  数据持久层的接口定义:      

import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface UserMapper {
 public List<User> getAll();
}

第六步:添加UserMapper.xml

如果想要实现数据持久层的,我们需要添加一个Mapper的xml格式!

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybati
s.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserMapper">
 <select id="getAll" resultType="com.example.demo.model.User">
 select * from userinfo
 </select>
</mapper>

详细说明: 

标签的说明:
  • <mapper>标签:需要指定 namespace 属性,表示命名空间,值为 mapper 接⼝的全限定 名,包括全包名.类名。
  • <select>查询标签:是⽤来执⾏数据库的查询操作的:
    • id:是和 Interface(接⼝)中定义的⽅法名称⼀样的,表示对接⼝的具体实现⽅法resultType:是返回的数据类型,也就是开头我们定义的实体类。

第五、六步化作一步的方法:

        我们直接在Mapper接口上使用sql语句,执行调用。代码如下:

@Mapper
public interface UserMapper {
   @Select("select * from userinfo")
   public List<User> getAll();
}

不需要配置文件,直接调用,然后就会执行该方法

第七步:添加Service

这一步是为了Controller层调用而使用

import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
@Service
public class UserService {
 @Resource
 private UserMapper userMapper;
 public List<User> getAll() {
 return userMapper.getAll();
 }
}

第八步:Controller层不调用,直接进行测试代码

@SpringBootTest
class BlogMyBatisApplicationTests {
    @Autowired
    private UserService userService;
    @Test
    void contextLoads() {
        List<User> t1= userService.getAll();
        for(User user: t1){
            System.out.println(user.toString());
        }
    }
}

结果:

四.MyBatis的增删改操作

        MyBatis要实现增加、删除和修改的操作,对应使⽤ MyBatis 的标签如下:

  • <insert>标签:插⼊语句
  • <update>标签:修改语句
  • <delete>标签:删除语句

没有主键的插入代码:

<insert id="add2" useGeneratedKeys="true" keyProperty="id">
     insert into userinfo(username,password,photo,state)
     values(#{username},#{password},#{photo},1)
</insert>
  • useGeneratedKeys:这会令 MyBatis 使⽤ JDBC 的 getGeneratedKeys ⽅法来取出由数据 库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系型数据库管理系统的自动递增字段),默认值:false。
  • keyColumn:设置⽣成键值在表中的列名,在某些数据库(像 PostgreSQL)中,当主键列不是表中的第⼀列的时候,是必须设置的。如果生成列不止⼀个,可以⽤逗号分隔多个属性 名称。
  • keyProperty:指定能够唯⼀识别对象的属性,MyBatis 会使⽤ getGeneratedKeys 的返回值或 insert 语句的 selectKey ⼦元素设置它的值,默认值:未设置(unset)。如果生成列不止⼀个,可以⽤逗号分隔多个属性名称。

修改操作:

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

删除操作:

<delete id="delById" parameterType="java.lang.Integer">
     delete from userinfo where id=#{id}
</delete>

parameterType:表示的是删除的行数。

五.参数占位符 #{} 和 ${}

        在增删查找里,我们可以发现我们使用了#{} 或者 ${},那么两者之间的区别?

  • #{}:预编译处理。
        预编译处理是指:MyBatis 在处理 #{} 时,会将 SQL 中的 #{} 替换为 ? 号,使⽤ PreparedStatement的 set ⽅法来赋值。
  • ${}:字符直接替换。
    直接替换:是MyBatis 在处理 ${} 时,就是把 ${} 替换成变量的值。如果是字符串类型,则会自动加单引号。容易SQL 注入攻击

或许你看不懂,但是我直接举例几种常见的情况:

情况一:使用${}的情况下,字符串会变成单引号:

UserMapper接口:

@Mapper
public interface UserMapper {
    User get(String name);
}

Service服务层:

@Service
public class UserService {
    @Resource
    private UserMapper userMapper;
    public User get(String name) {
    return userMapper.get(name);
    }
}

配置文件:

测试方法:

    @Autowired
    private UserService userService;
    @Test
    void contextLoads() {
        String name = "tq02";
        User t1= userService.get(name);
        System.out.println(t1.toString());
    }

测试结果:

解析:获取tq02字符串时,实际获取的是 'tq02' ,而不是tq02.想要避免这种情况,从配置文件入手,在${name}上,添加一个单引号:

    <select id="get" resultType="com.example.blog_mybatis.model.User">
        select * from userinfo where username = '${name}';
    </select>

情况二:使用${}的情况下,却不传递任何值

 配置文件:

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

Mapper接口:

@Mapper
public interface UserMapper {
    void getAllBySor();
}

编译结果:

        当 ${sort} 没有传递数据时,MyBatis 会将 ${sort} 原封不动地插入到 SQL 查询语句中,导致最终执行的 SQL 查询语句:select * from userinfo order by id

情况三:使用${}的情况下,却乱传递值

配置文件 UserMapper.xml

<select id="getUsersByCondition" resultType="User">
    SELECT * FROM user WHERE name = '${name}'
</select>

接口还是第一种的方式,但是我传递的值如果是  tq03' or '1' = '1  呢?

你会发现返回值不再是一个User类,而是表的全部结果,因为 or '1' = '1 ' 总是为真

而这种情况就是SQL 注入攻击


情况四:使用#{}的情况下,传递值为String类型

和情况一,一样,只不过把#变成$。

你会发现,会正常运行,返回数据。

情况五:使用${}的情况下,不传递值

会报错,sql语句会显示?

情况五:使用#{}的情况下,胡乱传递

与情况三的操作一致,只不过唯一的区别是,$变为了# ,就会变成:

SELECT * FROM userinfo WHERE username = 'tq02\' OR \'1\'=\'1'

这种情况下\’会变成空字符:也就是后半部分为:username = ' tq02' OR 1'=1' '


在传统的 SQL 查询中,通常使用单引号 ' 来表示字符串的开始和结束因此,如果在字符串中需要包含单引号本身,需要使用转义字符 \ 来转义,表示单引号不是字符串的结束,而是字符串的一部分。

 表中数据存在时:

只不过在数据库查询时,我们不能直接使用单引号,我们需要转义字符 \’ 

情况六:使用#{}的情况下,模糊查询 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'%';

这种情况也不能使用${}的,我们需要使用内置函数concat() 来处理,

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

六.映射返回

1.增、删、改返回的是影响的行数,但是在mapper.xml 中是可以不设置返回的类型。但如果是数据,例如是String类型的数据,不返回就报错!

正确情况下,使用resultType:

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

2.parameterType和resultType:

   parameterType可以来明确指定传递的参数类型,但是简单情况下,可以不设置,MyBatis 会尝试根据传递的参数来推断参数类型,然后将参数传递给 SQL 语句。

特殊情况下:

 <update id="update" parameterType="java.lang.Integer">
        update userinfo set username=#{name} where id=#{id}
    </update>

这种情况下,指定传递的参数类型为int,但是我的username却是String类型啊

解析:尽管 parameterType 被设置为 java.lang.Integer,但你仍然可以在 SQL 语句中引用其他类型的参数,只要参数名正确匹配即可。因此,设置 parameterType 的目的是为了提高代码的可读性和可维护性,以及防止类型不匹配或错误的结果发生。

        ResultType用于指定查询结果的类型的属性。在 MyBatis 中, ResultType 用于映射 SQL 查询的结果到 Java 对象或基本数据类型,和parameterType不一样,当需要返回值不为影响的行数时,必须写上去。

3.返回字典映射:resultMap

resultMap 使用场景:
  • 字段名称和程序中的属性名不同的情况,可使⽤ resultMap 配置映射;
  • ⼀对⼀和⼀对多关系可以使⽤ resultMap 映射并查询数据。

我们的表中字段和数据库字段名不同

而类中实际的属性:

对比发现:表中的password变成了pas,那我们进行查询时,这个属性是永远装到不表中的password数据,

此时此刻,我们为了让这个类和我们的数据表匹配,可以使用:resultMap

<resultMap id="BaseMap" type="com.example.demo.model.User">
 <id column="id" property="id"></id>
 <result column="username" property="username"></result>
 <result column="password" property="pwd"></result>
</resultMap>

此时此刻,我们查询就有了结果的。

一对一的表映射的情况

        换句话,一对一的映射就是指,一个类和一张表对应,只不过,我们使用一些标签使其属性和字段名映射成功。

<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.* from articleinfo a
 left join userinfo u on u.id=a.uid
 </select>

如上图,我们使用了<association>标签,表示⼀对⼀的结果映射:

  • property 属性:指定 Article 中对应的属性,即⽤户。
  • resultMap 属性:指定关联的结果集映射,将基于该映射配置来组织⽤户数据。
  • columnPrefix 属性:指定了关联查询中,关联表(userinfo表)的列名前缀,效果类似 as

注:columnPrefix 属性不能省略,原因:省略当联表中如果有相同的字段,那么就会导致查询出来的列名会有重复。重复情况下:

id列有2行,可见性差。

一对多的情况下:

        一对多指的是一个类中有一个列表,而数据库却不能存在改字段:

例如:两个实体类 UserArticle

public class User {
    private Long id;
    private String username;
    private List<Article> articles; // 用户拥有的文章列表,使用集合类型表示
    // 省略其他属性和方法
}

public class Article {
    private Long id;
    private String title;
    private Long userId; // 文章所属用户的ID,用于关联查询
    // 省略其他属性和方法
}

假设你有两表,结构如下:

  •  uer_info表包含用户信息:

    • id
    • username
  • article_info 表包含文章信息:

    • id
    • title
    • user_id (对应用户表中的 id)

七.映射失败

        当数据库的表名和属性不一致时,有三种解决方法:

  1. 起别名
  2. 结果映射 ,第6部分写了详细内容,这里再举其他方式
  3. 开启驼峰命名

起别名:

@Select("select id, username, 'password', age, gender, phone, delete_flag as del
 "create_time as createTime, update_time as updateTime from userinfo")
 public List<UserInfo> queryAllUser();

结果映射:

 @Select("select delete_flag, creat_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> queryAllUser();

colum:表示数据库的字段,porperty表示类的属性,

如果我查询很多语句,那我岂不是要重写很多段@Results注解?

便利方法:

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

开启驼峰命名

        数据库字段和类属性的不一致,原因:数据库列使⽤蛇形命名法进⾏命名(下划线分割各个单词), ⽽ Java 属性⼀般遵循驼峰命名法约定.

解决方法:为了在这两种命名⽅式之间启⽤⾃动映射,需要将 mapUnderscoreToCamelCase 设置为 true。

 mybatis:
    configuration:
        map-underscore-to-camel-case: true #配置驼峰⾃动转换

八.数据库连接池

        连接池介绍: 数据库连接池负责分配、管理和释放数据库连接,它允许应⽤程序重复使⽤⼀个现有的数据库连接。⽽不是再重新建⽴⼀个(开销太大了)

 

在常见的连接池当中,我们常用的几种:

  1. C3P0
  2. DBCP
  3. Druid
  4. Hikari

在MyBatis当中,以及内置好了连接池,可以直接使用,而默认的是Hikari,证据如下:

如果我们想使用其他连接池,可以通过加载对应的jar包。

九.动态SQL

        动态 SQL 在 MyBatis 中的存在是为了处理那些需要在运行时根据条件动态生成的 SQL 语句。这种特性允许我们在一个映射文件中编写复杂的 SQL 查询,并根据不同的条件生成不同的 SQL 语句,而不需要写多个相似的 SQL 查询。

9.1<if>标签

例如,查询操作,当你查询某个人时,可以通过姓名、性别、年龄等查询,但查询时,我们难道应该写多个sql语句吗?进行分别查询吗?

nonono!

我们可以直接使用动态语句:

<select id="getUsers" parameterType="map" resultType="User">
    SELECT * FROM user_info
    where
        <if test="username != null">
            AND username = #{username}
        </if>
        <if test="sex != null">
            AND sex = #{sex}
        </if>
        <if test="age != null">
            AND age = #{age}
        </if>
</select>

<if> 标签根据传入的参数来动态生成 WHERE 子句,如果 usernamesex和age 参数哪些存在,则会同时根据存在的条件查询用户信息。

9.2<trim>标签

        和<if>标签搭配使用,当你查询或者插入某个人的信息时,如果没有可查询的信息或者插入,则不会使用trim标签:

<select id="getUsers" parameterType="map" resultType="User">
    SELECT * FROM user_info
    <trim prefix="WHERE" prefixOverrides="AND | OR" suffixOverrides="AND | OR">
        <if test="username != null">
            AND username = #{username}
        </if>
        <if test="email != null">
            AND email = #{email}
        </if>
    </trim>
</select>
  • prefix:表⽰整个语句块,以prefix的值作为前缀,如果数据存在,则添加在sql首部
  • suffix:表⽰整个语句块,以suffix的值作为后缀,如果数据存在,则加入sql语句尾部
  • prefixOverrides:表⽰整个语句块要去除掉的前缀,如果首部是约定的存在,则删除
  • suffixOverrides:表⽰整个语句块要去除掉的后缀,如果尾部部是约定的存在,则删除

当username和email不存在时,sql语句就相当于:

        SELECT * FROM user_info

当username不存在,email存在时,sql语句相当于:

        SELECT * FROM user_info where email = #{email};

当username和email存在时,sql语句就相当于:

      SELECT * FROM user_info where  username = #{username}   and   email =  #{email};

9.3<where>标签

        和<trim>标签有异曲同工之妙,不过它只能限定where。

<select id="queryByCondition" resultType="com.example.demo.model.UserInfo">
 select id, username, age, gender, phone, delete_flag, create_time, update_ti
     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>
<where> 只会在⼦元素有内容的情况下才插⼊where⼦句,⽽且会⾃动去除⼦句的开头的AND或
OR

9.4<set>标签

       <set>就是sql语句当中的修改update当中的set

<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>
<set> :动态的在SQL语句中插⼊set关键字,并会自动删掉额外的逗号. (⽤于update语句中)

9.5<foreach>标签

对集合进行遍历时可以使用该标签。标签有如下属性:
  • collection:绑定⽅法参数中的集合,如 List,Set,Map或数组对象
  • item:遍历时的每⼀个对象
  • open:语句块开头的字符串
  • close:语句块结束的字符串
  • separator:每次遍历之间间隔的字符串
这个标签和之前不太一样,因为他需要传入的是多个数据,例如多选删除, 根据多个userid, 删除用户数据。

接口方法:

void deleteByIds(List<Integer> ids);
ArticleMapper.xml 中新增删除 sql:
<delete id="deleteByIds">
     delete from userinfo
     where id in
         <foreach collection="ids" item="id" separator="," open="(" close=")">
             #{id}
         </foreach>
</delete>

通过这种方式,可以动态生成一个类似于 DELETE FROM userinfo WHERE id IN (id1, id2, id3) 的 SQL 语句。

9.6<include>标签

        在sql语句当中,不同的语句,但是会有大量冗余的代码,例如:我们查询所有用户信息、查询指定用户信息、查询条件用户信息,但是他们查询的字段名都是一样的,那么我们能不能简便一点呢?

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


<select id="queryById" resultType="com.example.demo.model.UserInfo">
select id, username, age, gender, phone, delete_flag, create_time, update_time
from userinfo where id= #{id}

</select>


<select id="queryByCondition" resultType="com.example.demo.model.UserInfo">
select id, username, age, gender, phone, delete_flag, create_time, update_time  from userinfo  where  id = #{id}  and age >18;

解决方法:

抽取重复代码片段,通过<sql>标签封装到SQL片段,再使用<include>标签引用。

  • <sql> :定义可重⽤的SQL⽚段
  • <include> :通过属性refid,指定包含的SQL⽚段
<sql id="allColumn">
 id, username, age, gender, phone, delete_flag, create_time, update_time
</sql>

<select id="queryAllUser" resultMap="BaseMap">
     select
     <include refid="allColumn"></include>
     from userinfo
 </select>

结尾小注:要动起你们的小手,敲哦!

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

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

相关文章

JavaSE——异常(2/2)-异常的处理(记录异常并提示 、尝试重新修复)

目录 记录异常并提示 案例演示 流程解析 写法优化 尝试重新修复 开发中对于异常的常见处理方式 一层一层往上抛出异常&#xff0c;并且在最上层捕获异常&#xff0c;分为两种不同的处理方式。 例如&#xff0c;B站网页报错就是采取的第一种方式&#xff1a; 记录异常并…

擎天科技与禅道合作,打造统一的项目管理平台

统一、全面的项目管理平台能够帮助企业优化管理流程&#xff0c;提升业务效率。擎天集团选择与禅道软件合作&#xff0c;打造统一的项目管理平台&#xff0c;在降低自研软件的研发成本、打破团队信息孤岛、保障数据全面性等方面效果显著&#xff0c;大大提高了团队沟通协作效率…

NL6621 WIFI模块烧录及其他

某宝淘得NL6621: 测了一下引脚&#xff1a; 做了以下功课&#xff1a; 新岸线物联网NL6621解决方案是高性价比、完全开源、高成熟度的解决方案&#xff0c;特别为高数据吞吐率低成本的无线局域网产品而设计。它集成了MCU&#xff0c; MAC&#xff0c;1T1R基带和带功放RF收发机于…

【JavaEE网络】HTTP响应详解:状态码、报头与正文的全面解析

目录 HTTP响应&#xff08;Response&#xff09;认识 "状态码" (status code)认识响应 “报头”&#xff08;header&#xff09;认识响应 “正文”&#xff08;body&#xff09; HTTP响应&#xff08;Response&#xff09; 响应&#xff1a; 首行响应头空行正文 认…

互动科技如何强化法治教育基地体验?

近年来&#xff0c;多媒体互动技术正日益融入我们生活的各个角落&#xff0c;法治教育领域亦不例外。步入法治教育基地&#xff0c;我们不难发现&#xff0c;众多创新的多媒体互动装置如雨后春笋般涌现&#xff0c;这些装置凭借前沿的科技手段&#xff0c;不仅极大地丰富了法制…

Kubernetes学习-集群搭建篇(二) 部署Node服务,启动JNI网络插件

目录 1. 前言 2. 部署Node服务 2.1. 前置环境安装 2.2. 将Node服务加入集群 3. 部署JNI网络插件 4. 测试集群 5. 总结 1. 前言 我们在上一篇文章完成了Matster结点的部署&#xff0c;这次我们接着来部署Node服务&#xff0c;注意&#xff0c;我Node服务是部署在另外两台…

java 文件表创建及前后端使用

表结构task_file 前端具体到业务表单 <el-form-item label"任务附件" prop"taskAttachment"><el-upload ref"upload" accept".jpg, .png, .txt, .xlsx, .doc, .docx, .xls, .pdf, .zip, .rar":action"upload.url" …

(三)小程序样式和组件

视频链接&#xff1a;尚硅谷2024最新版微信小程序 文章目录 小程序的样式和组件介绍样式-尺寸单位 rpx样式-全局样式和局部样式组件-组件案例演示组件案例-轮播图区域绘制组件案例-轮播图图片添加组件案例-绘制公司信息区域组件案例-商品导航区域组件案例-跳转到商品列表组件案…

Java实现 selenium Web自动化测试(简单篇)

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

LifeCycle之ProcessLifeCycleOwner

问题&#xff1a;想要知道应用程序当前处在前台、后台、或从后台回到前台&#xff0c;想要知道应用的状态&#xff0c; LifeCycle提供了ProcessLifeCycleOwner的类&#xff0c;方便我们知道整个应用程序的生命周期情况 ProcessLifeCycleOwner 使用方法 1.首先添加依赖 imple…

Flutter 首次亮相 Google Cloud Next 大会

作者 / Kelvin Boateng Flutter 团队在近期首次参加了 Google Cloud Next 大会&#xff0c;这意味着 Flutter 在开发社区中的影响力正在日益增长。 Google Cloud Next https://cloud.withgoogle.com/next 我们与 Google Cloud、Firebase、Very Good Ventures 和 Serverpod 的团…

深化产教融合,泰迪智能科技助力西南林业大学提质培优

2024年5月7日&#xff0c;泰迪智能科技昆明分公司院校部总监查良红和数据部负责人余雄亮赴西南林业大学理学院就工作室共建事宜进行交流会谈。西南林业大学理学院院长张雁、党委副书记魏轶、副院长谢爽、就业负责人罗丽及学生代表参与本次交流会。 会议伊始&#xff0c;谢副院长…

C++语法|进程虚拟地址空间和函数调用栈

本文来自施磊老师的课程&#xff0c;老师讲的非常不错&#xff0c;我的笔记也是囫囵吞枣全部记下&#xff0c;但是我在这里推荐一本书&#xff0c;真的真的建议初学C或者想要进阶C的同学们看看&#xff1a;《CPU眼里的C/C》 文章目录 进程的虚拟地址空间和布局进程虚拟地址空间…

服务异步通讯MQ

同步调用存在的问题: 异步调用方案: RabbitMQ安装: 第一种:在线拉取 docker pull rabbitmq:3-management 第二种:将已有的安装包放入再用load加载 我这里放到tmp包里边 然后:cd /tmp docker load -i mq.tar 加载进去 然后运行mq容器 docker run \-e RABBITMQ_DEFAULT_USER…

tag-字符串:数组拆分I

题目 给定长度为 2n 的整数数组 nums &#xff0c;你的任务是将这些数分成 n 对, 例如 (a1, b1), (a2, b2), …, (an, bn) &#xff0c;使得从 1 到 n 的 min(ai, bi) 总和最大。 返回该 最大总和 。 示例 题解一 class Solution:def arrayPairSum(self, nums: List[int]) …

@Test测试Mapper接口报错java.lang.NullPointerException

Test测试Mapper接口报错java.lang.NullPointerException 报错原因&#xff1a;没有注入依赖 解决方法&#xff1a;在测试类上面添加SpringBootTest

windows 环境下安装《车辆动态监控系统》支持JT808、JT1078、苏标主动安全设备接入

《车辆动态监控系统》下载安装部署包 开放端口 80/443/8800&#xff0c;web后台端口&#xff0c;nginx代理服务&#xff0c;nginx默认为8800端口8808&#xff0c;JT808专用端口6802&#xff0c;视频播放推流端口6891-6898&#xff0c;FTP端口6821&#xff0c;苏标主动安全附件…

[GESP样题 四级] 绝对素数

B3939 [GESP样题 四级] 绝对素数 题目 如果一个两位数是素数&#xff0c;且它的数字位置经过对换后仍为素数&#xff0c;则称为绝对素数&#xff0c;例如 13。给定两个正整数 A, B&#xff0c;请求出大于等于 A&#xff0c;小于等于 B 的所有绝对素数。 输入 1 行&#xff0…

LPDDR5电路设计的新功能

最近因为需要使用到LPDDR5&#xff0c;快速地浏览了JEDEC标准文档&#xff0c;发现与前几代相比出现了一些新的电路设计功能&#xff0c;总结为如下三点&#xff1a; 1. CK/WCK/RDQS时钟方案&#xff1b; 2. 电源的PDN设计目标&#xff1b; 3. DQ, DMI和RDQS的Rx端DFE均衡技术。…

便宜的智能组网系统有哪些?

随着物联网的迅猛发展&#xff0c;智能设备的普及与应用也日益增多。不同地区的智能设备之间的互联通信仍然存在着很多困难和挑战。其中一个主要问题是如何实现便宜而高效的智能组网。在这篇文章中&#xff0c;我们将介绍一款名为【天联】的组网产品&#xff0c;它是北京金万维…