【JavaEE进阶】MyBatis表查询

文章目录

  • 一. 使用MyBatis完成数据库的操作
    • 1. MyBatis程序中sql语句的即时执行和预编译
      • 1.1 即时执行(${})
      • 1.2 预编译(#{})
      • 1.3 即时执行和预编译的优缺点
    • 2. 单表的增删改等操作
      • 2.1 增加操作
      • 2.2 修改操作
      • 2.3 删除操作
      • 2.4 like(模糊查询)操作
      • 2.5 实体类中的属性和数据库表中的字段名不一致出现的问题的三种解决方式
    • 3. 多表查询
      • 3.1 多表查询(一对一)
      • 3.2 多表查询(一对多)
  • 二. 动态SQL的使用
    • 1. < if >标签
    • 2. < trim >标签
    • 3. < where >标签
    • 4. < set >标签
    • 5. < foreach >标签
  • 单元测试

在上一篇博客中我们简单了解了MyBatis的创建与使用,接下来我们进一步的学习MyBatis的相关知识。
注:此博客中测试案例所使用的单元测试在文末有教程.

一. 使用MyBatis完成数据库的操作

1. MyBatis程序中sql语句的即时执行和预编译

我们在JDBC中在构造sql语句的时候,常常给字段的值用问号?代替,最后在使用方法对这些?进行赋值,这是预编译
使用预编译的好处可以防止sql注入。当然还有一种sql的执行方式就是即时执行
SQL注入是一种常见的安全漏洞,它利用了未正确过滤或转义用户输入的数据,导致恶意用户可以在执行SQL查询时插入恶意的SQL代码。
下面我们来了解一下MyBatis程序中的即使执行和预编译的构建方式.

1.1 即时执行(${})

就像下面我们写道的根据某个字段查询单个信息的时候,我们传递了参数,在xml文件中对相应的字段进行赋值的时候使用${}这种方式就是构造sql语句即时执行的方式。

    <select id="getUserById" resultType="com.example.demo.entity.UserEntity">select * from userinfo where id=${id}</select>

在这里插入图片描述
我们从运行结果看到,执行的sql语句是直接被赋值的,并没有使用?.

1.2 预编译(#{})

这种写法在程序执行的时候,我们可以看到sql语句中id的值先是被?将位置占着的。这里?表示的是只能是值,而不能是sql语句,这就防止了sql注入。

    <select id="getUserById" resultType="com.example.demo.entity.UserEntity">select * from userinfo where id=#{id}</select>

在这里插入图片描述

1.3 即时执行和预编译的优缺点

即时执行(${}):
优点:

  • 当我们逛淘宝的时候,筛选商品点击价格按照从低到高,这个时候传递的就是SQL命令。从低到高传递的就是asc,从高到低传递的就是desc。使用${}可以实现排序查询,而使用#{}就不能实现排序查询,因为当使用#{}查询时,如果传递的值为String就会加单引号,就会导致sql错误.

缺点:

  • 它的执行不安全,存在sql注入.
  • 在使用${}时,如果传入的参数是字符串类型的数据,还需要再构造sql的语句的时候使用单引号将传入的参数引住'${}'

SQL注入是一种常见的安全漏洞,它利用了未正确过滤或转义用户输入的数据,导致恶意用户可以在执行SQL查询时插入恶意的SQL代码。
查询数据库可以看到用户名和密码都是admin.
在这里插入图片描述
正常情况下,用户只能通过密码来输入.

    //用户登录的场景UserEntity login(@Param("username")String username,@Param("password")String password);
    <select id="login" resultType="com.example.demo.entity.UserEntity">select * from userinfo where username='${username}' and password='${password}'</select>
//单元测试代码
@Testvoid login() {UserEntity user = userMapper.login("admin","admin");System.out.println(user);}

在这里插入图片描述
但是非法情况下,我们给password的属性填写一个语句就可以登录成功:

@Testvoid login() {UserEntity user = userMapper.login("admin","'or 1='1");System.out.println(user);}

在这里插入图片描述
由于and的优先级高,先执行前面的结果为false.但是后面的or表示的是两个表达式中只要有一个表达式为真,那么最后的结果就为真,那么1=‘1’,这个表达式就为真.
这就是最简单的SQL注入.

预编译(#{}):
优点:

  • 它的执行是安全的,可以防止sql注入。预编译他会将传入的值当成value来看待,判断这个value是否和数据库中这个字段中的值是否相等,相等就会执行成功,不相等会查找不到.
  • 在使用#{}这种写法的时候,如果我们传递的参数是字符串类型的,我们不需要使用单引号(’ ')将#{}括起来,执行的时候,他会自动给value添加单引号。

缺点:

  • 不能传递SQL命令,当传递SQL命令的时候他会给这个命令自动添加单引号(’ '),但是给SQL命令添加单引号SQL语句就会报错。
//UserMapper类下://传递排序规则List<UserEntity> getAllByOrder(@Param("myorder")String myorder);
//UserMapper.xml<select id="getAllByOrder" resultType="com.example.demo.entity.UserEntity">select * from userinfo order by id #{myorder}</select>
//单元测试@Testvoid getAllByOrder() {List<UserEntity> list = userMapper.getAllByOrder("desc");System.out.println(list);}

在这里插入图片描述
当时当我们将#{}换成${},再次运行就会执行成功。

select * from userinfo order by id ${myorder}

在这里插入图片描述
总结:
${}#{}的区别在于替换方式和安全性。${}是简单的字符串替换,直接将参数值拼接到SQL语句中,没有安全处理,存在安全风险和SQL注入风险;而#{}是预编译处理,将参数放入PreparedStatement中,使用占位符进行替换,提供了类型转换、安全处理和预编译功能,更加安全可靠。因此,为了防止SQL注入攻击和保证系统的安全性,推荐使用#{}作为参数占位符。

2. 单表的增删改等操作

在上述博客中,我们简单介绍了<select>标签.详情见:MyBatis项目创建与使用
接下来,我们来实现用户的增删改操作,对应使用MyBatis的标签如下:
<insert>标签:插入语句.
<update>标签:修改语句.
<delete>标签:删除语句.

2.1 增加操作

添加操作在接口中声明方法的时候,定义的返回值类型是int,因为默认的返回值是受影响的行数,在XML文件实现add方法时,也不需要规定返回值类型。

//UserMapper类下://添加用户int add(UserEntity user);
//UserMapper.xml:<insert id="add">insert into userinfo(username,password,photo)values(#{username},#{password},#{photo})</insert>	  
//单元测试:@Testvoid add() {UserEntity user = new UserEntity();user.setUsername("张三");user.setPassword("123");user.setPhoto("/image/default.png");int result = userMapper.add(user);System.out.println("受影响的行数: "+result);}   

在这里插入图片描述
在这里插入图片描述
特殊的添加:返回自增id
之前的方法默认情况下返回的是受影响的行数,如果想要返回自增id,具体实现如下。

//UserMapper类下://添加操作:返回自增idint insert(UserEntity user);
//UserMapper.xml:<insert id="insert" useGeneratedKeys="true" keyColumn="id" keyProperty="id">insert into userinfo(username,password,photo)values(#{username},#{password},#{photo})</insert>	  
//单元测试:@Testvoid insert() {UserEntity user = new UserEntity();user.setUsername("李四");user.setPassword("123");user.setPhoto("");//因为在接口中声明的 insert 方法的参数为 user,所以测试的时候可以直接将 user对象传给这个insert方法int result = userMapper.insert(user);System.out.println("受影响的行数: "+result+" | id: "+user.getId());}

在XML文件中的insert标签中添加useGenerateKeys,keyColumnkeyProperty属性。

useGenerateKeys: 表示获取数据库中开启自增主键的值。在insert标签中表示的意思为获取本次添加的成员的自增主键的值。默认值为false.
keyColumn: 表示设置自增主键在数据表中的字段名。
keyProperty: 表示将获取到的自增主键的值赋值给keyProperty所指的属性(实体类).
在这里插入图片描述
在这里插入图片描述

2.2 修改操作

修改的实现和删除一样在xml文件中的update标签中不用设置返回值类型(resultMap或者resultType),默认的返回值是受影响的行数,所以在UserMapper接口中声明方法的时候,返回值类型为int。

//UserMapper类下://修改操作int update(UserEntity user);
//UserMapper.xml:<update id="update">update userinfo set username=#{username} where id=#{id}</update>	  
//单元测试:@Testvoid update() {UserEntity user = new UserEntity();user.setId(1);user.setUsername("管理员");int result = userMapper.update(user);System.out.println("受影响的行数"+result);}

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

2.3 删除操作

删除信息,默认返回的是受影响的行数,所以我们在声明方法的时候设置的返回值类型为int.

//UserMapper类下://删除操作int delById(@Param("id")Integer id);    
//UserMapper.xml:<delete id="delById">delete from userinfo where id=#{id}</delete>	  
//单元测试:@Testvoid delById() {int id = 2;int result = userMapper.delById(id);System.out.println("受影响的行数:"+result);}

在这里插入图片描述
在这里插入图片描述
MyBatis中开启事务的注解:@Transactional
在这里插入图片描述
在这里插入图片描述

2.4 like(模糊查询)操作

like查询,我们按照学习MySQL是使用的语法在XML文件中构造sql语句,在执行的时候会出现报错的问题。

//UserMapper类下://like查询List<UserEntity> getLikeList(@Param("username")String username);  
//UserMapper.xml:<select id="getLikeList" resultType="com.example.demo.entity.UserEntity">select * from userinfo where username like %#{username}%</select>	  
//单元测试:@Testvoid getLikeList() {String username = "三";List<UserEntity> list = userMapper.getLikeList(username);System.out.println(list);}

在这里插入图片描述
我们可以看到,使用预编译的方式不行,那我们可以尝试使用即时执行的方式,这种方式执行确实结果是对的,但是这里使用即时执行的方式并不满足使用它的条件,会出现sql注入的情况。

解决方案:

  1. 第一种解决方法是在XML中继续直接使用#{username},我们在业务代码中给username赋值为%三%

    //UserMapper类下://like查询List<UserEntity> getLikeList(@Param("username")String username);  
    //UserMapper.xml:<select id="getLikeList" resultType="com.example.demo.entity.UserEntity">select * from userinfo where username like #{username}</select>	  
    //单元测试:@Testvoid getLikeList() {String username = "%三%";List<UserEntity> list = userMapper.getLikeList(username);System.out.println(list);}
    

    在这里插入图片描述
    虽然这种方式可以解决问题,但是在业务代码中写,看起来就是不太好看。

  2. 第二种解决方法是使用SQL语法中的concat字段,对多个字符进行拼接。

    //UserMapper类下://like查询List<UserEntity> getLikeList(@Param("username")String username);  
    //UserMapper.xml:<select id="getLikeList" resultType="com.example.demo.entity.UserEntity">select * from userinfo where username like concat('%',#{username},'%')</select>	  
    //单元测试:@Testvoid getLikeList() {String username = "三";List<UserEntity> list = userMapper.getLikeList(username);System.out.println(list);}
    

    在这里插入图片描述
    这种写法既不会出现单引号套单引号的问题,在业务代码中进行like查询传参的时候,也没有出现使用多余的符号的问题。

2.5 实体类中的属性和数据库表中的字段名不一致出现的问题的三种解决方式

MyBatis是通过实体类的属性名称和数据库中的字段名进行映射的,如果实体类中的属性名和数据库表中的字段名不同,在进行查询的时候,出现的结果中字段的值会为null.
在这里插入图片描述
解决方法:

  1. 将实体类中的属性名修改成和数据库表中的数据修改成一致的。这种方式只适合于当前这个实体类,只有你一个人使用了,如果其他人的代码中也使用了你创建的实体类,那么就不能使用这种方式来修改了。
  2. 使用SQL语句中的as对数据表中的字段名进行重命名,让字段名等于创建的实体类的属性名。
        <select id="getAllByOrder" resultType="com.example.demo.entity.UserEntity">select id,username as name from userinfo order by id ${myorder}</select>
    
  3. 定义一个resultMap,将属性名和字段名进行手动映射。
    在这里插入图片描述
    在这里插入图片描述

3. 多表查询

3.1 多表查询(一对一)

这里我们查询一篇文章对应的作者的名字,站在文章的角度进行多表联合查询就是一对一的情况。
使用注解的方式在MyBaits程序中构造SQL语句,我们想要使用SQL的查询,就可以在接口中的方法上加上注解@Select,想要使用删除,可以在接口的方法上添加@Delete,想要使用插入可以在方法上添加@Insert,想要实现修改可以在方法上添加@Update,然后将要执行的sql语句写在这些注解的参数中即可。

  1. 创建文章实体类
package com.example.demo.entity;import lombok.Data;import java.time.LocalDateTime;@Data
public class ArticleInfo {private int id;private String title;private  String content;private LocalDateTime createtime;private LocalDateTime updatetime;private  int uid;private int rcount;private int state;//链表字段private  String username;
}
  1. 定义接口
package com.example.demo.mapper;import com.example.demo.entity.ArticleInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;import java.util.List;@Mapper
public interface ArticleMapper {@Select("select articleinfo.*,userinfo.username from articleinfo left join userinfo on articleinfo.uid=userinfo.id")List<ArticleInfo> getAll();
}
  1. 创建单元测试
package com.example.demo.mapper;import com.example.demo.entity.ArticleInfo;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import java.util.List;import static org.junit.jupiter.api.Assertions.*;@SpringBootTest
class ArticleMapperTest {@Autowiredprivate ArticleMapper articleMapper;@Testvoid getAll() {List<ArticleInfo> list = articleMapper.getAll();System.out.println(list);}
}

运行结果:
在这里插入图片描述

3.2 多表查询(一对多)

一对多的多表查询,这里我们将查询步骤分为三步:

  1. 根据id找到用户信息
  2. 根据uid查询文章列表
  3. 然后将得到的文章信息和用户信息进行组装即可
  1. 首先我们需要在userinfo类(用户实体类)中添加一个alist属性,最后用来将得到文章信息组装到userinfo对象中。
package com.example.demo.model;import lombok.Data;import java.time.LocalDateTime;
import java.util.List;@Data
public class Userinfo {private int id;private String username;private String password;private String photo;private LocalDateTime createtime;private LocalDateTime updatetime;private int state;private List<ArticleInfo> alist;
}
  1. 然后在数据持久层的UserinfoMapper类和ArticleMapper类中添加查询的方法
package com.example.demo.entity;import lombok.Data;import java.time.LocalDateTime;
import java.util.List;@Data
public class Userinfo {private int id;private String username;private String password;private String photo;private LocalDateTime createtime;private LocalDateTime updatetime;private int state;private List<ArticleInfo> alist;
}
  1. UserMapperTest1单元测试类中创建一个getUserList方法,在这个方法中调用上述两个方法,最后调用setAlist方法,将getListByUid方法中得到的文章列表添加到userinfo对象中,就完成了多表查询的一对多的情况
package com.example.demo.mapper;import com.example.demo.entity.ArticleInfo;
import com.example.demo.entity.Userinfo;
import com.example.demo.entity.ArticleInfo;
import com.example.demo.entity.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;import static org.junit.jupiter.api.Assertions.*;@SpringBootTest//不能省略,告诉当前的测试程序,目前项目是运行在Spring容器中的
class UserMapperTest1 {@Autowiredprivate UserinfoMapper userinfoMapper;@Autowiredprivate ArticleMapper articleMapper;@Testvoid getUserList(){int uid = 1;//1.根据uid查询userinfoUserinfo userinfo = userinfoMapper.getUserById2(uid);//2.根据uid查询文章列表List<ArticleInfo> list = articleMapper.getListByUid(uid);//3.组装数据userinfo.setAlist(list);System.out.println(userinfo);}}

在这里插入图片描述

二. 动态SQL的使用

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

1. < if >标签

我们在上网时,经常需要填写一些表单,其中有些选项是必填的,有些是选填的,那么这个时候在MyBatis程序中按照XML的方式构造sql语句时,是不能完全胜任的。比如填通讯信息的时候,出现了一个选填项是填写QQ号,如果不填这个选项,前端传给后端代码中的这个数据的值为null,现在规定让这一项在数据库中默认为空,如果如不使用标签,那么在XML中是无法完成这个规定。在数据库中null和空是两个概念。

语法:

<!--  test中的表达式是满足使用多个条件  -->
<if test="表达式"><!-- 满足表达式的条件,就会进入执行其中的内容  --> ....
</if>
  1. 在接口中声明方法
    //动态sql添加操作<if>int add2(Userinfo userinfo);
  1. 在XML文件中实现动态sql,这里再sql语句中添加标签用来判断是否设置了photo的值,如果没有设置,那就不添加这个字段在sql语句中,如果添加这个字段的值,就会在sql语句中添加这个字段。
    <insert id="add2">insert into userinfo(username,password<if test="photo != null">,photo</if>)values(#{username},#{password}<if test="photo != null">,#{photo}</if>)</insert>
  1. 测试单元
//给对象的属性设置值得时候,给photo属性添加值@Testvoid add2() {Userinfo userinfo = new Userinfo();userinfo.setUsername("张三");userinfo.setPassword("123");userinfo.setPhoto("cat.png");int result = userMapper.add2(userinfo);System.out.println("执行的结果: "+result);}
  1. 运行结果:
    在这里插入图片描述
    可以看到如果给photo没有设置值,那么在数据看中photo这一列是空的,不会出现null.这就解决了表单中可选项的填写问题了。如果填了表单中的可选项就会将值保存在数据库中,如果没有填写可选项,那么数据库中这个字段就不会有值。
    在这里插入图片描述

2. < trim >标签

上面我们说的表单中存在某个选填项,假设表单上所有的选项都是选填的,那么使用<if>标签就不能满足我们的需求了。因为在判断给字段是否传值时,使用<if>标签将字段包裹起来了,但是字段和字段之间要使用逗号隔开,所以我们还需要将逗号拼接上。但是我们不知道用户选填了那些字段,所以将逗号拼接上之后,还需要考虑逗号不能出现在开始的字段前面,结束的字段后面不能出现逗号。这个就需要使用<trim>标签中的属性来解决了。

<trim>标签的属性:
prefix:表示整个语句块,以prefix的值作为前缀
suffix:表示整个语句块,以suffix的值作为后缀
prefixOverrides:表示整个语句块要去除掉的前缀
suffixOverrides:表示整个语句块要去除掉的后缀

  1. 在接口中声明方法
    /** 动态sql <trim>标签* */int add3(Userinfo userinfo);
  1. 在XML文件中实现方法,由于<trim>标签中的prefixsuffix属性可以添加整个语句块的前缀和后缀,所以这里我们直接使用这两个属性拼接括号,我们在<if>标签中将逗号拼接在字段的后面,使用suffixOverrides属性指定要去除语句块中某个后缀(逗号),整个时候就会将语句块中最后一个字段之后的逗号去掉。
    <insert id="add3">insert into userinfo<trim prefix="(" suffix=")" suffixOverrides=","><if test="username != null">username,</if><if test="password != null">password,</if><if test="photo!=null">photo,</if></trim>values<trim prefix="(" suffix=")" suffixOverrides=","><if test="username != null">#{username},</if><if test="password != null">#{password},</if><if test="photo != null">#{photo},</if></trim></insert>
  1. 单元测试,这里我们只添加名字和密码,不添加照片的属性。
    @Testvoid add3() {Userinfo userinfo = new Userinfo();userinfo.setUsername("李四");userinfo.setPassword("666");int result = userMapper.add2(userinfo);System.out.println("执行的结果: "+result);}
  1. 执行结果
    可以看到执行结果中字段中没有将photo拼接上,并且语句块中结尾的字段之后也没有逗号。数据保存在数据库中,也是按照我们的预想执行的,没有添加照片字段的值,photo列为空,不是null.
    在这里插入图片描述
    在这里插入图片描述

3. < where >标签

MyBatis 中多个都是非必传参数的解决方案:

  1. 1=1解决方案
    在这里插入图片描述
  2. <trim>标签解决:
    在这里插入图片描述
  3. <where>标签方案:
    在这里插入图片描述
    注意: <where>只会自动帮你去除最前面的 and 关键字,使用 where 标签不会自动帮你去除最后面的and关键字.
    MyBatis中,<where>标签并不只能用于生成 AND 条件,它可以用于生成任何类型的条件语句(包括AND和OR)。
    例如:
<select id="getUserList" parameterType="Map" resultType="User">SELECT *FROM usersWHERE age = #{age}<where><if test="name != null">OR name = #{name}</if></where>
</select>

上述示例中,无论age的值是什么,都会根据name的值生成OR条件。

4. < set >标签

<set>标签和<where>标签在sql语句中添加方式相同,只不过where标签用在查询,set标签用在修改。但是<set>标签是去掉代码块的后缀的,而<where>标签是去掉代码块的前缀的。使用<set>标签可以避免在更新操作中出现多余的逗号和无效的更新字段。

<update id="updateUser" parameterType="User">UPDATE users<set><if test="name != null">name = #{name},</if><if test="age != null">age = #{age},</if><if test="email != null">email = #{email},</if></set>WHERE id = #{id}
</update>

在上述示例中,<set>标签用于动态生成set子句。根据传入的参数值判断是否生成相应的更新字段,如果参数值为null,则不会生成相应的更新语句。
注意,在生成set子句时,每个更新字段末尾都会有一个逗号,即使是最后一个字段。这是因为在动态SQL中,可以通过条件判断来控制是否生成该字段,但为了简化逻辑和代码,可以在每个字段之后都加上逗号,不影响SQL的语法正确性。
另外,需要注意使用占位符(如#{name})来引用参数值,而不是直接拼接参数值。这样可以避免SQL注入攻击和确保参数值的正确性。
通过使用<set>标签,可以根据条件动态生成UPDATE语句中的字段和对应的值,提高灵活性并避免不必要的逗号和无效的更新字段。

5. < foreach >标签

在MyBatis中,<foreach>标签用于循环遍历集合或数组,并将其中的元素逐个应用到SQL语句中的特定位置,以便生成动态SQL。
<foreach>标签通常与动态SQL一起使用,可以在in子句中动态生成多个值或者在批量插入/更新操作中循环处理多个数据。
下面是一个使用<foreach>标签的示例:

<select id="getUserByIdList" parameterType="List" resultType="User">SELECT *FROM usersWHERE id IN<foreach item="id" collection="list" open="(" separator="," close=")">#{id}</foreach>
</select>

在上述示例中,<foreach>标签将会循环遍历传入的List类型参数list,并将每个元素存储到id变量中。循环体中的#{id}表示动态插入当前迭代的值。
生成的SQL语句可能类似于以下形式(假设list包含 [1, 2, 3]):

SELECT *
FROM users
WHERE id IN (1, 2, 3)

<foreach>标签的常用属性:
collection:指定要遍历的集合或数组。
item:指定当前元素的别名。
index:指定当前元素的索引。
open:指定循环开始时的字符。
close:指定循环结束时的字符。
separator:指定每个元素之间的分隔符。

需要注意的是,<foreach>标签也可以用于批量插入或更新操作中,通过循环处理多个数据。此时,可以将循环体中的SQL片段放置在合适的位置来重复执行插入或更新。

通过使用<foreach>标签,可以实现对集合或数组的循环遍历,动态生成包含多个值的SQL语句,并在动态SQL中灵活地处理多个数据。

单元测试

  1. 在要测试的类上右键Generate
    在这里插入图片描述
  2. 点击Test
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LeetCode538. 把二叉搜索树转换为累加树

538. 把二叉搜索树转换为累加树 文章目录 [538. 把二叉搜索树转换为累加树](https://leetcode.cn/problems/convert-bst-to-greater-tree/)一、题目二、题解方法一&#xff1a;递归&#xff08;中序遍历与节点更新&#xff09;方法二&#xff1a;反向中序遍历与累加更新&#x…

JavaFX 加载 fxml 文件

JavaFX 加载 fxml 文件主要有两种方式&#xff0c;第一种方式通过 FXMLLoader 类直接加载 fxml 文件&#xff0c;简单直接&#xff0c;但是有些控件目前还不知道该如何获取&#xff0c;所以只能显示&#xff0c;目前无法处理。第二种方式较为复杂&#xff0c;但是可以使用与 fx…

初阶数据结构(六)队列的介绍与实现

&#x1f493;博主csdn个人主页&#xff1a;小小unicorn&#x1f493; ⏩专栏分类&#xff1a;C &#x1f69a;代码仓库&#xff1a;小小unicorn的学习足迹&#x1f69a; &#x1f339;&#x1f339;&#x1f339;关注我带你学习编程知识 栈 队列的介绍队列的概念&#xff1a;队…

GWO-LSTM交通流量预测(python代码)

使用 GWO 优化 LSTM 模型的参数&#xff0c;从而实现交通流量的预测方法 代码运行版本要求 1.项目文件夹 data是数据文件夹&#xff0c;data.py是数据归一化等数据预处理脚本 images文件夹装的是不同模型结构打印图 model文件夹 GWO-LSTM测试集效果 效果视频&#xff1a;GWO…

SNN论文总结

Is SNN a great work ? Is SNN a convolutional work ? ANN的量化在SNN中是怎么体现的&#xff0c;和threshold有关系吗&#xff0c;threshold可训练和这个有关吗&#xff08;应该无关&#xff09; 解决过发放不发放的问题。 Intuation SNN编码方式 Image to spike patter…

stm32之19.温湿度模块(待补充)

dth11.c文件① #include "dht11.h" #include "delay.h"// 1、温湿度模块初始化(PG9) void Dht11_Init(void) {// 0、GPIO外设信息结构体GPIO_InitTypeDef GPIO_InitStruct;// 1、使能硬件时钟 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);//…

Pyqt5开发实战记录

入职以来第一个开发项目&#xff1a; 1、如何给Qlabel加边框&#xff1a;右键label对象&#xff0c;选择“改变样式表”输入一下代码&#xff1a; border: 1px solid black;2、如何让垂直布局中button大小不发生变化&#xff1a;其实很简单&#xff0c;只需要设置button的最大…

【seaweedfs】2、Finding a needle in Haystack: Facebook’s photo storage 分布式对象存储论文

文章目录 一、介绍二、背景、设计理念2.1 背景2.2 NFS-based Design2.3 Discussion 三、设计和实现3.1 概览3.2 Haystack Directory3.3 Haystack Cache3.4 Haystack Store3.4.1 Photo Read3.4.2 Photo Write3.4.3 Photo Delete3.4.4 The Index File3.4.5 Filesystem 3.5 Recove…

WebGL 缓冲区对象介绍,创建并使用缓冲区,使用缓冲区对象向顶点着色器传入多个顶点数据的所有步骤

目录 使用缓冲区对象 使用缓冲区对象向顶点着色器传入多个顶点的数据&#xff0c;需要遵循以下五个步骤。 创建缓冲区对象&#xff08;gl.createBuffer&#xff08;&#xff09;&#xff09; gl.createBuffer&#xff08;&#xff09;的函数规范 gl.deleteBuffer &#…

C# winform加载yolov8模型测试(附例程)

第一步&#xff1a;在NuGet中下载Yolov8.Net 第二步&#xff1a;引用 using Yolov8Net; 第三步&#xff1a;加载模型 private IPredictor yolov8 YoloV8Predictor.Create("D:\\0MyWork\\Learn\\vs2022\\yolov_onnx\\best.onnx", mylabel); 第四步&#xff1a;图…

【OpenCV • c++】图像对比度调整 | 图像亮度调整

&#x1f680; 个人简介&#xff1a;CSDN「博客新星」TOP 10 &#xff0c; C/C 领域新星创作者&#x1f49f; 作 者&#xff1a;锡兰_CC ❣️&#x1f4dd; 专 栏&#xff1a;【OpenCV • c】计算机视觉&#x1f308; 若有帮助&#xff0c;还请关注➕点赞➕收藏&#xff…

window系统中如何判断是物理机还是虚拟机及VMPROTECT无法检测云主机

为什么要判断物理机&#xff0c;因为授权不能对虚拟机安装后的软件进行授权。虚拟机可以复制可以克隆&#xff0c;无法作为一个不可复制ID来使用。 总结了如何判断物理机&#xff1a; 1. 用systeminfo的系统型号。&#xff08;注&#xff0c;有资料是看处理器和bios。但是我这…

一步一步实验,讲解python中模块和包的使用

背景 为什么要提出这个问题&#xff1f; 在一个项目中&#xff0c;每一个python文件打开后&#xff0c;都会看到依赖了其他的一些包、模块等&#xff1b;概念混乱&#xff0c;魔改目标不清晰 为什么要修改&#xff1f; 如果需要将某开源包进行自定义处理&#xff0c;不再使…

Python 包管理(pip、conda)基本使用指南

Python 包管理 概述 介绍 Python 有丰富的开源的第三方库和包&#xff0c;可以帮助完成各种任务&#xff0c;扩展 Python 的功能&#xff0c;例如 NumPy 用于科学计算&#xff0c;Pandas 用于数据处理&#xff0c;Matplotlib 用于绘图等。在开始编写 Pytlhon 程序之前&#…

数据隐私与安全在大数据时代的挑战与应对

文章目录 数据隐私的挑战数据安全的挑战应对策略和方法1. 合规和监管2. 加密技术3. 匿名化和脱敏4. 安全意识培训5. 隐私保护技术 结论 &#x1f388;个人主页&#xff1a;程序员 小侯 &#x1f390;CSDN新晋作者 &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 ✨收录专栏&…

【算法与数据结构】513、LeetCode找树左下角的值

文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析&#xff1a;这道题用层序遍历来做比较简单&#xff0c;最底层最左边节点就是层序遍历当中最底层元素容器的第一个值…

vue 简单实验 自定义组件 独立模块

1.概要 2.代码 2.1 const Counter {data() {return {counter: 0}},template:<div>Counter: {{ counter }}</div> }export default Counter 2.2 import Counter from ./t2.jsconst app Vue.createApp({components: {component-a: Counter} })app.mount(#count…

浅析 GlusterFS 与 JuiceFS 的架构异同

在进行分布式文件存储解决方案的选型时&#xff0c;GlusterFS 无疑是一个不可忽视的考虑对象。作为一款开源的软件定义分布式存储解决方案&#xff0c;GlusterFS 能够在单个集群中支持高达 PiB 级别的数据存储。自从首次发布以来&#xff0c;已经有超过十年的发展历程。目前&am…

不使用ip和port如何进行网络通讯(raw socket应用例子)

主要应用方向是上位机和嵌软(如stm32单片机)通讯&#xff0c;不在单片机中嵌入web server&#xff0c;即mac层通讯。 一、下面先了解网络数据包组成。 常见数据包的包头长度: EtherHeader Length: 14 BytesTCP Header Length : 20 BytesUDP Header Length : 8 BytesIP Heade…

Spring@Scheduled定时任务接入XXL-JOB的一种方案(基于SC Gateway)

背景 目前在职的公司&#xff0c;维护着Spring Cloud分布式微服务项目有25个。其中有10个左右微服务都写有定时任务逻辑&#xff0c;采用Spring Scheduled这种方式。 Spring Scheduled定时任务的缺点&#xff1a; 不支持集群&#xff1a;为避免重复执行&#xff0c;需引入分…